managerimpl.cpp 64 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"

49
#include "useragent.h"
jpbl's avatar
jpbl committed
50
51
52
53
54
55
56
57
58
59
60
61
62

#include "user_cfg.h"

#ifdef USE_ZEROCONF
#include "zeroconf/DNSService.h"
#include "zeroconf/DNSServiceTXTRecord.h"
#endif

#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
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
ManagerImpl::ManagerImpl (void) 
	: _hasTriedToRegister(false)
        , _config()
	, _currentCallId2()
        , _currentCallMutex()
        , _codecBuilder(NULL)
        , _audiodriver(NULL)
        , _dtmfKey(NULL)
        , _codecDescriptorMap()
        , _toneMutex()
        , _telephoneTone(NULL)
        , _audiofile()
        , _spkr_volume(0)
        , _mic_volume(0)
        , _mutex()
	, _dbus(NULL)
        , _waitingCall()
        , _waitingCallMutex()
        , _nbIncomingWaitingCall(0)
        , _path("")
        , _exist(0)
        , _setupLoaded(false)
        , _firewallPort()
        , _firewallAddr("")
        , _hasZeroconf(false)
        , _callAccountMap()
        , _callAccountMapMutex()
        , _accountMap()
        , _userAgent(NULL)
        , _userAgentInitlized(false)
        , _sipThreadStop()
 
jpbl's avatar
jpbl committed
95
{
Yun Liu's avatar
Yun Liu committed
96
97
98
99
100
101
102
103
  /* Init private variables 
     setup:    _path, _exist, _setupLoaded , _dbus
     sound:    _audiodriver, _dtmfKey, 
               _spkr_volume,_mic_volume  = 0;  // Initialize after by init() -> initVolume()
     Call:     _nbIncomingWaitingCall, _hasTriedToRegister
     SIP Link: _userAgent, _userAgentInitlized
  */

jpbl's avatar
jpbl committed
104
105
106
107
108
109
110
#ifdef USE_ZEROCONF
  _hasZeroconf = true;
  _DNSService = new DNSService();
#endif

  // initialize random generator for call id
  srand (time(NULL));
yanmorin's avatar
   
yanmorin committed
111
112
113
114
115
116
117
118
119

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

  // should be call before initConfigFile
120
  // loadAccountMap();, called in init() now.
jpbl's avatar
jpbl committed
121
122
123
124
125
126
127
128
}

// never call if we use only the singleton...
ManagerImpl::~ManagerImpl (void) 
{
  terminate();

#ifdef USE_ZEROCONF
129
  delete _DNSService; _DNSService = 0;
jpbl's avatar
jpbl committed
130
131
132
133
134
#endif

  _debug("%s stop correctly.\n", PROGNAME);
}

135
  void 
jpbl's avatar
jpbl committed
136
137
ManagerImpl::init() 
{
138
139
  // Load accounts, init map
  loadAccountMap();
140
141
 
  //Initialize sip manager 
142
143
144
  if(_userAgentInitlized) {
    _userAgent->sipCreate();
    _userAgent->sipInit();
145
  }
146

jpbl's avatar
jpbl committed
147
148
149
150
151
152
  initVolume();

  if (_exist == 0) {
    _debug("Cannot create config file in your home directory\n");
  }

153
154
155
  initAudioDriver();
  selectAudioDriver();

156
  // Initialize the list of supported audio codecs
jpbl's avatar
jpbl committed
157
158
  initAudioCodec();

159
160
  getAudioInputDeviceList();

jpbl's avatar
jpbl committed
161
162
163
164
165
166
  AudioLayer *audiolayer = getAudioDriver();
  if (audiolayer!=0) {
    unsigned int sampleRate = audiolayer->getSampleRate();

    _debugInit("Load Telephone Tone");
    std::string country = getConfigString(PREFERENCES, ZONE_TONE);
167
    _telephoneTone = new TelephoneTone(country, sampleRate);
jpbl's avatar
jpbl committed
168
169

    _debugInit("Loading DTMF key");
170
    _dtmfKey = new DTMF(sampleRate);
jpbl's avatar
jpbl committed
171
172
  }

173
  // initRegisterAccounts was here, but we doing it after the gui loaded... 
jpbl's avatar
jpbl committed
174
175
  // the stun detection is long, so it's a better idea to do it after getEvents
  initZeroconf();
176
  
jpbl's avatar
jpbl committed
177
178
179
180
181
182
}

void ManagerImpl::terminate()
{
  saveConfig();

yanmorin's avatar
   
yanmorin committed
183
  unloadAccountMap();
184
  
185
186
187
188
  if(_userAgentInitlized) {
      delete _userAgent;
      _userAgent = NULL;
      _userAgentInitlized = false;
189
  }
jpbl's avatar
jpbl committed
190
191
192
193
194

  _debug("Unload DTMF Key\n");
  delete _dtmfKey;

  _debug("Unload Audio Driver\n");
195
  delete _audiodriver; _audiodriver = NULL;
jpbl's avatar
jpbl committed
196
197

  _debug("Unload Telephone Tone\n");
198
  delete _telephoneTone; _telephoneTone = NULL;
199
200
201

  _debug("Unload Audio Codecs\n");
  _codecDescriptorMap.deleteHandlePointer();
jpbl's avatar
jpbl committed
202
203
}

yanmorin's avatar
   
yanmorin committed
204
205
206
207
bool
ManagerImpl::isCurrentCall(const CallID& callId) {
  ost::MutexLock m(_currentCallMutex);
  return (_currentCallId2 == callId ? true : false);
jpbl's avatar
jpbl committed
208
209
}

yanmorin's avatar
   
yanmorin committed
210
211
212
bool
ManagerImpl::hasCurrentCall() {
  ost::MutexLock m(_currentCallMutex);
Emmanuel Milou's avatar
Bug fix    
Emmanuel Milou committed
213
  _debug("Current call ID = %s\n", _currentCallId2.c_str());
yanmorin's avatar
   
yanmorin committed
214
215
  if ( _currentCallId2 != "") {
    return true;
jpbl's avatar
jpbl committed
216
  }
yanmorin's avatar
   
yanmorin committed
217
  return false;
jpbl's avatar
jpbl committed
218
219
}

yanmorin's avatar
   
yanmorin committed
220
221
222
223
const CallID& 
ManagerImpl::getCurrentCallId() {
  ost::MutexLock m(_currentCallMutex);
  return _currentCallId2;
jpbl's avatar
jpbl committed
224
225
226
}

void
yanmorin's avatar
   
yanmorin committed
227
228
229
ManagerImpl::switchCall(const CallID& id ) {
  ost::MutexLock m(_currentCallMutex);
  _currentCallId2 = id;
jpbl's avatar
jpbl committed
230
231
232
233
234
235
}


///////////////////////////////////////////////////////////////////////////////
// Management of events' IP-phone user
///////////////////////////////////////////////////////////////////////////////
yanmorin's avatar
   
yanmorin committed
236
/* Main Thread */ 
237
  bool
yanmorin's avatar
   
yanmorin committed
238
239
240
ManagerImpl::outgoingCall(const std::string& accountid, const CallID& id, const std::string& to)
{
  if (!accountExists(accountid)) {
241
    _debug("! Manager Error: Outgoing Call: account doesn't exist\n");
yanmorin's avatar
   
yanmorin committed
242
243
244
    return false;
  }
  if (getAccountFromCall(id) != AccountNULL) {
245
    _debug("! Manager Error: Outgoing Call: call id already exists\n");
yanmorin's avatar
   
yanmorin committed
246
247
    return false;
  }
yanmorin's avatar
   
yanmorin committed
248
  if (hasCurrentCall()) {
249
    _debug("* Manager Info: there is currently a call, try to hold it\n");
yanmorin's avatar
   
yanmorin committed
250
251
    onHoldCall(getCurrentCallId());
  }
252
  _debug("- Manager Action: Adding Outgoing Call %s on account %s\n", id.data(), accountid.data());
yanmorin's avatar
   
yanmorin committed
253
254
255
256
  if ( getAccountLink(accountid)->newOutgoingCall(id, to) ) {
    associateCallToAccount( id, accountid );
    switchCall(id);
    return true;
jpbl's avatar
jpbl committed
257
  } else {
258
    _debug("! Manager Error: An error occur, the call was not created\n");
jpbl's avatar
jpbl committed
259
  }
yanmorin's avatar
   
yanmorin committed
260
  return false;
jpbl's avatar
jpbl committed
261
262
}

yanmorin's avatar
   
yanmorin committed
263
//THREAD=Main : for outgoing Call
264
  bool
yanmorin's avatar
   
yanmorin committed
265
ManagerImpl::answerCall(const CallID& id)
jpbl's avatar
jpbl committed
266
{
267
  stopTone(false); 
268
  _debug("Try to answer call: %s\n", id.data());
yanmorin's avatar
   
yanmorin committed
269
270
271
272
  AccountID accountid = getAccountFromCall( id );
  if (accountid == AccountNULL) {
    _debug("Answering Call: Call doesn't exists\n");
    return false;
jpbl's avatar
jpbl committed
273
  }
yanmorin's avatar
   
yanmorin committed
274

275
  if (id != getCurrentCallId()) {
Yun Liu's avatar
Yun Liu committed
276
277
278
279
    _debug("* Manager Info: there is currently a call, try to hold it\n");
    onHoldCall(getCurrentCallId());
  }

yanmorin's avatar
   
yanmorin committed
280
281
282
283
  if (!getAccountLink(accountid)->answer(id)) {
    // error when receiving...
    removeCallAccount(id);
    return false;
jpbl's avatar
jpbl committed
284
  }
285
  
yanmorin's avatar
   
yanmorin committed
286
  // if it was waiting, it's waiting no more
287
  if (_dbus) _dbus->getCallManager()->callStateChanged(id, "CURRENT");
yanmorin's avatar
   
yanmorin committed
288
289
290
  removeWaitingCall(id);
  switchCall(id);
  return true;
jpbl's avatar
jpbl committed
291
292
}

yanmorin's avatar
   
yanmorin committed
293
//THREAD=Main
294
  bool
yanmorin's avatar
   
yanmorin committed
295
ManagerImpl::hangupCall(const CallID& id)
jpbl's avatar
jpbl committed
296
{
297
  stopTone(true);
298
  if (_dbus) _dbus->getCallManager()->callStateChanged(id, "HUNGUP");
yanmorin's avatar
   
yanmorin committed
299
300
  AccountID accountid = getAccountFromCall( id );
  if (accountid == AccountNULL) {
301
302
    /** @todo We should tell the GUI that the call doesn't exist, so
     * it clears up. This can happen. */
303
    _debug("! Manager Hangup Call: Call doesn't exists\n");
yanmorin's avatar
   
yanmorin committed
304
    return false;
jpbl's avatar
jpbl committed
305
  }
yanmorin's avatar
   
yanmorin committed
306
307

  bool returnValue = getAccountLink(accountid)->hangup(id);
308
  _debug("After voip link hungup!\n");
yanmorin's avatar
   
yanmorin committed
309
310
  removeCallAccount(id);
  switchCall("");
311

312
313
314
  if( getConfigInt( PREFERENCES , CONFIG_PA_VOLUME_CTRL ) )
    _audiodriver->restorePulseAppsVolume();

315
  _debug("Before hungup return!\n");
yanmorin's avatar
   
yanmorin committed
316
  return returnValue;
jpbl's avatar
jpbl committed
317
318
}

yanmorin's avatar
   
yanmorin committed
319
//THREAD=Main
320
  bool
yanmorin's avatar
   
yanmorin committed
321
ManagerImpl::cancelCall (const CallID& id)
jpbl's avatar
jpbl committed
322
{
323
  stopTone(true);
yanmorin's avatar
   
yanmorin committed
324
325
  AccountID accountid = getAccountFromCall( id );
  if (accountid == AccountNULL) {
326
    _debug("! Manager Cancel Call: Call doesn't exists\n");
yanmorin's avatar
   
yanmorin committed
327
    return false;
jpbl's avatar
jpbl committed
328
  }
yanmorin's avatar
   
yanmorin committed
329
330
331
332
333
334

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

yanmorin's avatar
   
yanmorin committed
336
  return returnValue;
jpbl's avatar
jpbl committed
337
338
}

yanmorin's avatar
   
yanmorin committed
339
//THREAD=Main
340
  bool
yanmorin's avatar
   
yanmorin committed
341
ManagerImpl::onHoldCall(const CallID& id)
jpbl's avatar
jpbl committed
342
{
343
  stopTone(true);
yanmorin's avatar
   
yanmorin committed
344
345
  AccountID accountid = getAccountFromCall( id );
  if (accountid == AccountNULL) {
346
    _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
347
    return false;
jpbl's avatar
jpbl committed
348
  }
yanmorin's avatar
   
yanmorin committed
349

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

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

yanmorin's avatar
   
yanmorin committed
354
  removeWaitingCall(id);
355
  if (_dbus) _dbus->getCallManager()->callStateChanged(id, "HOLD");
yanmorin's avatar
   
yanmorin committed
356
  switchCall("");
357

yanmorin's avatar
   
yanmorin committed
358
359
360
361
  return returnValue;
}

//THREAD=Main
362
  bool
yanmorin's avatar
   
yanmorin committed
363
364
ManagerImpl::offHoldCall(const CallID& id)
{
365
  stopTone(false);
yanmorin's avatar
   
yanmorin committed
366
367
  AccountID accountid = getAccountFromCall( id );
  if (accountid == AccountNULL) {
368
    _debug("5 Manager OffHold Call: Call doesn't exists\n");
yanmorin's avatar
   
yanmorin committed
369
    return false;
jpbl's avatar
jpbl committed
370
  }
371

372
373
374
375
376
  //Place current call on hold if it isn't
  if (hasCurrentCall()) 
  { 
    onHoldCall(getCurrentCallId());
  }
377

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

yanmorin's avatar
   
yanmorin committed
380
  bool returnValue = getAccountLink(accountid)->offhold(id);
381
  if (_dbus) _dbus->getCallManager()->callStateChanged(id, "UNHOLD");
yanmorin's avatar
   
yanmorin committed
382
  switchCall(id);
383

jpbl's avatar
jpbl committed
384
385
386
  return returnValue;
}

yanmorin's avatar
   
yanmorin committed
387
//THREAD=Main
388
  bool
yanmorin's avatar
   
yanmorin committed
389
ManagerImpl::transferCall(const CallID& id, const std::string& to)
jpbl's avatar
jpbl committed
390
{
391
  stopTone(true);
yanmorin's avatar
   
yanmorin committed
392
393
  AccountID accountid = getAccountFromCall( id );
  if (accountid == AccountNULL) {
394
    _debug("! Manager Transfer Call: Call doesn't exists\n");
yanmorin's avatar
   
yanmorin committed
395
    return false;
jpbl's avatar
jpbl committed
396
  }
yanmorin's avatar
   
yanmorin committed
397
398
399
  bool returnValue = getAccountLink(accountid)->transfer(id, to);
  removeWaitingCall(id);
  removeCallAccount(id);
400
  if (_dbus) _dbus->getCallManager()->callStateChanged(id, "HUNGUP");
yanmorin's avatar
   
yanmorin committed
401
402
  switchCall("");
  return returnValue;
jpbl's avatar
jpbl committed
403
404
}

yanmorin's avatar
   
yanmorin committed
405
//THREAD=Main : Call:Incoming
406
  bool
yanmorin's avatar
   
yanmorin committed
407
ManagerImpl::refuseCall (const CallID& id)
jpbl's avatar
jpbl committed
408
{
409
  stopTone(true);
yanmorin's avatar
   
yanmorin committed
410
411
  AccountID accountid = getAccountFromCall( id );
  if (accountid == AccountNULL) {
412
    _debug("! Manager OffHold Call: Call doesn't exists\n");
yanmorin's avatar
   
yanmorin committed
413
414
415
    return false;
  }
  bool returnValue = getAccountLink(accountid)->refuse(id);
yanmorin's avatar
   
yanmorin committed
416
417
418
419
420
  // if the call was outgoing or established, we didn't refuse it
  // so the method did nothing
  if (returnValue) {
    removeWaitingCall(id);
    removeCallAccount(id);
421
    if (_dbus) _dbus->getCallManager()->callStateChanged(id, "HUNGUP");
yanmorin's avatar
   
yanmorin committed
422
423
    switchCall("");
  }
yanmorin's avatar
   
yanmorin committed
424
  return returnValue;
jpbl's avatar
jpbl committed
425
426
}

yanmorin's avatar
   
yanmorin committed
427
//THREAD=Main
428
  bool
jpbl's avatar
jpbl committed
429
430
431
432
433
434
435
436
437
438
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
439
//THREAD=Main
440
 int 
441
ManagerImpl::initRegisterAccounts() 
jpbl's avatar
jpbl committed
442
{
443
444

    int status; 
445
    bool flag = true;
446
447
448
449
450
451
452
453
454
455
    AccountMap::iterator iter;

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

    while( iter != _accountMap.end() ) {
      if ( iter->second ) {
        iter->second->loadConfig();
        if ( iter->second->isEnabled() ) {
	  status = iter->second->registerVoIPLink();
456
457
	  if (status != SUCCESS)
		flag = false;
458
        }
Emmanuel Milou's avatar
Emmanuel Milou committed
459
      }
460
      iter++;
Emmanuel Milou's avatar
Emmanuel Milou committed
461
    }
462
463
464
465
    // calls the client notification here in case of errors at startup...
    if( _audiodriver -> getErrorMessage() != -1 )
      notifyErrClient( _audiodriver -> getErrorMessage() );
    
466
    ASSERT( flag, true );
467
    return SUCCESS;
jpbl's avatar
jpbl committed
468
469
}

yanmorin's avatar
   
yanmorin committed
470
//THREAD=Main
471
  bool 
yanmorin's avatar
   
yanmorin committed
472
ManagerImpl::sendDtmf(const CallID& id, char code)
jpbl's avatar
jpbl committed
473
{
yanmorin's avatar
   
yanmorin committed
474
475
  AccountID accountid = getAccountFromCall( id );
  if (accountid == AccountNULL) {
Emmanuel Milou's avatar
Emmanuel Milou committed
476
477
    //_debug("Send DTMF: call doesn't exists\n");
    playDtmf(code, false);
yanmorin's avatar
   
yanmorin committed
478
479
480
    return false;
  }

jpbl's avatar
jpbl committed
481
  int sendType = getConfigInt(SIGNALISATION, SEND_DTMF_AS);
yanmorin's avatar
   
yanmorin committed
482
  bool returnValue = false;
jpbl's avatar
jpbl committed
483
  switch (sendType) {
484
    case 0: // SIP INFO
Emmanuel Milou's avatar
Emmanuel Milou committed
485
      playDtmf(code , true);
486
487
488
489
490
491
492
493
494
      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
495
496
497
498
  }
  return returnValue;
}

yanmorin's avatar
   
yanmorin committed
499
//THREAD=Main | VoIPLink
500
  bool
Emmanuel Milou's avatar
Emmanuel Milou committed
501
ManagerImpl::playDtmf(char code, bool isTalking)
jpbl's avatar
jpbl committed
502
{
503
504
505
506
  // HERE are the variable:
  // - boolean variable to play or not (config)
  // - length in milliseconds to play
  // - sample of audiolayer
507
  stopTone(false);
yanmorin's avatar
   
yanmorin committed
508
509
  int hasToPlayTone = getConfigInt(SIGNALISATION, PLAY_DTMF);
  if (!hasToPlayTone) return false;
jpbl's avatar
jpbl committed
510
511
512
513
514
515
516
517

  // length in milliseconds
  int pulselen = getConfigInt(SIGNALISATION, PULSE_LENGTH);
  if (!pulselen) { return false; }

  // numbers of int = length in milliseconds / 1000 (number of seconds)
  //                = number of seconds * SAMPLING_RATE by SECONDS
  AudioLayer* audiolayer = getAudioDriver();
518
  int layer = audiolayer->getLayerType();
jpbl's avatar
jpbl committed
519
520
521

  // fast return, no sound, so no dtmf
  if (audiolayer==0 || _dtmfKey == 0) { return false; }
522
523
524
525
  // number of data sampling in one pulselen depends on samplerate
  // size (n sampling) = time_ms * sampling/s 
  //                     ---------------------
  //                            ms/s
526
527
  int size = (int)(pulselen * ((float)audiolayer->getSampleRate()/1000));

528
529
530
  // this buffer is for mono
  // TODO <-- this should be global and hide if same size
  SFLDataFormat* _buf = new SFLDataFormat[size];
jpbl's avatar
jpbl committed
531
532
533
534
535
  bool returnValue = false;

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

536
  // copy the sound
537
  if ( _dtmfKey->generateDTMF(_buf, size) ) {
jpbl's avatar
jpbl committed
538
539
540

    // Put buffer to urgentRingBuffer 
    // put the size in bytes...
541
    // so size * 1 channel (mono) * sizeof (bytes for the data)
542
543
544
545
    if(CHECK_INTERFACE( layer , ALSA ))
      audiolayer->playSamples(_buf, size * sizeof(SFLDataFormat), isTalking);
    else
      _debug("DTMF disabled\n");
546
      audiolayer->putUrgent( _buf, size * sizeof(SFLDataFormat) );
547

jpbl's avatar
jpbl committed
548
  }
549
  returnValue = true;
550

551
552
  if( CHECK_INTERFACE( layer , PULSEAUDIO ))
  {
Emmanuel Milou's avatar
Emmanuel Milou committed
553
  // Cache the samples on the sound server
554
  // (PulseLayer*)audiolayer->putInCache( code, _buf , size * sizeof(SFLDataFormat) );
555
  }
Emmanuel Milou's avatar
Emmanuel Milou committed
556

Emmanuel Milou's avatar
Emmanuel Milou committed
557
558
  delete[] _buf; _buf = 0;
  return returnValue;
jpbl's avatar
jpbl committed
559
560
}

yanmorin's avatar
   
yanmorin committed
561
// Multi-thread 
jpbl's avatar
jpbl committed
562
563
bool
ManagerImpl::incomingCallWaiting() {
yanmorin's avatar
   
yanmorin committed
564
  ost::MutexLock m(_waitingCallMutex);
jpbl's avatar
jpbl committed
565
566
567
568
  return (_nbIncomingWaitingCall > 0) ? true : false;
}

void
yanmorin's avatar
   
yanmorin committed
569
570
571
ManagerImpl::addWaitingCall(const CallID& id) {
  ost::MutexLock m(_waitingCallMutex);
  _waitingCall.insert(id);
jpbl's avatar
jpbl committed
572
573
574
575
  _nbIncomingWaitingCall++;
}

void
yanmorin's avatar
   
yanmorin committed
576
577
578
579
580
ManagerImpl::removeWaitingCall(const CallID& id) {
  ost::MutexLock m(_waitingCallMutex);
  // should return more than 1 if it erase a call
  if (_waitingCall.erase(id)) {
    _nbIncomingWaitingCall--;
jpbl's avatar
jpbl committed
581
582
583
584
  }
}

bool
yanmorin's avatar
   
yanmorin committed
585
586
587
588
589
ManagerImpl::isWaitingCall(const CallID& id) {
  ost::MutexLock m(_waitingCallMutex);
  CallIDSet::iterator iter = _waitingCall.find(id);
  if (iter != _waitingCall.end()) {
    return false;
jpbl's avatar
jpbl committed
590
  }
yanmorin's avatar
   
yanmorin committed
591
  return true;
jpbl's avatar
jpbl committed
592
593
}

yanmorin's avatar
   
yanmorin committed
594
595
596
597
///////////////////////////////////////////////////////////////////////////////
// Management of event peer IP-phone 
////////////////////////////////////////////////////////////////////////////////
// SipEvent Thread 
598
  bool 
yanmorin's avatar
   
yanmorin committed
599
ManagerImpl::incomingCall(Call* call, const AccountID& accountId) 
jpbl's avatar
jpbl committed
600
{
601
  _debug("Incoming call %s\n", call->getCallId().data());
602

yanmorin's avatar
   
yanmorin committed
603
  associateCallToAccount(call->getCallId(), accountId);
604

yanmorin's avatar
   
yanmorin committed
605
606
  if ( !hasCurrentCall() ) {
    call->setConnectionState(Call::Ringing);
jpbl's avatar
jpbl committed
607
    ringtone();
yanmorin's avatar
   
yanmorin committed
608
    switchCall(call->getCallId());
jpbl's avatar
jpbl committed
609
  } else {
610
    addWaitingCall(call->getCallId());
jpbl's avatar
jpbl committed
611
612
  }

yanmorin's avatar
   
yanmorin committed
613
614
615
  std::string from = call->getPeerName();
  std::string number = call->getPeerNumber();

616
  if (from != "" && number != "") {
jpbl's avatar
jpbl committed
617
618
619
    from.append(" <");
    from.append(number);
    from.append(">");
620
621
622
623
  } else if ( from.empty() ) {
    from.append("<");
    from.append(number);
    from.append(">");
jpbl's avatar
jpbl committed
624
  }
625
  
626
  _dbus->getCallManager()->incomingCall(accountId, call->getCallId(), from);
627
  
628
  // Reduce volume of the other pulseaudio-connected audio applications
629
630
  if( getConfigInt( PREFERENCES , CONFIG_PA_VOLUME_CTRL ) )
    _audiodriver->reducePulseAppsVolume();
631
  
yanmorin's avatar
   
yanmorin committed
632
  return true;
jpbl's avatar
jpbl committed
633
634
}

yanmorin's avatar
   
yanmorin committed
635
636
637
//THREAD=VoIP
void
ManagerImpl::incomingMessage(const AccountID& accountId, const std::string& message) {
638
639
  if (_dbus) {
    _dbus->getCallManager()->incomingMessage(accountId, message);
jpbl's avatar
jpbl committed
640
641
642
  }
}

yanmorin's avatar
   
yanmorin committed
643
//THREAD=VoIP CALL=Outgoing
644
  void
yanmorin's avatar
   
yanmorin committed
645
ManagerImpl::peerAnsweredCall(const CallID& id)
jpbl's avatar
jpbl committed
646
{
yanmorin's avatar
   
yanmorin committed
647
  if (isCurrentCall(id)) {
648
    stopTone(false);
jpbl's avatar
jpbl committed
649
  }
Pierre-Luc Beaudoin's avatar
Pierre-Luc Beaudoin committed
650
  if (_dbus) _dbus->getCallManager()->callStateChanged(id, "CURRENT");
jpbl's avatar
jpbl committed
651
652
}

yanmorin's avatar
   
yanmorin committed
653
//THREAD=VoIP Call=Outgoing
654
  void
yanmorin's avatar
   
yanmorin committed
655
ManagerImpl::peerRingingCall(const CallID& id)
jpbl's avatar
jpbl committed
656
{
yanmorin's avatar
   
yanmorin committed
657
  if (isCurrentCall(id)) {
jpbl's avatar
jpbl committed
658
659
    ringback();
  }
Pierre-Luc Beaudoin's avatar
Pierre-Luc Beaudoin committed
660
  if (_dbus) _dbus->getCallManager()->callStateChanged(id, "RINGING");
jpbl's avatar
jpbl committed
661
662
}

yanmorin's avatar
   
yanmorin committed
663
//THREAD=VoIP Call=Outgoing/Ingoing
664
  void
yanmorin's avatar
   
yanmorin committed
665
ManagerImpl::peerHungupCall(const CallID& id)
jpbl's avatar
jpbl committed
666
{
yanmorin's avatar
   
yanmorin committed
667
668
669
670
  AccountID accountid = getAccountFromCall( id );
  if (accountid == AccountNULL) {
    _debug("peerHungupCall: Call doesn't exists\n");
    return;
Emmanuel Milou's avatar
Emmanuel Milou committed
671
  }
Emmanuel Milou's avatar
Emmanuel Milou committed
672
  
673
  if (_dbus) _dbus->getCallManager()->callStateChanged(id, "HUNGUP");
yanmorin's avatar
   
yanmorin committed
674
  if (isCurrentCall(id)) {
675
    stopTone(true);
yanmorin's avatar
   
yanmorin committed
676
    switchCall("");
jpbl's avatar
jpbl committed
677
  }
yanmorin's avatar
   
yanmorin committed
678
679
  removeWaitingCall(id);
  removeCallAccount(id);
Emmanuel Milou's avatar
Emmanuel Milou committed
680
  
681
682
  if( getConfigInt( PREFERENCES , CONFIG_PA_VOLUME_CTRL ) )
    _audiodriver->restorePulseAppsVolume();
jpbl's avatar
jpbl committed
683
684
}

yanmorin's avatar
   
yanmorin committed
685
//THREAD=VoIP
jpbl's avatar
jpbl committed
686
void
yanmorin's avatar
   
yanmorin committed
687
688
ManagerImpl::callBusy(const CallID& id) {
  _debug("Call busy\n");
689

690
  if (_dbus) _dbus->getCallManager()->callStateChanged(id, "BUSY");
yanmorin's avatar
   
yanmorin committed
691
692
693
  if (isCurrentCall(id) ) {
    playATone(Tone::TONE_BUSY);
    switchCall("");
jpbl's avatar
jpbl committed
694
  }
yanmorin's avatar
   
yanmorin committed
695
696
  removeCallAccount(id);
  removeWaitingCall(id);
jpbl's avatar
jpbl committed
697
698
}

yanmorin's avatar
   
yanmorin committed
699
//THREAD=VoIP
700
  void
yanmorin's avatar
   
yanmorin committed
701
702
ManagerImpl::callFailure(const CallID& id) 
{
703
  if (_dbus) _dbus->getCallManager()->callStateChanged(id, "FAILURE");
Emmanuel Milou's avatar
Emmanuel Milou committed
704
  _debug("CALL ID = %s\n" , id.c_str());
yanmorin's avatar
   
yanmorin committed
705
706
707
708
709
710
  if (isCurrentCall(id) ) {
    playATone(Tone::TONE_BUSY);
    switchCall("");
  }
  removeCallAccount(id);
  removeWaitingCall(id);
711

jpbl's avatar
jpbl committed
712
713
}

yanmorin's avatar
   
yanmorin committed
714
//THREAD=VoIP
715
  void
Emmanuel Milou's avatar
Emmanuel Milou committed
716
ManagerImpl::startVoiceMessageNotification(const AccountID& accountId, int nb_msg)
jpbl's avatar
jpbl committed
717
{
Emmanuel Milou's avatar
Emmanuel Milou committed
718
  if (_dbus) _dbus->getCallManager()->voiceMailNotify(accountId, nb_msg) ;
jpbl's avatar
jpbl committed
719
720
}

721
void ManagerImpl::connectionStatusNotification( void )
722
{
723
724
725
726
    if (_dbus)
        _dbus->getConfigurationManager()->accountsChanged();
    else
        _debug("Error: DBus connection not found\n");
Emmanuel Milou's avatar
nothing    
Emmanuel Milou committed
727
728
}

jpbl's avatar
jpbl committed
729
730
731
732
733
/**
 * Multi Thread
 */
bool 
ManagerImpl::playATone(Tone::TONEID toneId) {
yanmorin's avatar
   
yanmorin committed
734
735
736
  int hasToPlayTone = getConfigInt(SIGNALISATION, PLAY_TONES);
  if (!hasToPlayTone) return false;

jpbl's avatar
jpbl committed
737
738
739
740
741
  if (_telephoneTone != 0) {
    _toneMutex.enterMutex();
    _telephoneTone->setCurrentTone(toneId);
    _toneMutex.leaveMutex();

742
743
744
745
    AudioLoop* audioloop = getTelephoneTone();
    unsigned int nbSampling = audioloop->getSize();
    AudioLayer* audiolayer = getAudioDriver();
    SFLDataFormat buf[nbSampling];
746
747
    if ( audiolayer ) {
      int layer = audiolayer->getLayerType(); 
748
    if(CHECK_INTERFACE( layer , ALSA ) )
749
      audiolayer->putUrgent( buf, nbSampling );
750
751
752
    else{
      audiolayer->startStream();
    }
jpbl's avatar
jpbl committed
753
    }
754
755
    else 
      return false;
jpbl's avatar
jpbl committed
756
  }
757
  return true;
jpbl's avatar
jpbl committed
758
759
760
761
762
763
}

/**
 * Multi Thread
 */
void 
764
ManagerImpl::stopTone(bool stopAudio=true) {
yanmorin's avatar
   
yanmorin committed
765
766
767
  int hasToPlayTone = getConfigInt(SIGNALISATION, PLAY_TONES);
  if (!hasToPlayTone) return;

768
  if (stopAudio) {
769
    AudioLayer* audiolayer = getAudioDriver();
770
    int layer = audiolayer->getLayerType();
771
772
    if(CHECK_INTERFACE( layer , ALSA ) ){}
    else{}
773
  //if (audiolayer) { audiolayer->stopStream(); }
774

jpbl's avatar
jpbl committed
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
  }

  _toneMutex.enterMutex();
  if (_telephoneTone != 0) {
    _telephoneTone->setCurrentTone(Tone::TONE_NULL);
  }
  _toneMutex.leaveMutex();

  // for ringing tone..
  _toneMutex.enterMutex();
  _audiofile.stop();
  _toneMutex.leaveMutex();
}

/**
 * Multi Thread
 */
792
  bool
jpbl's avatar
jpbl committed
793
794
ManagerImpl::playTone()
{
795
  playATone(Tone::TONE_DIALTONE);
Yun Liu's avatar
Yun Liu committed
796
  return true;
jpbl's avatar
jpbl committed
797
798
}

799
800
801
802
803
804
805
/**
 * Multi Thread
 */
  bool
ManagerImpl::playToneWithMessage()
{
  playATone(Tone::TONE_CONGESTION);
Yun Liu's avatar
Yun Liu committed
806
  return true;
807
808
}

jpbl's avatar
jpbl committed
809
810
811
812
813
814
815
816
817
818
819
820
821
822
/**
 * Multi Thread
 */
void
ManagerImpl::congestion () {
  playATone(Tone::TONE_CONGESTION);
}

/**
 * Multi Thread
 */
void
ManagerImpl::ringback () {
  playATone(Tone::TONE_RINGTONE);
823
  getAudioDriver()->trigger_thread();
jpbl's avatar
jpbl committed
824
825
826
827
828
}

/**
 * Multi Thread
 */
829
  void
jpbl's avatar
jpbl committed
830
831
ManagerImpl::ringtone() 
{
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
    if( isRingtoneEnabled() )
    {
        //TODO Comment this because it makes the daemon crashes since the main thread
        //synchronizes the ringtone thread.
        
        std::string ringchoice = getConfigString(AUDIO, RING_CHOICE);
        //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; 
        }

        AudioLayer* audiolayer = getAudioDriver();
        int layer = audiolayer->getLayerType();
        if (audiolayer==0) { return; }
        int sampleRate  = audiolayer->getSampleRate();
        AudioCodec* codecForTone = _codecDescriptorMap.getFirstCodecAvailable();
Emmanuel Milou's avatar
Emmanuel Milou committed
849

850
        _toneMutex.enterMutex(); 
851
         bool loadFile = _audiofile.loadFile(ringchoice, codecForTone , sampleRate);
852
        _toneMutex.leaveMutex(); 
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
        if (loadFile) {
            _toneMutex.enterMutex(); 
            _audiofile.start();
            _toneMutex.leaveMutex(); 
            if(CHECK_INTERFACE( layer, ALSA )){
                /*int size = _audiofile.getSize();
                SFLDataFormat output[ size ];
                _audiofile.getNext(output, size , 100);
                audiolayer->putUrgent( output , size );
                audiolayer->trigger_thread();*/
                ringback();
            }
            else{
                audiolayer->startStream();
            }
        } else {
            ringback();
870
        }
871
872
873
874
875
    
    }
    else
    {
        ringback();
Emmanuel Milou's avatar
Emmanuel Milou committed
876
    }
jpbl's avatar
jpbl committed
877
878
}

879
  AudioLoop*
jpbl's avatar
jpbl committed
880
881
882
883
884
885
886
887
888
889
890
ManagerImpl::getTelephoneTone()
{
  if(_telephoneTone != 0) {
    ost::MutexLock m(_toneMutex);
    return _telephoneTone->getCurrentTone();
  }
  else {
    return 0;
  }
}

891
  AudioLoop*
jpbl's avatar
jpbl committed
892
893
894
895
896
897
898
899
900
901
902
ManagerImpl::getTelephoneFile()
{
  ost::MutexLock m(_toneMutex);
  if(_audiofile.isStarted()) {
    return &_audiofile;
  } else {
    return 0;
  }
}

void
yanmorin's avatar
   
yanmorin committed
903
ManagerImpl::notificationIncomingCall(void) {
jpbl's avatar
jpbl committed
904
905
906

  AudioLayer* audiolayer = getAudioDriver();
  if (audiolayer != 0) {
907
    int layer = audiolayer->getLayerType();
yanmorin's avatar
   
yanmorin committed
908
    unsigned int samplerate = audiolayer->getSampleRate();
jpbl's avatar
jpbl committed
909
910
911
    std::ostringstream frequency;
    frequency << "440/" << FRAME_PER_BUFFER;

912
913
914
    Tone tone(frequency.str(), samplerate);
    unsigned int nbSampling = tone.getSize();
    SFLDataFormat buf[nbSampling];
jpbl's avatar
jpbl committed
915
    tone.getNext(buf, tone.getSize());
916
917
918
919
920
    if(CHECK_INTERFACE( layer , ALSA ))
      audiolayer->playSamples(buf, sizeof(SFLDataFormat)*nbSampling, true);
    else
      audiolayer->putUrgent( buf, sizeof(SFLDataFormat)*nbSampling );
  
jpbl's avatar
jpbl committed
921
922
923
924
925
926
  }
}

/**
 * Multi Thread
 */
927
  bool
yanmorin's avatar
   
yanmorin committed
928
ManagerImpl::getStunInfo (StunAddress4& stunSvrAddr, int port) 
jpbl's avatar
jpbl committed
929
930
931
932
{
  StunAddress4 mappedAddr;
  struct in_addr in;
  char* addr;
yanmorin's avatar
   
yanmorin committed
933
934
935
936
937

  //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
938
  if (ok) {
yanmorin's avatar
   
yanmorin committed
939
940
941
    closesocket(fd1);
    //closesocket(fd3);
    //closesocket(fd4);
jpbl's avatar
jpbl committed
942
943
944
945
946
947
    _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
948
    return true;
jpbl's avatar
jpbl committed
949
950
951
  } else {
    _debug("Opening a stun socket pair failed\n");
  }
yanmorin's avatar
   
yanmorin committed
952
  return false;
jpbl's avatar
jpbl committed
953
954
}

955
  bool
yanmorin's avatar
   
yanmorin committed
956
ManagerImpl::behindNat(const std::string& svr, int port)
yanmorin's avatar
   
yanmorin committed
957
958
959
{
  StunAddress4 stunSvrAddr;
  stunSvrAddr.addr = 0;
960

yanmorin's avatar
   
yanmorin committed
961
962
963
964
965
  // 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
966
  }
967

yanmorin's avatar
   
yanmorin committed
968
  // Firewall address
969
  _debug("STUN server: %s\n", svr.data());
yanmorin's avatar
   
yanmorin committed
970
  return getStunInfo(stunSvrAddr, port);
jpbl's avatar
jpbl committed
971
972
}

yanmorin's avatar
   
yanmorin committed
973

jpbl's avatar
jpbl committed
974
975
976
977
978
979
///////////////////////////////////////////////////////////////////////////////
// Private functions
///////////////////////////////////////////////////////////////////////////////
/**
 * Initialization: Main Thread
 * @return 1: ok
980
 -1: error directory
jpbl's avatar
jpbl committed
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
 */
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";
996
  return 1;
jpbl's avatar
jpbl committed
997
998
999
1000
}

/**
 * Initialization: Main Thread