3 @author: Sudheer K. <scifi1947 at gmail.com>
4 @license: GNU General Public License
7 #include "callrouter.h"
8 #include "vicardbusadaptor.h"
9 #include <dbusutility.h>
10 #include <gconfutility.h>
11 #include <databaseutility.h>
12 #include <telepathyutility.h>
15 #include <QDBusConnection>
16 #include <QDBusMessage>
17 #include <QStringListIterator>
19 class CallRouterPrivate
22 CallRouterPrivate(CallRouter * p) :
23 databaseUtility(new DatabaseUtility(p)),
24 dbusAdaptor(new VicarDbusAdaptor(p)),
25 dbusUtility(new DbusUtility(p)),
26 gconfUtility(new GConfUtility(p)),
27 tpUtility(new TelepathyUtility(p)),
30 Q_ASSERT(0 != dbusAdaptor);
31 //Do not open here - Unable to capture changes to DB if it is open too early and closed late.
32 //databaseUtility->openDatabase();
37 qDebug() << "VICaR: Call Router Destructing";
38 //databaseUtility->closeDatabase();
41 DatabaseUtility *databaseUtility;
42 VicarDbusAdaptor * dbusAdaptor;
43 DbusUtility * dbusUtility;
44 GConfUtility * gconfUtility;
45 TelepathyUtility *tpUtility;
46 QString strLastDialedNumber;
47 QString strLastDTMFCode;
48 org::maemo::vicar::Profile *currentProfile;
49 CallRouter * const parent;
52 // ---------------------------------------------------------------------------
54 CallRouter::CallRouter(QObject *parent) :
56 d(new CallRouterPrivate(this))
59 this->registerDBusService();
60 qDebug() << "Vicar-Daemon: Registered DBus Service " << APPLICATION_DBUS_SERVICE;
63 CallRouter::~CallRouter(){
66 void CallRouter::registerDBusService(){
67 //Connect to Session Bus
68 QDBusConnection connection = d->dbusUtility->getConnection(false);
70 if (!connection.interface()->isServiceRegistered(APPLICATION_DBUS_SERVICE)){
72 if (!connection.registerService(APPLICATION_DBUS_SERVICE)) {
73 qDebug() << "Vicar-Daemon: " << d->dbusUtility->getErrorMessage();
78 if (!connection.registerObject(APPLICATION_DBUS_PATH, this, QDBusConnection::ExportAdaptors)) {
79 qDebug() << "Vicar-Daemon: " << d->dbusUtility->getErrorMessage();
86 void CallRouter::unregisterDBusService(){
88 //Disconnect from Session bus
89 QDBusConnection connection = d->dbusUtility->getConnection(false);
91 connection.unregisterObject(APPLICATION_DBUS_PATH,QDBusConnection::UnregisterTree);
93 if (!connection.unregisterService(APPLICATION_DBUS_SERVICE)) {
94 qDebug() << "Vicar-Daemon: " << d->dbusUtility->getErrorMessage();
100 QString CallRouter::callViaCallingCard(QString strDestinationNumber){
102 d->currentProfile = new org::maemo::vicar::Profile();
103 d->currentProfile->profileID = 0;
105 d->databaseUtility->openDatabase();
106 bool result = d->databaseUtility->findProfileByNumber(strDestinationNumber,d->currentProfile);
108 QString strErrorMessage;
110 strErrorMessage = QString("Vicar-Daemon: Error finding VICaR profile. %1").arg(d->databaseUtility->lastError().text());
112 else if (d->currentProfile->profileID == 0){
113 bool routeOnDefault = d->gconfUtility->getGconfValueBoolean("route_on_default");
115 qDebug() << "Vicar-Daemon: Routing directly as per configuration";
116 this->placeCall(strDestinationNumber);
119 qDebug() << "Vicar-Daemon: No profile found. Stopping..";
120 strErrorMessage = "Vicar: No routing profile defined for this number.";
121 d->dbusUtility->displayNotification(strErrorMessage );
125 //Now call the calling card number. This is generally a local and/or tollfree number
126 QString strCallingCardNumber = d->currentProfile->gatewayNumber;
127 qDebug() << "Vicar-Daemon: Initiating call to "<< strCallingCardNumber;
128 bool status = this->placeCall(strCallingCardNumber);
129 d->strLastDialedNumber = strDestinationNumber;
131 QString strUserMessage;
134 qDebug() << "Vicar-Daemon: Call initiated successfully. Connecting DBus slot for audio connection monitor";
135 startCallStatusMonitors();
138 strUserMessage = QString("Unable to initiate new call to ").append(strCallingCardNumber);
139 strErrorMessage = d->dbusUtility->getErrorMessage();
140 qDebug() << "Vicar-Daemon: " << strErrorMessage;
141 d->strLastDialedNumber.clear();
142 delete d->currentProfile;
143 d->currentProfile = 0;
145 d->dbusUtility->displayNotification(strUserMessage);
148 d->databaseUtility->closeDatabase();
149 return strErrorMessage;
152 bool CallRouter::placeCall(QString number){
154 QList<QVariant> argsToSend;
155 argsToSend.append(number);
156 argsToSend.append(0);
158 bool status = d->dbusUtility->sendMethodCall(CSD_SERVICE,
161 QString("CreateWith"),argsToSend);
166 void CallRouter::startCallStatusMonitors(){
167 /* Declare the slot to be executed when a call is picked up by other party (Audio connection established).
168 We need this to confirm whether a call went though successfully.
171 QDBusConnection connection = d->dbusUtility->getConnection();
173 bool success = connection.connect(QString(""),
174 CSD_CALL_INSTANCE_PATH,
175 CSD_CALL_INSTANCE_INTERFACE,
176 QString("AudioConnect"),this,
177 SLOT(sendNumberAsDTMFCode(const QDBusMessage&)));
180 qDebug() << "Vicar-Daemon: Successfully connected to Dbus signal AudioConnect in interface "<< CSD_CALL_INSTANCE_INTERFACE;
183 qDebug() << "Vicar-Daemon: Failed to connect to Dbus signal AudioConnect in interface "<< CSD_CALL_INSTANCE_INTERFACE;
184 qDebug() <<"Vicar-Daemon: DBus Error: "<< d->dbusUtility->getErrorMessage();
188 /* Declare the slot to be executed when the DTMF code is sent.
191 success = connection.connect(QString(""),
192 CSD_CALL_INSTANCE_PATH,
193 CSD_CALL_INSTANCE_INTERFACE,
194 QString("StoppedDTMF"),this,
195 SLOT(displayDTMFConfirmation()));
198 qDebug() << "Vicar-Daemon: Successfully connected to Dbus signal StoppedDTMF in interface "<< CSD_CALL_INSTANCE_INTERFACE;
201 qDebug() << "Vicar-Daemon: Failed to connect to Dbus signal StoppedDTMF in interface "<< CSD_CALL_INSTANCE_INTERFACE;
202 qDebug() <<"Vicar-Daemon: DBus Error: "<< d->dbusUtility->getErrorMessage();
206 /* Declare the slot to be executed when the call is terminated (due to connection errors etc).
207 We need this to avoid sending DTMF code on wrong calls.
210 success = connection.connect(QString(""),
211 CSD_CALL_INSTANCE_PATH,
212 CSD_CALL_INSTANCE_INTERFACE,
213 QString("Terminated"),this,
214 SLOT(stopCallStatusMonitors()));
217 qDebug() << "Vicar-Daemon: Successfully connected to Dbus signal Terminated in interface "<< CSD_CALL_INSTANCE_INTERFACE;
220 qDebug() << "Vicar-Daemon: Failed to connect to Dbus signal Terminated in interface "<< CSD_CALL_INSTANCE_INTERFACE;
221 qDebug() <<"Vicar-Daemon: DBus Error: "<< d->dbusUtility->getErrorMessage();
224 /* Declare the slot to be executed when a call is received
225 (before we can place the call to calling card number).
226 It is extremely rare that somebody should get a call within these few seconds.
227 In any case, we need this to avoid sending DTMF code on the received call.
229 Btw - I don't care for the incoming number here. If anyone is calling the user before we can send DTMF code,
230 then we stop sending the DTMF code even if user does not respond to the call.
233 success = connection.connect(QString(""),
236 QString("Coming"),this,
237 SLOT(stopCallStatusMonitors()));
240 qDebug() << "Vicar-Daemon: Successfully connected to Dbus signal Coming in interface" << CSD_CALL_INTERFACE;
243 qDebug() << "Vicar-Daemon: Failed to connect to Dbus signal Coming in interface" << CSD_CALL_INTERFACE;
244 qDebug() <<"Vicar-Daemon: DBus Error: "<< d->dbusUtility->getErrorMessage();
248 void CallRouter::stopCallStatusMonitors(){
250 d->strLastDTMFCode.clear();
251 d->strLastDialedNumber.clear();
252 delete d->currentProfile;
253 d->currentProfile = 0;
255 QDBusConnection connection = d->dbusUtility->getConnection();
257 // Disconnect the slot for audio connection status
258 bool status = connection.disconnect(QString(""),
259 CSD_CALL_INSTANCE_PATH,
260 CSD_CALL_INSTANCE_INTERFACE,
261 QString("AudioConnect"),this,
262 SLOT(sendNumberAsDTMFCode(const QDBusMessage&)));
265 qDebug() << "Vicar-Daemon: Successfully disconnected from Dbus signal AudioConnect in interface "<< CSD_CALL_INSTANCE_INTERFACE;
268 qDebug() << "Vicar-Daemon: Failed to disconnect from Dbus signal AudioConnect in interface "<< CSD_CALL_INSTANCE_INTERFACE;
269 qDebug() <<"Vicar-Daemon: DBus Error: "<< d->dbusUtility->getErrorMessage();
272 // Disconnect the slot for monitoring DTMF completion
273 status = connection.disconnect(QString(""),
274 CSD_CALL_INSTANCE_PATH,
275 CSD_CALL_INSTANCE_INTERFACE,
276 QString("StoppedDTMF"),this,
277 SLOT(displayDTMFConfirmation()));
280 qDebug() << "Vicar-Daemon: Successfully disconnected from Dbus signal StoppedDTMF in interface "<< CSD_CALL_INSTANCE_INTERFACE;
283 qDebug() << "Vicar-Daemon: Failed to disconnect from Dbus signal StoppedDTMF in interface "<< CSD_CALL_INSTANCE_INTERFACE;
284 qDebug() <<"Vicar-Daemon: DBus Error: "<< d->dbusUtility->getErrorMessage();
288 // Disconnect the slot for monitoring terminated calls
289 status = connection.disconnect(QString(""),
290 CSD_CALL_INSTANCE_PATH,
291 CSD_CALL_INSTANCE_INTERFACE,
292 QString("Terminated"),this,
293 SLOT(stopCallStatusMonitors()));
296 qDebug() << "Vicar-Daemon: Successfully disconnected from Dbus signal Terminated in interface "<< CSD_CALL_INSTANCE_INTERFACE;
299 qDebug() << "Vicar-Daemon: Failed to disconnect from Dbus signal Terminated in interface "<< CSD_CALL_INSTANCE_INTERFACE;
300 qDebug() <<"Vicar-Daemon: DBus Error: "<< d->dbusUtility->getErrorMessage();
303 // Disconnect the slot for monitoring incoming calls
304 status = connection.disconnect(QString(""),
307 QString("Coming"),this,
308 SLOT(stopCallStatusMonitors()));
311 qDebug() << "Vicar-Daemon: Successfully disconnected from Dbus signal Coming in interface" << CSD_CALL_INTERFACE;
314 qDebug() << "Vicar-Daemon: Failed to disconnect from Dbus signal Coming in interface" << CSD_CALL_INTERFACE;
315 qDebug() <<"Vicar-Daemon: DBus Error: "<< d->dbusUtility->getErrorMessage();
319 void CallRouter::sendNumberAsDTMFCode(const QDBusMessage& dbusMessage){
321 if (!d->strLastDialedNumber.isEmpty() && d->currentProfile != 0){
322 //Verify whether we have the last dialed number available
324 QList<QVariant> listArguments = dbusMessage.arguments();
325 bool audioConnected = listArguments.first().toBool();
328 // Now that the call to Calling card number is successful. We can send the original number as DTMF tones
329 QString strDTMFCode = convertToDTMFCode(d->strLastDialedNumber);
331 qDebug() << "Vicar-Daemon: Audio connection established. Sending DTMF code "<< strDTMFCode;
334 QList<QVariant> argsToSend;
335 argsToSend.append(strDTMFCode);
336 bool status = d->dbusUtility->sendMethodCall(CSD_SERVICE,
339 QString("SendDTMF"),argsToSend);
342 qDebug() << "Vicar-Daemon: Sending " << strDTMFCode << " as DTMF code.";
343 d->strLastDTMFCode = strDTMFCode;
346 qDebug() << "Vicar-Daemon: Unable to send DTMF code.";
350 qDebug() << "Vicar-Daemon: Audio not yet connected.";
355 qDebug() << "Vicar-Daemon: Last dialed number is empty.";
359 void CallRouter::displayDTMFConfirmation(){
360 //This slot is called when the all the DTMF tones are sent (i.e StoppedDTMF signal is emitted)
361 //Just display confirmation message and cleanup
364 if (!d->strLastDTMFCode.isEmpty()){
365 QString strMessage = d->strLastDTMFCode.append(" sent as DTMF code");
366 d->dbusUtility->displayNotification(strMessage);
367 qDebug() << "Vicar-Daemon: "<< d->strLastDTMFCode << " sent as DTMF code.";
371 Connecting and Disconnecting from/to DBus signal for each international call
372 may not be the most efficient way of handling this. But we need to make sure
373 that the DTMF codes are sent only for the calls placed by this app (i.e calls to Calling card number).
376 qDebug() << "Vicar-Daemon: Now disconnecting from call status monitors..";
377 stopCallStatusMonitors();
380 QString CallRouter::convertToDTMFCode(QString strNumber){
383 if (!strNumber.isEmpty()){
384 //int intDTMFDelay = 1;
386 //Add the prefix p so that there is some delay after the call is picked up by the automated system to send DTMF tones.
387 //strDTMFCode = QString("").fill('p',intDTMFDelay);
390 //Now check whether we need a prefix
391 QString strDTMFPrefix = d->currentProfile->dtmfPrefix;
393 if (!strDTMFPrefix.isEmpty()){
394 strDTMFCode = strDTMFCode.append(strDTMFPrefix);
397 //Get the format required by calling card from coniguration
398 QString qstrDTMFFormat = d->currentProfile->dtmfFormat;
399 if (qstrDTMFFormat.isEmpty()) qstrDTMFFormat = "<Country Code><Area Code><Phone Number>";
401 /* Replace 00 (international dialing code) at the beginning
402 and also replace any character other than the numbers 0-9 and p.
404 QRegExp regexp = QRegExp("(^0{2})|[^0-9p]");
405 strNumber = strNumber.replace(regexp,"");
407 /* Now we have a clean number with only country code, area code and phone number,
408 lets convert it to the calling card friendly format
410 if (qstrDTMFFormat.startsWith("+")){
411 strDTMFCode = strDTMFCode.append("+");
413 else if (qstrDTMFFormat.startsWith("00")){
414 strDTMFCode = strDTMFCode.append("00");
416 else if (qstrDTMFFormat.startsWith("011")){
417 strDTMFCode = strDTMFCode.append("011");
420 strDTMFCode = strDTMFCode.append(strNumber);
422 //Now check whether we need a suffix
423 QString strDTMFSuffix = d->currentProfile->dtmfSuffix;
424 if (!strDTMFSuffix.isEmpty()){
425 strDTMFCode = strDTMFCode.append(strDTMFSuffix);
432 //DBus Method used by external applications to check whether VICaR is enabled and running
433 bool CallRouter::isRunning(){
437 //Verify Whether VICaR telepathy account is online
439 if (d->tpUtility->getAccountStatus() == "Connected"){
448 //DBus Method used by external applications to call via VICaR
449 QString CallRouter::callInternationalNumber(const QString& strDestinationNumber){
451 QString strErrorMessage;
453 qDebug() << "Vicar-Daemon: New call requested by external application. Destination number is " << strDestinationNumber;
455 if (isValidPhoneNumber(strDestinationNumber)){
457 //Remove spaces in the phone number before using
458 QString numberWithoutSpaces = QString(strDestinationNumber).remove(" ");
460 strErrorMessage = this->callViaCallingCard(numberWithoutSpaces);
463 strErrorMessage = QString("Vicar-Daemon: %1 is not a valid number").arg(strDestinationNumber);
464 if (strDestinationNumber != "publish" && strDestinationNumber != "subscribe"){
465 d->dbusUtility->displayNotification(QString("Vicar: %1 is not a valid number").arg(strDestinationNumber));
469 qDebug() << strErrorMessage;
471 if (strErrorMessage.isEmpty()){
472 return QString("Success");
475 return strErrorMessage;
479 //Check whether a string is valid phone number
480 bool CallRouter::isValidPhoneNumber(QString strPhoneNumber){
482 /* Remove all dialble characters and space. The resulting string should be a valid number */
483 QRegExp regexp = QRegExp("[p+*# ]");
485 strPhoneNumber = strPhoneNumber.replace(regexp,"");
487 qDebug() << "Vicar Daemon: Cleaned up phone number is " << strPhoneNumber;
489 /* Now remove all digits, the resulting string should be empty, then it is a valid number */
490 regexp = QRegExp("[0-9]");
492 strPhoneNumber = strPhoneNumber.replace(regexp,"");
494 bool isNumber = strPhoneNumber.isEmpty();