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 <databaseutility.h>
11 #include <telepathyutility.h>
14 #include <QDBusConnection>
15 #include <QDBusMessage>
16 #include <QStringListIterator>
18 class CallRouterPrivate
21 CallRouterPrivate(CallRouter * p) :
22 databaseUtility(new DatabaseUtility(p)),
23 dbusAdaptor(new VicarDbusAdaptor(p)),
24 dbusUtility(new DbusUtility(p)),
25 tpUtility(new TelepathyUtility(p)),
28 Q_ASSERT(0 != dbusAdaptor);
29 //Do not open here - Unable to capture changes to DB if it is open too early and closed late.
30 //databaseUtility->openDatabase();
31 qDebug () << "In Private Constructor";
36 qDebug() << "VICaR: Call Router Destructing";
37 //databaseUtility->closeDatabase();
40 DatabaseUtility *databaseUtility;
41 VicarDbusAdaptor * dbusAdaptor;
42 DbusUtility * dbusUtility;
43 TelepathyUtility *tpUtility;
44 QString strLastDialedNumber;
45 QString strLastDTMFCode;
46 org::maemo::vicar::Profile *currentProfile;
47 CallRouter * const parent;
50 // ---------------------------------------------------------------------------
52 CallRouter::CallRouter(QObject *parent) :
54 d(new CallRouterPrivate(this))
57 this->registerDBusService();
58 qDebug() << "Vicar-Daemon: Registered DBus Service " << APPLICATION_DBUS_SERVICE;
61 CallRouter::~CallRouter(){
64 void CallRouter::registerDBusService(){
65 //Connect to Session Bus
66 QDBusConnection connection = d->dbusUtility->getConnection(false);
68 if (!connection.interface()->isServiceRegistered(APPLICATION_DBUS_SERVICE)){
70 if (!connection.registerService(APPLICATION_DBUS_SERVICE)) {
71 qDebug() << "Vicar-Daemon: " << d->dbusUtility->getErrorMessage();
76 if (!connection.registerObject(APPLICATION_DBUS_PATH, this, QDBusConnection::ExportAdaptors)) {
77 qDebug() << "Vicar-Daemon: " << d->dbusUtility->getErrorMessage();
84 void CallRouter::unregisterDBusService(){
86 //Disconnect from Session bus
87 QDBusConnection connection = d->dbusUtility->getConnection(false);
89 connection.unregisterObject(APPLICATION_DBUS_PATH,QDBusConnection::UnregisterTree);
91 if (!connection.unregisterService(APPLICATION_DBUS_SERVICE)) {
92 qDebug() << "Vicar-Daemon: " << d->dbusUtility->getErrorMessage();
98 QString CallRouter::callViaCallingCard(QString strDestinationNumber){
100 d->currentProfile = new org::maemo::vicar::Profile();
101 d->currentProfile->profileID = 0;
103 d->databaseUtility->openDatabase();
104 bool result = d->databaseUtility->findProfileByNumber(strDestinationNumber,d->currentProfile);
106 QString strErrorMessage;
108 strErrorMessage = QString("Vicar-Daemon: Error finding VICaR profile. %1").arg(d->databaseUtility->lastError().text());
110 else if (d->currentProfile->profileID == 0){
111 QString routeOnDefaultSetting = d->databaseUtility->getSetting("route_on_default");
112 //bool routeOnDefault = d->gconfUtility->getGconfValueBoolean("route_on_default");
113 bool routeOnDefault = routeOnDefaultSetting == "1"? true:false;
116 qDebug() << "Vicar-Daemon: Routing directly as per configuration";
117 this->placeCall(strDestinationNumber);
120 qDebug() << "Vicar-Daemon: No profile found. Stopping..";
121 strErrorMessage = "Vicar: No routing profile defined for this number.";
122 d->dbusUtility->displayNotification(strErrorMessage );
126 //Now call the calling card number. This is generally a local and/or tollfree number
127 QString strCallingCardNumber = d->currentProfile->gatewayNumber;
128 qDebug() << "Vicar-Daemon: Initiating call to "<< strCallingCardNumber;
129 bool status = this->placeCall(strCallingCardNumber);
130 d->strLastDialedNumber = strDestinationNumber;
132 QString strUserMessage;
135 qDebug() << "Vicar-Daemon: Call initiated successfully. Connecting DBus slot for audio connection monitor";
136 startCallStatusMonitors();
139 strUserMessage = QString("Unable to initiate new call to ").append(strCallingCardNumber);
140 strErrorMessage = d->dbusUtility->getErrorMessage();
141 qDebug() << "Vicar-Daemon: " << strErrorMessage;
142 d->strLastDialedNumber.clear();
143 delete d->currentProfile;
144 d->currentProfile = 0;
146 d->dbusUtility->displayNotification(strUserMessage);
149 d->databaseUtility->closeDatabase();
150 return strErrorMessage;
153 bool CallRouter::placeCall(QString number){
155 QList<QVariant> argsToSend;
156 argsToSend.append(number);
157 argsToSend.append(0);
159 bool status = d->dbusUtility->sendMethodCall(CSD_SERVICE,
162 QString("CreateWith"),argsToSend);
167 void CallRouter::startCallStatusMonitors(){
168 /* Declare the slot to be executed when a call is picked up by other party (Audio connection established).
169 We need this to confirm whether a call went though successfully.
172 QDBusConnection connection = d->dbusUtility->getConnection();
174 bool success = connection.connect(QString(""),
175 CSD_CALL_INSTANCE_PATH,
176 CSD_CALL_INSTANCE_INTERFACE,
177 QString("AudioConnect"),this,
178 SLOT(sendNumberAsDTMFCode(const QDBusMessage&)));
181 qDebug() << "Vicar-Daemon: Successfully connected to Dbus signal AudioConnect in interface "<< CSD_CALL_INSTANCE_INTERFACE;
184 qDebug() << "Vicar-Daemon: Failed to connect to Dbus signal AudioConnect in interface "<< CSD_CALL_INSTANCE_INTERFACE;
185 qDebug() <<"Vicar-Daemon: DBus Error: "<< d->dbusUtility->getErrorMessage();
189 /* Declare the slot to be executed when the DTMF code is sent.
192 success = connection.connect(QString(""),
193 CSD_CALL_INSTANCE_PATH,
194 CSD_CALL_INSTANCE_INTERFACE,
195 QString("StoppedDTMF"),this,
196 SLOT(displayDTMFConfirmation()));
199 qDebug() << "Vicar-Daemon: Successfully connected to Dbus signal StoppedDTMF in interface "<< CSD_CALL_INSTANCE_INTERFACE;
202 qDebug() << "Vicar-Daemon: Failed to connect to Dbus signal StoppedDTMF in interface "<< CSD_CALL_INSTANCE_INTERFACE;
203 qDebug() <<"Vicar-Daemon: DBus Error: "<< d->dbusUtility->getErrorMessage();
207 /* Declare the slot to be executed when the call is terminated (due to connection errors etc).
208 We need this to avoid sending DTMF code on wrong calls.
211 success = connection.connect(QString(""),
212 CSD_CALL_INSTANCE_PATH,
213 CSD_CALL_INSTANCE_INTERFACE,
214 QString("Terminated"),this,
215 SLOT(stopCallStatusMonitors()));
218 qDebug() << "Vicar-Daemon: Successfully connected to Dbus signal Terminated in interface "<< CSD_CALL_INSTANCE_INTERFACE;
221 qDebug() << "Vicar-Daemon: Failed to connect to Dbus signal Terminated in interface "<< CSD_CALL_INSTANCE_INTERFACE;
222 qDebug() <<"Vicar-Daemon: DBus Error: "<< d->dbusUtility->getErrorMessage();
225 /* Declare the slot to be executed when a call is received
226 (before we can place the call to calling card number).
227 It is extremely rare that somebody should get a call within these few seconds.
228 In any case, we need this to avoid sending DTMF code on the received call.
230 Btw - I don't care for the incoming number here. If anyone is calling the user before we can send DTMF code,
231 then we stop sending the DTMF code even if user does not respond to the call.
234 success = connection.connect(QString(""),
237 QString("Coming"),this,
238 SLOT(stopCallStatusMonitors()));
241 qDebug() << "Vicar-Daemon: Successfully connected to Dbus signal Coming in interface" << CSD_CALL_INTERFACE;
244 qDebug() << "Vicar-Daemon: Failed to connect to Dbus signal Coming in interface" << CSD_CALL_INTERFACE;
245 qDebug() <<"Vicar-Daemon: DBus Error: "<< d->dbusUtility->getErrorMessage();
249 void CallRouter::stopCallStatusMonitors(){
251 d->strLastDTMFCode.clear();
252 d->strLastDialedNumber.clear();
253 delete d->currentProfile;
254 d->currentProfile = 0;
256 QDBusConnection connection = d->dbusUtility->getConnection();
258 // Disconnect the slot for audio connection status
259 bool status = connection.disconnect(QString(""),
260 CSD_CALL_INSTANCE_PATH,
261 CSD_CALL_INSTANCE_INTERFACE,
262 QString("AudioConnect"),this,
263 SLOT(sendNumberAsDTMFCode(const QDBusMessage&)));
266 qDebug() << "Vicar-Daemon: Successfully disconnected from Dbus signal AudioConnect in interface "<< CSD_CALL_INSTANCE_INTERFACE;
269 qDebug() << "Vicar-Daemon: Failed to disconnect from Dbus signal AudioConnect in interface "<< CSD_CALL_INSTANCE_INTERFACE;
270 qDebug() <<"Vicar-Daemon: DBus Error: "<< d->dbusUtility->getErrorMessage();
273 // Disconnect the slot for monitoring DTMF completion
274 status = connection.disconnect(QString(""),
275 CSD_CALL_INSTANCE_PATH,
276 CSD_CALL_INSTANCE_INTERFACE,
277 QString("StoppedDTMF"),this,
278 SLOT(displayDTMFConfirmation()));
281 qDebug() << "Vicar-Daemon: Successfully disconnected from Dbus signal StoppedDTMF in interface "<< CSD_CALL_INSTANCE_INTERFACE;
284 qDebug() << "Vicar-Daemon: Failed to disconnect from Dbus signal StoppedDTMF in interface "<< CSD_CALL_INSTANCE_INTERFACE;
285 qDebug() <<"Vicar-Daemon: DBus Error: "<< d->dbusUtility->getErrorMessage();
289 // Disconnect the slot for monitoring terminated calls
290 status = connection.disconnect(QString(""),
291 CSD_CALL_INSTANCE_PATH,
292 CSD_CALL_INSTANCE_INTERFACE,
293 QString("Terminated"),this,
294 SLOT(stopCallStatusMonitors()));
297 qDebug() << "Vicar-Daemon: Successfully disconnected from Dbus signal Terminated in interface "<< CSD_CALL_INSTANCE_INTERFACE;
300 qDebug() << "Vicar-Daemon: Failed to disconnect from Dbus signal Terminated in interface "<< CSD_CALL_INSTANCE_INTERFACE;
301 qDebug() <<"Vicar-Daemon: DBus Error: "<< d->dbusUtility->getErrorMessage();
304 // Disconnect the slot for monitoring incoming calls
305 status = connection.disconnect(QString(""),
308 QString("Coming"),this,
309 SLOT(stopCallStatusMonitors()));
312 qDebug() << "Vicar-Daemon: Successfully disconnected from Dbus signal Coming in interface" << CSD_CALL_INTERFACE;
315 qDebug() << "Vicar-Daemon: Failed to disconnect from Dbus signal Coming in interface" << CSD_CALL_INTERFACE;
316 qDebug() <<"Vicar-Daemon: DBus Error: "<< d->dbusUtility->getErrorMessage();
320 void CallRouter::sendNumberAsDTMFCode(const QDBusMessage& dbusMessage){
322 if (!d->strLastDialedNumber.isEmpty() && d->currentProfile != 0){
323 //Verify whether we have the last dialed number available
325 QList<QVariant> listArguments = dbusMessage.arguments();
326 bool audioConnected = listArguments.first().toBool();
329 // Now that the call to Calling card number is successful. We can send the original number as DTMF tones
330 QString strDTMFCode = convertToDTMFCode(d->strLastDialedNumber);
332 qDebug() << "Vicar-Daemon: Audio connection established. Sending DTMF code "<< strDTMFCode;
335 QList<QVariant> argsToSend;
336 argsToSend.append(strDTMFCode);
337 bool status = d->dbusUtility->sendMethodCall(CSD_SERVICE,
340 QString("SendDTMF"),argsToSend);
343 qDebug() << "Vicar-Daemon: Sending " << strDTMFCode << " as DTMF code.";
344 d->strLastDTMFCode = strDTMFCode;
347 qDebug() << "Vicar-Daemon: Unable to send DTMF code.";
351 qDebug() << "Vicar-Daemon: Audio not yet connected.";
356 qDebug() << "Vicar-Daemon: Last dialed number is empty.";
360 void CallRouter::displayDTMFConfirmation(){
361 //This slot is called when the all the DTMF tones are sent (i.e StoppedDTMF signal is emitted)
362 //Just display confirmation message and cleanup
365 if (!d->strLastDTMFCode.isEmpty()){
366 QString strMessage = d->strLastDTMFCode.append(" sent as DTMF code");
367 d->dbusUtility->displayNotification(strMessage);
368 qDebug() << "Vicar-Daemon: "<< d->strLastDTMFCode << " sent as DTMF code.";
372 Connecting and Disconnecting from/to DBus signal for each international call
373 may not be the most efficient way of handling this. But we need to make sure
374 that the DTMF codes are sent only for the calls placed by this app (i.e calls to Calling card number).
377 qDebug() << "Vicar-Daemon: Now disconnecting from call status monitors..";
378 stopCallStatusMonitors();
381 QString CallRouter::convertToDTMFCode(QString strNumber){
384 if (!strNumber.isEmpty()){
385 //int intDTMFDelay = 1;
387 //Add the prefix p so that there is some delay after the call is picked up by the automated system to send DTMF tones.
388 //strDTMFCode = QString("").fill('p',intDTMFDelay);
391 //Now check whether we need a prefix
392 QString strDTMFPrefix = d->currentProfile->dtmfPrefix;
394 if (!strDTMFPrefix.isEmpty()){
395 strDTMFCode = strDTMFCode.append(strDTMFPrefix);
398 //Get the format required by calling card from coniguration
399 QString qstrDTMFFormat = d->currentProfile->dtmfFormat;
400 if (qstrDTMFFormat.isEmpty()) qstrDTMFFormat = "<Country Code><Area Code><Phone Number>";
402 /* Replace 00 (international dialing code) at the beginning
403 and also replace any character other than the numbers 0-9 and p.
405 QRegExp regexp = QRegExp("(^0{2})|[^0-9p]");
406 strNumber = strNumber.replace(regexp,"");
408 /* Now we have a clean number with only country code, area code and phone number,
409 lets convert it to the calling card friendly format
411 if (qstrDTMFFormat.startsWith("+")){
412 strDTMFCode = strDTMFCode.append("+");
414 else if (qstrDTMFFormat.startsWith("00")){
415 strDTMFCode = strDTMFCode.append("00");
417 else if (qstrDTMFFormat.startsWith("011")){
418 strDTMFCode = strDTMFCode.append("011");
421 strDTMFCode = strDTMFCode.append(strNumber);
423 //Now check whether we need a suffix
424 QString strDTMFSuffix = d->currentProfile->dtmfSuffix;
425 if (!strDTMFSuffix.isEmpty()){
426 strDTMFCode = strDTMFCode.append(strDTMFSuffix);
433 //DBus Method used by external applications to check whether VICaR is enabled and running
434 bool CallRouter::isRunning(){
438 //Verify Whether VICaR telepathy account is online
440 if (d->tpUtility->getAccountStatus() == "Connected"){
449 //DBus Method used by external applications to call via VICaR
450 QString CallRouter::callInternationalNumber(const QString& strDestinationNumber){
452 QString strErrorMessage;
454 qDebug() << "Vicar-Daemon: New call requested by external application. Destination number is " << strDestinationNumber;
456 if (isValidPhoneNumber(strDestinationNumber)){
458 //Remove spaces in the phone number before using
459 QString numberWithoutSpaces = QString(strDestinationNumber).remove(" ");
461 strErrorMessage = this->callViaCallingCard(numberWithoutSpaces);
464 strErrorMessage = QString("Vicar-Daemon: %1 is not a valid number").arg(strDestinationNumber);
465 if (strDestinationNumber != "publish" && strDestinationNumber != "subscribe"){
466 d->dbusUtility->displayNotification(QString("Vicar: %1 is not a valid number").arg(strDestinationNumber));
470 qDebug() << strErrorMessage;
472 if (strErrorMessage.isEmpty()){
473 return QString("Success");
476 return strErrorMessage;
480 //Check whether a string is valid phone number
481 bool CallRouter::isValidPhoneNumber(QString strPhoneNumber){
483 /* Remove all dialble characters and space. The resulting string should be a valid number */
484 QRegExp regexp = QRegExp("[p+*# ]");
486 strPhoneNumber = strPhoneNumber.replace(regexp,"");
488 qDebug() << "Vicar Daemon: Cleaned up phone number is " << strPhoneNumber;
490 /* Now remove all digits, the resulting string should be empty, then it is a valid number */
491 regexp = QRegExp("[0-9]");
493 strPhoneNumber = strPhoneNumber.replace(regexp,"");
495 bool isNumber = strPhoneNumber.isEmpty();