--- /dev/null
+/*
+@version: 0.6
+@author: Sudheer K. <scifi1947 at gmail.com>
+@license: GNU General Public License
+*/
+
+#include "callrouter.h"
+#include "vicardbusadaptor.h"
+#include <dbusutility.h>
+#include <gconfutility.h>
+#include <databaseutility.h>
+#include <telepathyutility.h>
+#include <QDebug>
+#include <QRegExp>
+#include <QDBusConnection>
+#include <QDBusMessage>
+#include <QStringListIterator>
+
+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<QVariant> 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<QVariant> 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<QVariant> 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 = "<Country Code><Area Code><Phone Number>";
+
+ /* 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;
+}