managerimpl.cpp 56.8 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
29
30
31
32
33
34
35
36
37
 *  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>

#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>

38
39
#include <boost/tokenizer.hpp>

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

yanmorin's avatar
   
yanmorin committed
45
#include "accountcreator.h" // create new account
46
#include "voiplink.h"
jpbl's avatar
jpbl committed
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72

#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)))

ManagerImpl::ManagerImpl (void)
{
  // Init private variables 
  _hasZeroconf = false;
#ifdef USE_ZEROCONF
  _hasZeroconf = true;
  _DNSService = new DNSService();
#endif

  // setup
  _path = ""; 
  _exist = 0;
  _setupLoaded = false;
73
  _dbus = NULL;
jpbl's avatar
jpbl committed
74

yanmorin's avatar
   
yanmorin committed
75
  // sound
Alexandre Bourget's avatar
Pouet    
Alexandre Bourget committed
76
  _audiodriver = NULL;
jpbl's avatar
jpbl committed
77
  _dtmfKey = 0;
78
  _spkr_volume = 0;  // Initialize after by init() -> initVolume()
yanmorin's avatar
   
yanmorin committed
79
80
  _mic_volume  = 0;  // Initialize after by init() -> initVolume()
  _mic_volume_before_mute = 0; 
jpbl's avatar
jpbl committed
81
82
83
84

  // Call
  _nbIncomingWaitingCall=0;
  _hasTriedToRegister = false;
yanmorin's avatar
   
yanmorin committed
85

jpbl's avatar
jpbl committed
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
103
104
105
}

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

#ifdef USE_ZEROCONF
106
  delete _DNSService; _DNSService = 0;
jpbl's avatar
jpbl committed
107
108
109
110
111
#endif

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

112
  void 
jpbl's avatar
jpbl committed
113
114
ManagerImpl::init() 
{
115
116
117
  // Load accounts, init map
  loadAccountMap();

jpbl's avatar
jpbl committed
118
119
120
121
122
123
  initVolume();

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

124
125
126
  initAudioDriver();
  selectAudioDriver();

127
  // Initialize the list of supported audio codecs
jpbl's avatar
jpbl committed
128
129
  initAudioCodec();

130
131
  getAudioInputDeviceList();

jpbl's avatar
jpbl committed
132
133
134
135
136
137
  AudioLayer *audiolayer = getAudioDriver();
  if (audiolayer!=0) {
    unsigned int sampleRate = audiolayer->getSampleRate();

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

    _debugInit("Loading DTMF key");
141
    _dtmfKey = new DTMF(sampleRate);
jpbl's avatar
jpbl committed
142
143
  }

144
  // initRegisterAccounts was here, but we doing it after the gui loaded... 
jpbl's avatar
jpbl committed
145
146
147
148
149
150
151
152
  // the stun detection is long, so it's a better idea to do it after getEvents
  initZeroconf();
}

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

yanmorin's avatar
   
yanmorin committed
153
  unloadAccountMap();
jpbl's avatar
jpbl committed
154
155
156
157
158

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

  _debug("Unload Audio Driver\n");
159
  delete _audiodriver; _audiodriver = NULL;
jpbl's avatar
jpbl committed
160
161

  _debug("Unload Telephone Tone\n");
162
  delete _telephoneTone; _telephoneTone = NULL;
jpbl's avatar
jpbl committed
163
164
}

yanmorin's avatar
   
yanmorin committed
165
166
167
168
bool
ManagerImpl::isCurrentCall(const CallID& callId) {
  ost::MutexLock m(_currentCallMutex);
  return (_currentCallId2 == callId ? true : false);
jpbl's avatar
jpbl committed
169
170
}

yanmorin's avatar
   
yanmorin committed
171
172
173
bool
ManagerImpl::hasCurrentCall() {
  ost::MutexLock m(_currentCallMutex);
Emmanuel Milou's avatar
Bug fix    
Emmanuel Milou committed
174
  _debug("Current call ID = %s\n", _currentCallId2.c_str());
yanmorin's avatar
   
yanmorin committed
175
176
  if ( _currentCallId2 != "") {
    return true;
jpbl's avatar
jpbl committed
177
  }
yanmorin's avatar
   
yanmorin committed
178
  return false;
jpbl's avatar
jpbl committed
179
180
}

yanmorin's avatar
   
yanmorin committed
181
182
183
184
const CallID& 
ManagerImpl::getCurrentCallId() {
  ost::MutexLock m(_currentCallMutex);
  return _currentCallId2;
jpbl's avatar
jpbl committed
185
186
187
}

void
yanmorin's avatar
   
yanmorin committed
188
189
190
ManagerImpl::switchCall(const CallID& id ) {
  ost::MutexLock m(_currentCallMutex);
  _currentCallId2 = id;
jpbl's avatar
jpbl committed
191
192
193
194
195
196
}


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

yanmorin's avatar
   
yanmorin committed
224
//THREAD=Main : for outgoing Call
225
  bool
yanmorin's avatar
   
yanmorin committed
226
ManagerImpl::answerCall(const CallID& id)
jpbl's avatar
jpbl committed
227
{
228
  stopTone(false); 
Emmanuel Milou's avatar
Bug fix    
Emmanuel Milou committed
229
  /*if (hasCurrentCall()) 
230
    { 
231
    onHoldCall(getCurrentCallId());
232
    }*/
yanmorin's avatar
   
yanmorin committed
233
234
235
236
  AccountID accountid = getAccountFromCall( id );
  if (accountid == AccountNULL) {
    _debug("Answering Call: Call doesn't exists\n");
    return false;
jpbl's avatar
jpbl committed
237
  }
yanmorin's avatar
   
yanmorin committed
238
239
240
241
242

  if (!getAccountLink(accountid)->answer(id)) {
    // error when receiving...
    removeCallAccount(id);
    return false;
jpbl's avatar
jpbl committed
243
  }
yanmorin's avatar
   
yanmorin committed
244

245
  //Place current call on hold if it isn't
246
247


yanmorin's avatar
   
yanmorin committed
248
  // if it was waiting, it's waiting no more
249
  if (_dbus) _dbus->getCallManager()->callStateChanged(id, "CURRENT");
yanmorin's avatar
   
yanmorin committed
250
251
252
  removeWaitingCall(id);
  switchCall(id);
  return true;
jpbl's avatar
jpbl committed
253
254
}

yanmorin's avatar
   
yanmorin committed
255
//THREAD=Main
256
  bool 
yanmorin's avatar
   
yanmorin committed
257
ManagerImpl::sendTextMessage(const AccountID& accountId, const std::string& to, const std::string& message) 
jpbl's avatar
jpbl committed
258
{
yanmorin's avatar
   
yanmorin committed
259
260
  if (accountExists(accountId)) {
    return getAccountLink(accountId)->sendMessage(to, message);
jpbl's avatar
jpbl committed
261
  }
yanmorin's avatar
   
yanmorin committed
262
  return false;
jpbl's avatar
jpbl committed
263
264
}

yanmorin's avatar
   
yanmorin committed
265
//THREAD=Main
266
  bool
yanmorin's avatar
   
yanmorin committed
267
ManagerImpl::hangupCall(const CallID& id)
jpbl's avatar
jpbl committed
268
{
269
  stopTone(true);
270
  if (_dbus) _dbus->getCallManager()->callStateChanged(id, "HUNGUP");
yanmorin's avatar
   
yanmorin committed
271
272
  AccountID accountid = getAccountFromCall( id );
  if (accountid == AccountNULL) {
273
274
    /** @todo We should tell the GUI that the call doesn't exist, so
     * it clears up. This can happen. */
275
    _debug("! Manager Hangup Call: Call doesn't exists\n");
yanmorin's avatar
   
yanmorin committed
276
    return false;
jpbl's avatar
jpbl committed
277
  }
yanmorin's avatar
   
yanmorin committed
278
279
280
281

  bool returnValue = getAccountLink(accountid)->hangup(id);
  removeCallAccount(id);
  switchCall("");
282
283


yanmorin's avatar
   
yanmorin committed
284
  return returnValue;
jpbl's avatar
jpbl committed
285
286
}

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

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

yanmorin's avatar
   
yanmorin committed
304
  return returnValue;
jpbl's avatar
jpbl committed
305
306
}

yanmorin's avatar
   
yanmorin committed
307
//THREAD=Main
308
  bool
yanmorin's avatar
   
yanmorin committed
309
ManagerImpl::onHoldCall(const CallID& id)
jpbl's avatar
jpbl committed
310
{
311
  stopTone(true);
yanmorin's avatar
   
yanmorin committed
312
313
  AccountID accountid = getAccountFromCall( id );
  if (accountid == AccountNULL) {
314
    _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
315
    return false;
jpbl's avatar
jpbl committed
316
  }
yanmorin's avatar
   
yanmorin committed
317

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

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

yanmorin's avatar
   
yanmorin committed
322
  removeWaitingCall(id);
323
  if (_dbus) _dbus->getCallManager()->callStateChanged(id, "HOLD");
yanmorin's avatar
   
yanmorin committed
324
  switchCall("");
325

yanmorin's avatar
   
yanmorin committed
326
327
328
329
  return returnValue;
}

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

340
341
342
343
344
  //Place current call on hold if it isn't
  if (hasCurrentCall()) 
  { 
    onHoldCall(getCurrentCallId());
  }
345

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

yanmorin's avatar
   
yanmorin committed
348
  bool returnValue = getAccountLink(accountid)->offhold(id);
349
  if (_dbus) _dbus->getCallManager()->callStateChanged(id, "UNHOLD");
yanmorin's avatar
   
yanmorin committed
350
351
  switchCall(id);
  if (returnValue) {
jpbl's avatar
jpbl committed
352
    try {
353
      //getAudioDriver()->startStream();
jpbl's avatar
jpbl committed
354
    } catch(...) {
355
      _debugException("! Manager Off hold could not start audio stream");
jpbl's avatar
jpbl committed
356
357
358
359
360
    }
  }
  return returnValue;
}

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

  return returnValue;
jpbl's avatar
jpbl committed
378
379
}

yanmorin's avatar
   
yanmorin committed
380
//THREAD=Main
jpbl's avatar
jpbl committed
381
382
383
384
385
386
void
ManagerImpl::mute() {
  _mic_volume_before_mute = _mic_volume;
  setMicVolume(0);
}

yanmorin's avatar
   
yanmorin committed
387
//THREAD=Main
jpbl's avatar
jpbl committed
388
389
390
391
392
393
394
void
ManagerImpl::unmute() {
  if ( _mic_volume == 0 ) {
    setMicVolume(_mic_volume_before_mute);
  }
}

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

yanmorin's avatar
   
yanmorin committed
417
//THREAD=Main
418
  bool
jpbl's avatar
jpbl committed
419
420
421
422
423
424
425
426
427
428
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
429
//THREAD=Main
430
  bool
431
ManagerImpl::initRegisterAccounts() 
jpbl's avatar
jpbl committed
432
{
433
  _debugInit("Initiate VoIP Links Registration");
yanmorin's avatar
   
yanmorin committed
434
435
436
437
  AccountMap::iterator iter = _accountMap.begin();
  while( iter != _accountMap.end() ) {
    if ( iter->second) {
      iter->second->loadConfig();
438
      if ( iter->second->isEnabled() ) {
439
	iter->second->registerVoIPLink();
jpbl's avatar
jpbl committed
440
441
      }
    }
yanmorin's avatar
   
yanmorin committed
442
    iter++;
jpbl's avatar
jpbl committed
443
  }
yanmorin's avatar
   
yanmorin committed
444
  return true;
jpbl's avatar
jpbl committed
445
446
}

yanmorin's avatar
   
yanmorin committed
447
//THREAD=Main
448
// Currently unused
449
  bool
450
ManagerImpl::registerAccount(const AccountID& accountId)
jpbl's avatar
jpbl committed
451
{
452
453
  _debug("Register one VoIP Link\n");

454
455
456
457
458
459
460
  // right now, we don't support two SIP account
  // so we close everything before registring a new account
  Account* account = getAccount(accountId);
  if (account != 0) {
    AccountMap::iterator iter = _accountMap.begin();
    while ( iter != _accountMap.end() ) {
      if ( iter->second ) {
461
	iter->second->unregisterVoIPLink();
462
463
464
      }
      iter++;
    }
465
    account->registerVoIPLink();
jpbl's avatar
jpbl committed
466
  }
467
  return true;
jpbl's avatar
jpbl committed
468
469
}

yanmorin's avatar
   
yanmorin committed
470
//THREAD=Main
471
// Currently unused
472
  bool 
473
ManagerImpl::unregisterAccount(const AccountID& accountId)
jpbl's avatar
jpbl committed
474
{
475
476
  _debug("Unregister one VoIP Link\n");

yanmorin's avatar
   
yanmorin committed
477
  if (accountExists( accountId ) ) {
478
    getAccount(accountId)->unregisterVoIPLink();
yanmorin's avatar
   
yanmorin committed
479
  }
480
  return true;
jpbl's avatar
jpbl committed
481
482
}

yanmorin's avatar
   
yanmorin committed
483
//THREAD=Main
484
  bool 
yanmorin's avatar
   
yanmorin committed
485
ManagerImpl::sendDtmf(const CallID& id, char code)
jpbl's avatar
jpbl committed
486
{
yanmorin's avatar
   
yanmorin committed
487
488
489
490
491
492
  AccountID accountid = getAccountFromCall( id );
  if (accountid == AccountNULL) {
    _debug("Send DTMF: call doesn't exists\n");
    return false;
  }

jpbl's avatar
jpbl committed
493
  int sendType = getConfigInt(SIGNALISATION, SEND_DTMF_AS);
yanmorin's avatar
   
yanmorin committed
494
  bool returnValue = false;
jpbl's avatar
jpbl committed
495
  switch (sendType) {
496
497
498
499
500
501
502
503
504
505
506
    case 0: // SIP INFO
      playDtmf(code);
      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
507
508
509
510
  }
  return returnValue;
}

yanmorin's avatar
   
yanmorin committed
511
//THREAD=Main | VoIPLink
512
  bool
jpbl's avatar
jpbl committed
513
514
ManagerImpl::playDtmf(char code)
{
515
516
517
518
  // HERE are the variable:
  // - boolean variable to play or not (config)
  // - length in milliseconds to play
  // - sample of audiolayer
519
  stopTone(false);
yanmorin's avatar
   
yanmorin committed
520
521
  int hasToPlayTone = getConfigInt(SIGNALISATION, PLAY_DTMF);
  if (!hasToPlayTone) return false;
jpbl's avatar
jpbl committed
522
523
524
525
526
527
528
529
530
531
532

  // 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();

  // fast return, no sound, so no dtmf
  if (audiolayer==0 || _dtmfKey == 0) { return false; }
533
534
535
536
  // number of data sampling in one pulselen depends on samplerate
  // size (n sampling) = time_ms * sampling/s 
  //                     ---------------------
  //                            ms/s
537
538
  int size = (int)(pulselen * ((float)audiolayer->getSampleRate()/1000));

539
540
541
  // 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
542
543
544
545
546
  bool returnValue = false;

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

547
  // copy the sound
548
  if ( _dtmfKey->generateDTMF(_buf, size) ) {
jpbl's avatar
jpbl committed
549
550
551

    // Put buffer to urgentRingBuffer 
    // put the size in bytes...
552
    // so size * 1 channel (mono) * sizeof (bytes for the data)
553
554
    audiolayer->playSamples(_buf, size * sizeof(SFLDataFormat));
    //audiolayer->putUrgent(_buf, size * sizeof(SFLDataFormat));
jpbl's avatar
jpbl committed
555

556
    // We activate the stream if it's not active yet.
557
    //if (!audiolayer->isStreamActive()) {
558
      //audiolayer->startStream();
559
560
561
562
    //} else {
      //_debugAlsa("send dtmf - sleep\n");
      //audiolayer->sleep(pulselen); // in milliseconds
    //}
jpbl's avatar
jpbl committed
563
  }
564
  returnValue = true;
565

566
567
568
// TODO: add caching
delete[] _buf; _buf = 0;
return returnValue;
jpbl's avatar
jpbl committed
569
570
}

yanmorin's avatar
   
yanmorin committed
571
// Multi-thread 
jpbl's avatar
jpbl committed
572
573
bool
ManagerImpl::incomingCallWaiting() {
yanmorin's avatar
   
yanmorin committed
574
  ost::MutexLock m(_waitingCallMutex);
jpbl's avatar
jpbl committed
575
576
577
578
  return (_nbIncomingWaitingCall > 0) ? true : false;
}

void
yanmorin's avatar
   
yanmorin committed
579
580
581
ManagerImpl::addWaitingCall(const CallID& id) {
  ost::MutexLock m(_waitingCallMutex);
  _waitingCall.insert(id);
jpbl's avatar
jpbl committed
582
583
584
585
  _nbIncomingWaitingCall++;
}

void
yanmorin's avatar
   
yanmorin committed
586
587
588
589
590
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
591
592
593
594
  }
}

bool
yanmorin's avatar
   
yanmorin committed
595
596
597
598
599
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
600
  }
yanmorin's avatar
   
yanmorin committed
601
  return true;
jpbl's avatar
jpbl committed
602
603
604
605
}



yanmorin's avatar
   
yanmorin committed
606
607
608
609
///////////////////////////////////////////////////////////////////////////////
// Management of event peer IP-phone 
////////////////////////////////////////////////////////////////////////////////
// SipEvent Thread 
610
  bool 
yanmorin's avatar
   
yanmorin committed
611
ManagerImpl::incomingCall(Call* call, const AccountID& accountId) 
jpbl's avatar
jpbl committed
612
{
yanmorin's avatar
   
yanmorin committed
613
  _debug("Incoming call\n");
614

yanmorin's avatar
   
yanmorin committed
615
  associateCallToAccount(call->getCallId(), accountId);
616

yanmorin's avatar
   
yanmorin committed
617
618
  if ( !hasCurrentCall() ) {
    call->setConnectionState(Call::Ringing);
619
    _debugAlsa(" call ringtone() method\n ");
jpbl's avatar
jpbl committed
620
    ringtone();
yanmorin's avatar
   
yanmorin committed
621
    switchCall(call->getCallId());
jpbl's avatar
jpbl committed
622
  } else {
623
    addWaitingCall(call->getCallId());
jpbl's avatar
jpbl committed
624
625
  }

yanmorin's avatar
   
yanmorin committed
626
627
628
  std::string from = call->getPeerName();
  std::string number = call->getPeerNumber();

629
  if (from != "" && number != "") {
jpbl's avatar
jpbl committed
630
631
632
    from.append(" <");
    from.append(number);
    from.append(">");
633
634
635
636
  } else if ( from.empty() ) {
    from.append("<");
    from.append(number);
    from.append(">");
jpbl's avatar
jpbl committed
637
  }
638
  _dbus->getCallManager()->incomingCall(accountId, call->getCallId(), from);
yanmorin's avatar
   
yanmorin committed
639
  return true;
jpbl's avatar
jpbl committed
640
641
}

yanmorin's avatar
   
yanmorin committed
642
643
644
//THREAD=VoIP
void
ManagerImpl::incomingMessage(const AccountID& accountId, const std::string& message) {
645
646
  if (_dbus) {
    _dbus->getCallManager()->incomingMessage(accountId, message);
jpbl's avatar
jpbl committed
647
648
649
  }
}

yanmorin's avatar
   
yanmorin committed
650
//THREAD=VoIP CALL=Outgoing
651
  void
yanmorin's avatar
   
yanmorin committed
652
ManagerImpl::peerAnsweredCall(const CallID& id)
jpbl's avatar
jpbl committed
653
{
yanmorin's avatar
   
yanmorin committed
654
  if (isCurrentCall(id)) {
655
    stopTone(false);
jpbl's avatar
jpbl committed
656
  }
Pierre-Luc Beaudoin's avatar
Pierre-Luc Beaudoin committed
657
  if (_dbus) _dbus->getCallManager()->callStateChanged(id, "CURRENT");
jpbl's avatar
jpbl committed
658
659
}

yanmorin's avatar
   
yanmorin committed
660
//THREAD=VoIP Call=Outgoing
661
  void
yanmorin's avatar
   
yanmorin committed
662
ManagerImpl::peerRingingCall(const CallID& id)
jpbl's avatar
jpbl committed
663
{
yanmorin's avatar
   
yanmorin committed
664
  if (isCurrentCall(id)) {
jpbl's avatar
jpbl committed
665
666
    ringback();
  }
Pierre-Luc Beaudoin's avatar
Pierre-Luc Beaudoin committed
667
  if (_dbus) _dbus->getCallManager()->callStateChanged(id, "RINGING");
jpbl's avatar
jpbl committed
668
669
}

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

jpbl's avatar
jpbl committed
687
688
}

yanmorin's avatar
   
yanmorin committed
689
//THREAD=VoIP
jpbl's avatar
jpbl committed
690
void
yanmorin's avatar
   
yanmorin committed
691
692
ManagerImpl::callBusy(const CallID& id) {
  _debug("Call busy\n");
693

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

yanmorin's avatar
   
yanmorin committed
703
//THREAD=VoIP
704
  void
yanmorin's avatar
   
yanmorin committed
705
706
707
ManagerImpl::callFailure(const CallID& id) 
{
  _debug("Call failed\n");
708
  if (_dbus) _dbus->getCallManager()->callStateChanged(id, "FAILURE");
yanmorin's avatar
   
yanmorin committed
709
710
711
712
713
714
  if (isCurrentCall(id) ) {
    playATone(Tone::TONE_BUSY);
    switchCall("");
  }
  removeCallAccount(id);
  removeWaitingCall(id);
715

jpbl's avatar
jpbl committed
716
717
}

yanmorin's avatar
   
yanmorin committed
718
//THREAD=VoIP
719
  void 
yanmorin's avatar
   
yanmorin committed
720
ManagerImpl::displayTextMessage(const CallID& id, const std::string& message)
jpbl's avatar
jpbl committed
721
{
722
  /*if(_gui) {
723
724
    _gui->displayTextMessage(id, message);
    }*/
jpbl's avatar
jpbl committed
725
726
}

yanmorin's avatar
   
yanmorin committed
727
//THREAD=VoIP
728
  void 
yanmorin's avatar
   
yanmorin committed
729
ManagerImpl::displayErrorText(const CallID& id, const std::string& message)
jpbl's avatar
jpbl committed
730
{
731
  /*if(_gui) {
jpbl's avatar
jpbl committed
732
    _gui->displayErrorText(id, message);
733
    } else {
jpbl's avatar
jpbl committed
734
    std::cerr << message << std::endl;
735
    }*/
jpbl's avatar
jpbl committed
736
737
}

yanmorin's avatar
   
yanmorin committed
738
//THREAD=VoIP
739
  void 
yanmorin's avatar
   
yanmorin committed
740
ManagerImpl::displayError (const std::string& error)
jpbl's avatar
jpbl committed
741
{
742
  /*if(_gui) {
yanmorin's avatar
   
yanmorin committed
743
    _gui->displayError(error);
744
    }*/
jpbl's avatar
jpbl committed
745
746
}

yanmorin's avatar
   
yanmorin committed
747
//THREAD=VoIP
748
  void 
yanmorin's avatar
   
yanmorin committed
749
ManagerImpl::displayStatus(const std::string& status)
jpbl's avatar
jpbl committed
750
{
751
  /*if(_gui) {
jpbl's avatar
jpbl committed
752
    _gui->displayStatus(status);
753
    }*/
jpbl's avatar
jpbl committed
754
755
}

yanmorin's avatar
   
yanmorin committed
756
//THREAD=VoIP
757
  void 
jpbl's avatar
jpbl committed
758
759
ManagerImpl::displayConfigError (const std::string& message)
{
760
  /*if(_gui) {
jpbl's avatar
jpbl committed
761
    _gui->displayConfigError(message);
762
    }*/
jpbl's avatar
jpbl committed
763
764
}

yanmorin's avatar
   
yanmorin committed
765
//THREAD=VoIP
766
  void
yanmorin's avatar
   
yanmorin committed
767
ManagerImpl::startVoiceMessageNotification(const AccountID& accountId, const std::string& nb_msg)
jpbl's avatar
jpbl committed
768
{
Pierre-Luc Beaudoin's avatar
Pierre-Luc Beaudoin committed
769
  if (_dbus) _dbus->getCallManager()->voiceMailNotify(accountId, atoi(nb_msg.c_str()) );
jpbl's avatar
jpbl committed
770
771
}

yanmorin's avatar
   
yanmorin committed
772
//THREAD=VoIP
773
  void
yanmorin's avatar
   
yanmorin committed
774
ManagerImpl::stopVoiceMessageNotification(const AccountID& accountId)
jpbl's avatar
jpbl committed
775
{
Pierre-Luc Beaudoin's avatar
Pierre-Luc Beaudoin committed
776
  if (_dbus) _dbus->getCallManager()->voiceMailNotify(accountId, 0 );
777
} 
jpbl's avatar
jpbl committed
778

yanmorin's avatar
   
yanmorin committed
779
//THREAD=VoIP
780
  void 
yanmorin's avatar
   
yanmorin committed
781
ManagerImpl::registrationSucceed(const AccountID& accountid)
jpbl's avatar
jpbl committed
782
{
783
  Account* acc = getAccount(accountid);
784
  if ( acc ) { 
785
    //acc->setState(true); 
Pierre-Luc Beaudoin's avatar
Pierre-Luc Beaudoin committed
786
    if (_dbus) _dbus->getConfigurationManager()->accountsChanged();
Pierre-Luc Beaudoin's avatar
Pierre-Luc Beaudoin committed
787
  }
jpbl's avatar
jpbl committed
788
789
}

yanmorin's avatar
   
yanmorin committed
790
//THREAD=VoIP
791
  void 
yanmorin's avatar
   
yanmorin committed
792
ManagerImpl::registrationFailed(const AccountID& accountid)
jpbl's avatar
jpbl committed
793
{
Pierre-Luc Beaudoin's avatar
Pierre-Luc Beaudoin committed
794
  Account* acc = getAccount(accountid);
795
  if ( acc ) { 
796
    //acc->setState(false);
Pierre-Luc Beaudoin's avatar
Pierre-Luc Beaudoin committed
797
    if (_dbus) _dbus->getConfigurationManager()->accountsChanged();
Pierre-Luc Beaudoin's avatar
Pierre-Luc Beaudoin committed
798
  }
jpbl's avatar
jpbl committed
799
800
801
802
803
804
805
}

/**
 * Multi Thread
 */
bool 
ManagerImpl::playATone(Tone::TONEID toneId) {
yanmorin's avatar
   
yanmorin committed
806
807
808
  int hasToPlayTone = getConfigInt(SIGNALISATION, PLAY_TONES);
  if (!hasToPlayTone) return false;

jpbl's avatar
jpbl committed
809
810
811
812
813
  if (_telephoneTone != 0) {
    _toneMutex.enterMutex();
    _telephoneTone->setCurrentTone(toneId);
    _toneMutex.leaveMutex();

814
815
816
817
    AudioLoop* audioloop = getTelephoneTone();
    unsigned int nbSampling = audioloop->getSize();
    AudioLayer* audiolayer = getAudioDriver();
    SFLDataFormat buf[nbSampling];
818
819
    audioloop->getNext(buf, (int) nbSampling);
    if ( audiolayer ) { 
Emmanuel Milou's avatar
Emmanuel Milou committed
820
      //audiolayer->putUrgent( buf, sizeof(SFLDataFormat)*nbSampling );
jpbl's avatar
jpbl committed
821
    }
822
823
    else 
      return false;
jpbl's avatar
jpbl committed
824
  }
825
  return true;
jpbl's avatar
jpbl committed
826
827
828
829
830
831
}

/**
 * Multi Thread
 */
void 
832
ManagerImpl::stopTone(bool stopAudio=true) {
yanmorin's avatar
   
yanmorin committed
833
834
835
  int hasToPlayTone = getConfigInt(SIGNALISATION, PLAY_TONES);
  if (!hasToPlayTone) return;

836
  if (stopAudio) {
837
838
    AudioLayer* audiolayer = getAudioDriver();
    if (audiolayer) { audiolayer->stopStream(); }
jpbl's avatar
jpbl committed
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
  }

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

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

/**
 * Multi Thread
 */
856
  bool
jpbl's avatar
jpbl committed
857
858
ManagerImpl::playTone()
{
859
860
  //return playATone(Tone::TONE_DIALTONE);
  playATone(Tone::TONE_DIALTONE);
jpbl's avatar
jpbl committed
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
}

/**
 * Multi Thread
 */
void
ManagerImpl::congestion () {
  playATone(Tone::TONE_CONGESTION);
}

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

/**
 * Multi Thread
 */
882
  void
jpbl's avatar
jpbl committed
883
884
ManagerImpl::ringtone() 
{
yanmorin's avatar
   
yanmorin committed
885
  int hasToPlayTone = getConfigInt(SIGNALISATION, PLAY_TONES);
886
  if (!hasToPlayTone) { return; }
yanmorin's avatar
   
yanmorin committed
887

jpbl's avatar
jpbl committed
888
889
890
891
892
893
  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; 
  }
894
895
896
897
898

  AudioLayer* audiolayer = getAudioDriver();
  if (audiolayer==0) { return; }
  int sampleRate  = audiolayer->getSampleRate();

jpbl's avatar
jpbl committed
899
  _toneMutex.enterMutex(); 
900
  bool loadFile = _audiofile.loadFile(ringchoice, sampleRate);
jpbl's avatar
jpbl committed
901
902
903
904
905
  _toneMutex.leaveMutex(); 
  if (loadFile) {
    _toneMutex.enterMutex(); 
    _audiofile.start();
    _toneMutex.leaveMutex(); 
906
907
    int size = _audiofile.getSize();
    SFLDataFormat output[ size ];
908
    _audiofile.getNext(output, size , 100);
Emmanuel Milou's avatar
Emmanuel Milou committed
909
    //audiolayer->putUrgent( output , size );
jpbl's avatar
jpbl committed
910
911
912
913
914
  } else {
    ringback();
  }
}

915
  AudioLoop*
jpbl's avatar
jpbl committed
916
917
918
919
920
921
922
923
924
925
926
ManagerImpl::getTelephoneTone()
{
  if(_telephoneTone != 0) {
    ost::MutexLock m(_toneMutex);
    return _telephoneTone->getCurrentTone();
  }
  else {
    return 0;
  }
}

927
  AudioLoop*
jpbl's avatar
jpbl committed
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
ManagerImpl::getTelephoneFile()
{
  ost::MutexLock m(_toneMutex);
  if(_audiofile.isStarted()) {
    return &_audiofile;
  } else {
    return 0;
  }
}


/**
 * Use Urgent Buffer
 * By AudioRTP thread
 */
void
yanmorin's avatar
   
yanmorin committed
944
ManagerImpl::notificationIncomingCall(void) {
jpbl's avatar
jpbl committed
945
946
947

  AudioLayer* audiolayer = getAudioDriver();
  if (audiolayer != 0) {
yanmorin's avatar
   
yanmorin committed
948
    unsigned int samplerate = audiolayer->getSampleRate();
jpbl's avatar
jpbl committed
949
950
951
    std::ostringstream frequency;
    frequency << "440/" << FRAME_PER_BUFFER;

952
953
954
    Tone tone(frequency.str(), samplerate);
    unsigned int nbSampling = tone.getSize();
    SFLDataFormat buf[nbSampling];
jpbl's avatar
jpbl committed
955
    tone.getNext(buf, tone.getSize());
956
    audiolayer->playSamples(buf, sizeof(SFLDataFormat)*nbSampling);
jpbl's avatar
jpbl committed
957
958
959
960
961
962
  }
}

/**
 * Multi Thread
 */
963
  bool
yanmorin's avatar
   
yanmorin committed
964
ManagerImpl::getStunInfo (StunAddress4& stunSvrAddr, int port) 
jpbl's avatar
jpbl committed
965
966
967
968
{
  StunAddress4 mappedAddr;
  struct in_addr in;
  char* addr;
yanmorin's avatar
   
yanmorin committed
969