X-Git-Url: http://git.maemo.org/git/?p=vicar;a=blobdiff_plain;f=src%2Fvicar-daemon%2Fcpp%2Fcallrouter.cpp;fp=src%2Fvicar-daemon%2Fcpp%2Fcallrouter.cpp;h=7a5d8ed39da5411f7ae368ffee598279982eda2f;hp=0000000000000000000000000000000000000000;hb=74800375ecf7f41e290cf7cc7fa9ee8b230be68e;hpb=89f0017e6a73945ea83247472a6fa07d6ee536b5 diff --git a/src/vicar-daemon/cpp/callrouter.cpp b/src/vicar-daemon/cpp/callrouter.cpp new file mode 100755 index 0000000..7a5d8ed --- /dev/null +++ b/src/vicar-daemon/cpp/callrouter.cpp @@ -0,0 +1,496 @@ +/* +@version: 0.6 +@author: Sudheer K. +@license: GNU General Public License +*/ + +#include "callrouter.h" +#include "vicardbusadaptor.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class CallRouterPrivate +{ +public: + CallRouterPrivate(CallRouter * p) : + databaseUtility(new DatabaseUtility(p)), + dbusAdaptor(new VicarDbusAdaptor(p)), + dbusUtility(new DbusUtility(p)), + gconfUtility(new GConfUtility(p)), + tpUtility(new TelepathyUtility(p)), + parent(p) + { + Q_ASSERT(0 != dbusAdaptor); + //Do not open here - Unable to capture changes to DB if it is open too early and closed late. + //databaseUtility->openDatabase(); + } + + ~CallRouterPrivate() + { + qDebug() << "VICaR: Call Router Destructing"; + //databaseUtility->closeDatabase(); + } + + DatabaseUtility *databaseUtility; + VicarDbusAdaptor * dbusAdaptor; + DbusUtility * dbusUtility; + GConfUtility * gconfUtility; + TelepathyUtility *tpUtility; + QString strLastDialedNumber; + QString strLastDTMFCode; + org::maemo::vicar::Profile *currentProfile; + CallRouter * const parent; +}; + +// --------------------------------------------------------------------------- + +CallRouter::CallRouter(QObject *parent) : + QObject(parent), + d(new CallRouterPrivate(this)) +{ + Q_ASSERT(0 != d); + this->registerDBusService(); + qDebug() << "Vicar-Daemon: Registered DBus Service " << APPLICATION_DBUS_SERVICE; +} + +CallRouter::~CallRouter(){ +} + +void CallRouter::registerDBusService(){ + //Connect to Session Bus + QDBusConnection connection = d->dbusUtility->getConnection(false); + + if (!connection.interface()->isServiceRegistered(APPLICATION_DBUS_SERVICE)){ + + if (!connection.registerService(APPLICATION_DBUS_SERVICE)) { + qDebug() << "Vicar-Daemon: " << d->dbusUtility->getErrorMessage(); + exit(1); + } + } + + if (!connection.registerObject(APPLICATION_DBUS_PATH, this, QDBusConnection::ExportAdaptors)) { + qDebug() << "Vicar-Daemon: " << d->dbusUtility->getErrorMessage(); + exit(2); + } + +} + + +void CallRouter::unregisterDBusService(){ + + //Disconnect from Session bus + QDBusConnection connection = d->dbusUtility->getConnection(false); + + connection.unregisterObject(APPLICATION_DBUS_PATH,QDBusConnection::UnregisterTree); + + if (!connection.unregisterService(APPLICATION_DBUS_SERVICE)) { + qDebug() << "Vicar-Daemon: " << d->dbusUtility->getErrorMessage(); + exit(3); + } + +} + +QString CallRouter::callViaCallingCard(QString strDestinationNumber){ + + d->currentProfile = new org::maemo::vicar::Profile(); + d->currentProfile->profileID = 0; + + d->databaseUtility->openDatabase(); + bool result = d->databaseUtility->findProfileByNumber(strDestinationNumber,d->currentProfile); + + QString strErrorMessage; + if (!result){ + strErrorMessage = QString("Vicar-Daemon: Error finding VICaR profile. %1").arg(d->databaseUtility->lastError().text()); + } + else if (d->currentProfile->profileID == 0){ + bool routeOnDefault = d->gconfUtility->getGconfValueBoolean("route_on_default"); + if (routeOnDefault){ + qDebug() << "Vicar-Daemon: Routing directly as per configuration"; + this->placeCall(strDestinationNumber); + } + else{ + qDebug() << "Vicar-Daemon: No profile found. Stopping.."; + strErrorMessage = "Vicar: No routing profile defined for this number."; + d->dbusUtility->displayNotification(strErrorMessage ); + } + } + else{ + //Now call the calling card number. This is generally a local and/or tollfree number + QString strCallingCardNumber = d->currentProfile->gatewayNumber; + qDebug() << "Vicar-Daemon: Initiating call to "<< strCallingCardNumber; + bool status = this->placeCall(strCallingCardNumber); + d->strLastDialedNumber = strDestinationNumber; + + QString strUserMessage; + + if (status){ + qDebug() << "Vicar-Daemon: Call initiated successfully. Connecting DBus slot for audio connection monitor"; + startCallStatusMonitors(); + } + else { + strUserMessage = QString("Unable to initiate new call to ").append(strCallingCardNumber); + strErrorMessage = d->dbusUtility->getErrorMessage(); + qDebug() << "Vicar-Daemon: " << strErrorMessage; + d->strLastDialedNumber.clear(); + delete d->currentProfile; + d->currentProfile = 0; + } + d->dbusUtility->displayNotification(strUserMessage); + } + + d->databaseUtility->closeDatabase(); + return strErrorMessage; +} + +bool CallRouter::placeCall(QString number){ + + QList argsToSend; + argsToSend.append(number); + argsToSend.append(0); + + bool status = d->dbusUtility->sendMethodCall(CSD_SERVICE, + CSD_CALL_PATH, + CSD_CALL_INTERFACE, + QString("CreateWith"),argsToSend); + return status; + +} + +void CallRouter::startCallStatusMonitors(){ + /* Declare the slot to be executed when a call is picked up by other party (Audio connection established). + We need this to confirm whether a call went though successfully. + */ + + QDBusConnection connection = d->dbusUtility->getConnection(); + + bool success = connection.connect(QString(""), + CSD_CALL_INSTANCE_PATH, + CSD_CALL_INSTANCE_INTERFACE, + QString("AudioConnect"),this, + SLOT(sendNumberAsDTMFCode(const QDBusMessage&))); + + if (success){ + qDebug() << "Vicar-Daemon: Successfully connected to Dbus signal AudioConnect in interface "<< CSD_CALL_INSTANCE_INTERFACE; + } + else{ + qDebug() << "Vicar-Daemon: Failed to connect to Dbus signal AudioConnect in interface "<< CSD_CALL_INSTANCE_INTERFACE; + qDebug() <<"Vicar-Daemon: DBus Error: "<< d->dbusUtility->getErrorMessage(); + } + + + /* Declare the slot to be executed when the DTMF code is sent. + */ + + success = connection.connect(QString(""), + CSD_CALL_INSTANCE_PATH, + CSD_CALL_INSTANCE_INTERFACE, + QString("StoppedDTMF"),this, + SLOT(displayDTMFConfirmation())); + + if (success){ + qDebug() << "Vicar-Daemon: Successfully connected to Dbus signal StoppedDTMF in interface "<< CSD_CALL_INSTANCE_INTERFACE; + } + else{ + qDebug() << "Vicar-Daemon: Failed to connect to Dbus signal StoppedDTMF in interface "<< CSD_CALL_INSTANCE_INTERFACE; + qDebug() <<"Vicar-Daemon: DBus Error: "<< d->dbusUtility->getErrorMessage(); + } + + + /* Declare the slot to be executed when the call is terminated (due to connection errors etc). + We need this to avoid sending DTMF code on wrong calls. + */ + + success = connection.connect(QString(""), + CSD_CALL_INSTANCE_PATH, + CSD_CALL_INSTANCE_INTERFACE, + QString("Terminated"),this, + SLOT(stopCallStatusMonitors())); + + if (success){ + qDebug() << "Vicar-Daemon: Successfully connected to Dbus signal Terminated in interface "<< CSD_CALL_INSTANCE_INTERFACE; + } + else{ + qDebug() << "Vicar-Daemon: Failed to connect to Dbus signal Terminated in interface "<< CSD_CALL_INSTANCE_INTERFACE; + qDebug() <<"Vicar-Daemon: DBus Error: "<< d->dbusUtility->getErrorMessage(); + } + + /* Declare the slot to be executed when a call is received + (before we can place the call to calling card number). + It is extremely rare that somebody should get a call within these few seconds. + In any case, we need this to avoid sending DTMF code on the received call. + + Btw - I don't care for the incoming number here. If anyone is calling the user before we can send DTMF code, + then we stop sending the DTMF code even if user does not respond to the call. + */ + + success = connection.connect(QString(""), + CSD_CALL_PATH, + CSD_CALL_INTERFACE, + QString("Coming"),this, + SLOT(stopCallStatusMonitors())); + + if (success){ + qDebug() << "Vicar-Daemon: Successfully connected to Dbus signal Coming in interface" << CSD_CALL_INTERFACE; + } + else{ + qDebug() << "Vicar-Daemon: Failed to connect to Dbus signal Coming in interface" << CSD_CALL_INTERFACE; + qDebug() <<"Vicar-Daemon: DBus Error: "<< d->dbusUtility->getErrorMessage(); + } +} + +void CallRouter::stopCallStatusMonitors(){ + + d->strLastDTMFCode.clear(); + d->strLastDialedNumber.clear(); + delete d->currentProfile; + d->currentProfile = 0; + + QDBusConnection connection = d->dbusUtility->getConnection(); + + // Disconnect the slot for audio connection status + bool status = connection.disconnect(QString(""), + CSD_CALL_INSTANCE_PATH, + CSD_CALL_INSTANCE_INTERFACE, + QString("AudioConnect"),this, + SLOT(sendNumberAsDTMFCode(const QDBusMessage&))); + + if (status){ + qDebug() << "Vicar-Daemon: Successfully disconnected from Dbus signal AudioConnect in interface "<< CSD_CALL_INSTANCE_INTERFACE; + } + else{ + qDebug() << "Vicar-Daemon: Failed to disconnect from Dbus signal AudioConnect in interface "<< CSD_CALL_INSTANCE_INTERFACE; + qDebug() <<"Vicar-Daemon: DBus Error: "<< d->dbusUtility->getErrorMessage(); + } + + // Disconnect the slot for monitoring DTMF completion + status = connection.disconnect(QString(""), + CSD_CALL_INSTANCE_PATH, + CSD_CALL_INSTANCE_INTERFACE, + QString("StoppedDTMF"),this, + SLOT(displayDTMFConfirmation())); + + if (status){ + qDebug() << "Vicar-Daemon: Successfully disconnected from Dbus signal StoppedDTMF in interface "<< CSD_CALL_INSTANCE_INTERFACE; + } + else{ + qDebug() << "Vicar-Daemon: Failed to disconnect from Dbus signal StoppedDTMF in interface "<< CSD_CALL_INSTANCE_INTERFACE; + qDebug() <<"Vicar-Daemon: DBus Error: "<< d->dbusUtility->getErrorMessage(); + } + + + // Disconnect the slot for monitoring terminated calls + status = connection.disconnect(QString(""), + CSD_CALL_INSTANCE_PATH, + CSD_CALL_INSTANCE_INTERFACE, + QString("Terminated"),this, + SLOT(stopCallStatusMonitors())); + + if (status){ + qDebug() << "Vicar-Daemon: Successfully disconnected from Dbus signal Terminated in interface "<< CSD_CALL_INSTANCE_INTERFACE; + } + else{ + qDebug() << "Vicar-Daemon: Failed to disconnect from Dbus signal Terminated in interface "<< CSD_CALL_INSTANCE_INTERFACE; + qDebug() <<"Vicar-Daemon: DBus Error: "<< d->dbusUtility->getErrorMessage(); + } + + // Disconnect the slot for monitoring incoming calls + status = connection.disconnect(QString(""), + CSD_CALL_PATH, + CSD_CALL_INTERFACE, + QString("Coming"),this, + SLOT(stopCallStatusMonitors())); + + if (status){ + qDebug() << "Vicar-Daemon: Successfully disconnected from Dbus signal Coming in interface" << CSD_CALL_INTERFACE; + } + else{ + qDebug() << "Vicar-Daemon: Failed to disconnect from Dbus signal Coming in interface" << CSD_CALL_INTERFACE; + qDebug() <<"Vicar-Daemon: DBus Error: "<< d->dbusUtility->getErrorMessage(); + } +} + +void CallRouter::sendNumberAsDTMFCode(const QDBusMessage& dbusMessage){ + + if (!d->strLastDialedNumber.isEmpty() && d->currentProfile != 0){ + //Verify whether we have the last dialed number available + + QList listArguments = dbusMessage.arguments(); + bool audioConnected = listArguments.first().toBool(); + + if (audioConnected){ + // Now that the call to Calling card number is successful. We can send the original number as DTMF tones + QString strDTMFCode = convertToDTMFCode(d->strLastDialedNumber); + + qDebug() << "Vicar-Daemon: Audio connection established. Sending DTMF code "<< strDTMFCode; + + + QList argsToSend; + argsToSend.append(strDTMFCode); + bool status = d->dbusUtility->sendMethodCall(CSD_SERVICE, + CSD_CALL_PATH, + CSD_CALL_INTERFACE, + QString("SendDTMF"),argsToSend); + + if (status){ + qDebug() << "Vicar-Daemon: Sending " << strDTMFCode << " as DTMF code."; + d->strLastDTMFCode = strDTMFCode; + } + else{ + qDebug() << "Vicar-Daemon: Unable to send DTMF code."; + } + } + else{ + qDebug() << "Vicar-Daemon: Audio not yet connected."; + } + } + else + { + qDebug() << "Vicar-Daemon: Last dialed number is empty."; + } +} + +void CallRouter::displayDTMFConfirmation(){ + //This slot is called when the all the DTMF tones are sent (i.e StoppedDTMF signal is emitted) + //Just display confirmation message and cleanup + + + if (!d->strLastDTMFCode.isEmpty()){ + QString strMessage = d->strLastDTMFCode.append(" sent as DTMF code"); + d->dbusUtility->displayNotification(strMessage); + qDebug() << "Vicar-Daemon: "<< d->strLastDTMFCode << " sent as DTMF code."; + } + + /* + Connecting and Disconnecting from/to DBus signal for each international call + may not be the most efficient way of handling this. But we need to make sure + that the DTMF codes are sent only for the calls placed by this app (i.e calls to Calling card number). + */ + + qDebug() << "Vicar-Daemon: Now disconnecting from call status monitors.."; + stopCallStatusMonitors(); +} + +QString CallRouter::convertToDTMFCode(QString strNumber){ + QString strDTMFCode; + + if (!strNumber.isEmpty()){ + //int intDTMFDelay = 1; + + //Add the prefix p so that there is some delay after the call is picked up by the automated system to send DTMF tones. + //strDTMFCode = QString("").fill('p',intDTMFDelay); + strDTMFCode = ""; + + //Now check whether we need a prefix + QString strDTMFPrefix = d->currentProfile->dtmfPrefix; + + if (!strDTMFPrefix.isEmpty()){ + strDTMFCode = strDTMFCode.append(strDTMFPrefix); + } + + //Get the format required by calling card from coniguration + QString qstrDTMFFormat = d->currentProfile->dtmfFormat; + if (qstrDTMFFormat.isEmpty()) qstrDTMFFormat = ""; + + /* Replace 00 (international dialing code) at the beginning + and also replace any character other than the numbers 0-9 and p. + */ + QRegExp regexp = QRegExp("(^0{2})|[^0-9p]"); + strNumber = strNumber.replace(regexp,""); + + /* Now we have a clean number with only country code, area code and phone number, + lets convert it to the calling card friendly format + */ + if (qstrDTMFFormat.startsWith("+")){ + strDTMFCode = strDTMFCode.append("+"); + } + else if (qstrDTMFFormat.startsWith("00")){ + strDTMFCode = strDTMFCode.append("00"); + } + else if (qstrDTMFFormat.startsWith("011")){ + strDTMFCode = strDTMFCode.append("011"); + } + + strDTMFCode = strDTMFCode.append(strNumber); + + //Now check whether we need a suffix + QString strDTMFSuffix = d->currentProfile->dtmfSuffix; + if (!strDTMFSuffix.isEmpty()){ + strDTMFCode = strDTMFCode.append(strDTMFSuffix); + } + } + + return strDTMFCode; +} + +//DBus Method used by external applications to check whether VICaR is enabled and running +bool CallRouter::isRunning(){ + + return true; + + //Verify Whether VICaR telepathy account is online + /* + if (d->tpUtility->getAccountStatus() == "Connected"){ + return true; + } + else{ + return false; + } + */ +} + +//DBus Method used by external applications to call via VICaR +QString CallRouter::callInternationalNumber(const QString& strDestinationNumber){ + + QString strErrorMessage; + + qDebug() << "Vicar-Daemon: New call requested by external application. Destination number is " << strDestinationNumber; + + if (isValidPhoneNumber(strDestinationNumber)){ + + //Remove spaces in the phone number before using + QString numberWithoutSpaces = QString(strDestinationNumber).remove(" "); + + strErrorMessage = this->callViaCallingCard(numberWithoutSpaces); + } + else{ + strErrorMessage = QString("Vicar-Daemon: %1 is not a valid number").arg(strDestinationNumber); + if (strDestinationNumber != "publish" && strDestinationNumber != "subscribe"){ + d->dbusUtility->displayNotification(QString("Vicar: %1 is not a valid number").arg(strDestinationNumber)); + } + } + + qDebug() << strErrorMessage; + + if (strErrorMessage.isEmpty()){ + return QString("Success"); + } + else{ + return strErrorMessage; + } + } + +//Check whether a string is valid phone number +bool CallRouter::isValidPhoneNumber(QString strPhoneNumber){ + +/* Remove all dialble characters and space. The resulting string should be a valid number */ + QRegExp regexp = QRegExp("[p+*# ]"); + + strPhoneNumber = strPhoneNumber.replace(regexp,""); + + qDebug() << "Vicar Daemon: Cleaned up phone number is " << strPhoneNumber; + +/* Now remove all digits, the resulting string should be empty, then it is a valid number */ + regexp = QRegExp("[0-9]"); + + strPhoneNumber = strPhoneNumber.replace(regexp,""); + + bool isNumber = strPhoneNumber.isEmpty(); + return isNumber; +}