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();
60 CallRouter::~CallRouter(){
63 void CallRouter::registerDBusService(){
64 //Connect to Session Bus
65 QDBusConnection connection = d->dbusUtility->getConnection(false);
67 if (!connection.interface()->isServiceRegistered(APPLICATION_DBUS_SERVICE)){
69 if (!connection.registerService(APPLICATION_DBUS_SERVICE)) {
70 qDebug() << "Vicar-Daemon: Error registering D-Bus service " << d->dbusUtility->getErrorMessage();
71 qDebug() << "Vicar-Daemon: Exiting";
75 qDebug() << "Vicar-Daemon: Registered DBus Service " << APPLICATION_DBUS_SERVICE;
79 if (!connection.registerObject(APPLICATION_DBUS_PATH, this, QDBusConnection::ExportAdaptors)) {
80 qDebug() << "Vicar-Daemon: " << d->dbusUtility->getErrorMessage();
81 qDebug() << "Vicar-Daemon: Exiting";
85 qDebug() << "Vicar-Daemon: Registered DBus Object " << APPLICATION_DBUS_PATH;
90 void CallRouter::unregisterDBusService(){
92 //Disconnect from Session bus
93 QDBusConnection connection = d->dbusUtility->getConnection(false);
95 connection.unregisterObject(APPLICATION_DBUS_PATH,QDBusConnection::UnregisterTree);
97 if (!connection.unregisterService(APPLICATION_DBUS_SERVICE)) {
98 qDebug() << "Vicar-Daemon: " << d->dbusUtility->getErrorMessage();
104 QString CallRouter::callViaCallingCard(QString strDestinationNumber){
106 d->currentProfile = new org::maemo::vicar::Profile();
107 d->currentProfile->profileID = 0;
109 d->databaseUtility->openDatabase();
110 bool result = d->databaseUtility->findProfileByNumber(strDestinationNumber,d->currentProfile);
112 QString strErrorMessage;
114 strErrorMessage = QString("Vicar-Daemon: Error finding VICaR profile. %1").arg(d->databaseUtility->lastError().text());
116 else if (d->currentProfile->profileID == 0){
117 QString routeOnDefaultSetting = d->databaseUtility->getSetting("route_on_default");
118 //bool routeOnDefault = d->gconfUtility->getGconfValueBoolean("route_on_default");
119 bool routeOnDefault = routeOnDefaultSetting == "1"? true:false;
122 qDebug() << "Vicar-Daemon: Routing directly as per configuration";
123 this->placeCall(strDestinationNumber);
126 qDebug() << "Vicar-Daemon: No profile found. Stopping..";
127 strErrorMessage = "Vicar: No routing profile defined for this number.";
128 d->dbusUtility->displayNotification(strErrorMessage );
132 //Now call the calling card number. This is generally a local and/or tollfree number
133 QString strCallingCardNumber = d->currentProfile->gatewayNumber;
134 qDebug() << "Vicar-Daemon: Initiating call to "<< strCallingCardNumber;
135 bool status = this->placeCall(strCallingCardNumber);
136 d->strLastDialedNumber = strDestinationNumber;
138 QString strUserMessage;
141 qDebug() << "Vicar-Daemon: Call initiated successfully. Connecting DBus slot for audio connection monitor";
142 startCallStatusMonitors();
145 strUserMessage = QString("Unable to initiate new call to ").append(strCallingCardNumber);
146 strErrorMessage = d->dbusUtility->getErrorMessage();
147 qDebug() << "Vicar-Daemon: " << strErrorMessage;
148 d->strLastDialedNumber.clear();
149 delete d->currentProfile;
150 d->currentProfile = 0;
152 d->dbusUtility->displayNotification(strUserMessage);
155 d->databaseUtility->closeDatabase();
156 return strErrorMessage;
159 bool CallRouter::placeCall(QString number){
161 QList<QVariant> argsToSend;
162 argsToSend.append(number);
163 argsToSend.append(0);
165 bool status = d->dbusUtility->sendMethodCall(CSD_SERVICE,
168 QString("CreateWith"),argsToSend);
173 void CallRouter::startCallStatusMonitors(){
174 /* Declare the slot to be executed when a call is picked up by other party (Audio connection established).
175 We need this to confirm whether a call went though successfully.
178 QDBusConnection connection = d->dbusUtility->getConnection();
180 bool success = connection.connect(QString(""),
181 CSD_CALL_INSTANCE_PATH,
182 CSD_CALL_INSTANCE_INTERFACE,
183 QString("AudioConnect"),this,
184 SLOT(sendNumberAsDTMFCode(const QDBusMessage&)));
187 qDebug() << "Vicar-Daemon: Successfully connected to Dbus signal AudioConnect in interface "<< CSD_CALL_INSTANCE_INTERFACE;
190 qDebug() << "Vicar-Daemon: Failed to connect to Dbus signal AudioConnect in interface "<< CSD_CALL_INSTANCE_INTERFACE;
191 qDebug() <<"Vicar-Daemon: DBus Error: "<< d->dbusUtility->getErrorMessage();
195 /* Declare the slot to be executed when the DTMF code is sent.
198 success = connection.connect(QString(""),
199 CSD_CALL_INSTANCE_PATH,
200 CSD_CALL_INSTANCE_INTERFACE,
201 QString("StoppedDTMF"),this,
202 SLOT(displayDTMFConfirmation()));
205 qDebug() << "Vicar-Daemon: Successfully connected to Dbus signal StoppedDTMF in interface "<< CSD_CALL_INSTANCE_INTERFACE;
208 qDebug() << "Vicar-Daemon: Failed to connect to Dbus signal StoppedDTMF in interface "<< CSD_CALL_INSTANCE_INTERFACE;
209 qDebug() <<"Vicar-Daemon: DBus Error: "<< d->dbusUtility->getErrorMessage();
213 /* Declare the slot to be executed when the call is terminated (due to connection errors etc).
214 We need this to avoid sending DTMF code on wrong calls.
217 success = connection.connect(QString(""),
218 CSD_CALL_INSTANCE_PATH,
219 CSD_CALL_INSTANCE_INTERFACE,
220 QString("Terminated"),this,
221 SLOT(stopCallStatusMonitors()));
224 qDebug() << "Vicar-Daemon: Successfully connected to Dbus signal Terminated in interface "<< CSD_CALL_INSTANCE_INTERFACE;
227 qDebug() << "Vicar-Daemon: Failed to connect to Dbus signal Terminated in interface "<< CSD_CALL_INSTANCE_INTERFACE;
228 qDebug() <<"Vicar-Daemon: DBus Error: "<< d->dbusUtility->getErrorMessage();
231 /* Declare the slot to be executed when a call is received
232 (before we can place the call to calling card number).
233 It is extremely rare that somebody should get a call within these few seconds.
234 In any case, we need this to avoid sending DTMF code on the received call.
236 Btw - I don't care for the incoming number here. If anyone is calling the user before we can send DTMF code,
237 then we stop sending the DTMF code even if user does not respond to the call.
240 success = connection.connect(QString(""),
243 QString("Coming"),this,
244 SLOT(stopCallStatusMonitors()));
247 qDebug() << "Vicar-Daemon: Successfully connected to Dbus signal Coming in interface" << CSD_CALL_INTERFACE;
250 qDebug() << "Vicar-Daemon: Failed to connect to Dbus signal Coming in interface" << CSD_CALL_INTERFACE;
251 qDebug() <<"Vicar-Daemon: DBus Error: "<< d->dbusUtility->getErrorMessage();
255 void CallRouter::stopCallStatusMonitors(){
257 d->strLastDTMFCode.clear();
258 d->strLastDialedNumber.clear();
259 delete d->currentProfile;
260 d->currentProfile = 0;
262 QDBusConnection connection = d->dbusUtility->getConnection();
264 // Disconnect the slot for audio connection status
265 bool status = connection.disconnect(QString(""),
266 CSD_CALL_INSTANCE_PATH,
267 CSD_CALL_INSTANCE_INTERFACE,
268 QString("AudioConnect"),this,
269 SLOT(sendNumberAsDTMFCode(const QDBusMessage&)));
272 qDebug() << "Vicar-Daemon: Successfully disconnected from Dbus signal AudioConnect in interface "<< CSD_CALL_INSTANCE_INTERFACE;
275 qDebug() << "Vicar-Daemon: Failed to disconnect from Dbus signal AudioConnect in interface "<< CSD_CALL_INSTANCE_INTERFACE;
276 qDebug() <<"Vicar-Daemon: DBus Error: "<< d->dbusUtility->getErrorMessage();
279 // Disconnect the slot for monitoring DTMF completion
280 status = connection.disconnect(QString(""),
281 CSD_CALL_INSTANCE_PATH,
282 CSD_CALL_INSTANCE_INTERFACE,
283 QString("StoppedDTMF"),this,
284 SLOT(displayDTMFConfirmation()));
287 qDebug() << "Vicar-Daemon: Successfully disconnected from Dbus signal StoppedDTMF in interface "<< CSD_CALL_INSTANCE_INTERFACE;
290 qDebug() << "Vicar-Daemon: Failed to disconnect from Dbus signal StoppedDTMF in interface "<< CSD_CALL_INSTANCE_INTERFACE;
291 qDebug() <<"Vicar-Daemon: DBus Error: "<< d->dbusUtility->getErrorMessage();
295 // Disconnect the slot for monitoring terminated calls
296 status = connection.disconnect(QString(""),
297 CSD_CALL_INSTANCE_PATH,
298 CSD_CALL_INSTANCE_INTERFACE,
299 QString("Terminated"),this,
300 SLOT(stopCallStatusMonitors()));
303 qDebug() << "Vicar-Daemon: Successfully disconnected from Dbus signal Terminated in interface "<< CSD_CALL_INSTANCE_INTERFACE;
306 qDebug() << "Vicar-Daemon: Failed to disconnect from Dbus signal Terminated in interface "<< CSD_CALL_INSTANCE_INTERFACE;
307 qDebug() <<"Vicar-Daemon: DBus Error: "<< d->dbusUtility->getErrorMessage();
310 // Disconnect the slot for monitoring incoming calls
311 status = connection.disconnect(QString(""),
314 QString("Coming"),this,
315 SLOT(stopCallStatusMonitors()));
318 qDebug() << "Vicar-Daemon: Successfully disconnected from Dbus signal Coming in interface" << CSD_CALL_INTERFACE;
321 qDebug() << "Vicar-Daemon: Failed to disconnect from Dbus signal Coming in interface" << CSD_CALL_INTERFACE;
322 qDebug() <<"Vicar-Daemon: DBus Error: "<< d->dbusUtility->getErrorMessage();
326 void CallRouter::sendNumberAsDTMFCode(const QDBusMessage& dbusMessage){
328 if (!d->strLastDialedNumber.isEmpty() && d->currentProfile != 0){
329 //Verify whether we have the last dialed number available
331 QList<QVariant> listArguments = dbusMessage.arguments();
332 bool audioConnected = listArguments.first().toBool();
335 // Now that the call to Calling card number is successful. We can send the original number as DTMF tones
336 QString strDTMFCode = convertToDTMFCode(d->strLastDialedNumber);
338 qDebug() << "Vicar-Daemon: Audio connection established. Sending DTMF code "<< strDTMFCode;
341 QList<QVariant> argsToSend;
342 argsToSend.append(strDTMFCode);
343 bool status = d->dbusUtility->sendMethodCall(CSD_SERVICE,
346 QString("SendDTMF"),argsToSend);
349 qDebug() << "Vicar-Daemon: Sending " << strDTMFCode << " as DTMF code.";
350 d->strLastDTMFCode = strDTMFCode;
353 qDebug() << "Vicar-Daemon: Unable to send DTMF code.";
357 qDebug() << "Vicar-Daemon: Audio not yet connected.";
362 qDebug() << "Vicar-Daemon: Last dialed number is empty.";
366 void CallRouter::displayDTMFConfirmation(){
367 //This slot is called when the all the DTMF tones are sent (i.e StoppedDTMF signal is emitted)
368 //Just display confirmation message and cleanup
371 if (!d->strLastDTMFCode.isEmpty()){
372 QString strMessage = d->strLastDTMFCode.append(" sent as DTMF code");
373 d->dbusUtility->displayNotification(strMessage);
374 qDebug() << "Vicar-Daemon: "<< d->strLastDTMFCode << " sent as DTMF code.";
378 Connecting and Disconnecting from/to DBus signal for each international call
379 may not be the most efficient way of handling this. But we need to make sure
380 that the DTMF codes are sent only for the calls placed by this app (i.e calls to Calling card number).
383 qDebug() << "Vicar-Daemon: Now disconnecting from call status monitors..";
384 stopCallStatusMonitors();
387 QString CallRouter::convertToDTMFCode(QString strNumber){
390 if (!strNumber.isEmpty()){
391 //int intDTMFDelay = 1;
393 //Add the prefix p so that there is some delay after the call is picked up by the automated system to send DTMF tones.
394 //strDTMFCode = QString("").fill('p',intDTMFDelay);
397 //Now check whether we need a prefix
398 QString strDTMFPrefix = d->currentProfile->dtmfPrefix;
400 if (!strDTMFPrefix.isEmpty()){
401 strDTMFCode = strDTMFCode.append(strDTMFPrefix);
404 //Get the format required by calling card from coniguration
405 QString qstrDTMFFormat = d->currentProfile->dtmfFormat;
406 if (qstrDTMFFormat.isEmpty()) qstrDTMFFormat = "<Country Code><Area Code><Phone Number>";
408 /* Replace 00 (international dialing code) at the beginning
409 and also replace any character other than the numbers 0-9 and p.
411 QRegExp regexp = QRegExp("(^0{2})|[^0-9p]");
412 strNumber = strNumber.replace(regexp,"");
414 /* Now we have a clean number with only country code, area code and phone number,
415 lets convert it to the calling card friendly format
417 if (qstrDTMFFormat.startsWith("+")){
418 strDTMFCode = strDTMFCode.append("+");
420 else if (qstrDTMFFormat.startsWith("00")){
421 strDTMFCode = strDTMFCode.append("00");
423 else if (qstrDTMFFormat.startsWith("011")){
424 strDTMFCode = strDTMFCode.append("011");
427 strDTMFCode = strDTMFCode.append(strNumber);
429 //Now check whether we need a suffix
430 QString strDTMFSuffix = d->currentProfile->dtmfSuffix;
431 if (!strDTMFSuffix.isEmpty()){
432 strDTMFCode = strDTMFCode.append(strDTMFSuffix);
439 //DBus Method used by external applications to check whether VICaR is enabled and running
440 bool CallRouter::isRunning(){
444 //Verify Whether VICaR telepathy account is online
446 if (d->tpUtility->getAccountStatus() == "Connected"){
455 //DBus Method used by external applications to call via VICaR
456 QString CallRouter::callInternationalNumber(const QString& strDestinationNumber){
458 QString strErrorMessage;
460 qDebug() << "Vicar-Daemon: New call requested by external application. Destination number is " << strDestinationNumber;
462 if (isValidPhoneNumber(strDestinationNumber)){
464 //Remove spaces in the phone number before using
465 QString numberWithoutSpaces = QString(strDestinationNumber).remove(" ");
467 strErrorMessage = this->callViaCallingCard(numberWithoutSpaces);
470 strErrorMessage = QString("Vicar-Daemon: %1 is not a valid number").arg(strDestinationNumber);
471 if (strDestinationNumber != "publish" && strDestinationNumber != "subscribe"){
472 d->dbusUtility->displayNotification(QString("Vicar: %1 is not a valid number").arg(strDestinationNumber));
476 qDebug() << strErrorMessage;
478 if (strErrorMessage.isEmpty()){
479 return QString("Success");
482 return strErrorMessage;
486 //Check whether a string is valid phone number
487 bool CallRouter::isValidPhoneNumber(QString strPhoneNumber){
489 /* Remove all dialble characters and space. The resulting string should be a valid number */
490 QRegExp regexp = QRegExp("[p+*# ]");
492 strPhoneNumber = strPhoneNumber.replace(regexp,"");
494 qDebug() << "Vicar Daemon: Cleaned up phone number is " << strPhoneNumber;
496 /* Now remove all digits, the resulting string should be empty, then it is a valid number */
497 regexp = QRegExp("[0-9]");
499 strPhoneNumber = strPhoneNumber.replace(regexp,"");
501 bool isNumber = strPhoneNumber.isEmpty();