Release 0.5-1 contains (Muti-profile support, Contacts Integration)
[vicar] / src / vicar-daemon / src / callrouter.cpp
old mode 100644 (file)
new mode 100755 (executable)
index e6155f5..9313b2f
 /*
-@version: 0.2
-@author: Sudheer K. <scifi.guy@hotmail.com>
+@version: 0.5
+@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 <QTimer>
+#include <QDBusMessage>
 #include <QStringListIterator>
 
-//Create a statis Dbus utility object that will be shared across all member functions
-static DbusUtility dbusUtility = DbusUtility();
-static QString strLastDialedNumber = QString();
+
+//static QString strLastDialedNumber = QString();
+//static org::maemo::vicar::Profile currentProfile;
+
+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;
+    org::maemo::vicar::Profile *currentProfile;
+    CallRouter * const parent;
+};
+
+// ---------------------------------------------------------------------------
 
 CallRouter::CallRouter(QObject *parent) :
-    QObject(parent)
+    QObject(parent),
+    d(new CallRouterPrivate(this))
 {
-        gconfUtility = new GConfUtility();
+        Q_ASSERT(0 != d);
+        this->registerDBusService();
+        qDebug() << "Registered DBus Service " << APPLICATION_DBUS_SERVICE;
 }
 
 CallRouter::~CallRouter(){
-        delete gconfUtility;
-        gconfUtility = 0;
 }
 
 void CallRouter::registerDBusService(){
-    QDBusConnection connection = dbusUtility.getConnection();
+    //Connect to Session Bus
+    QDBusConnection connection = d->dbusUtility->getConnection(false);
+
+    if (!connection.interface()->isServiceRegistered(APPLICATION_DBUS_SERVICE)){
 
-    if (!connection.registerService(APPLICATION_DBUS_SERVICE)) {
-        qDebug() << dbusUtility.getErrorMessage();
-        exit(1);
+        if (!connection.registerService(APPLICATION_DBUS_SERVICE)) {
+            qDebug() << d->dbusUtility->getErrorMessage();
+            exit(1);
+        }
     }
 
-    if (!connection.registerObject(APPLICATION_DBUS_PATH, this,
-            QDBusConnection::ExportScriptableSlots)) {
-        qDebug() << dbusUtility.getErrorMessage();
+    if (!connection.registerObject(APPLICATION_DBUS_PATH, this, QDBusConnection::ExportAdaptors)) {
+        qDebug() << d->dbusUtility->getErrorMessage();
         exit(2);
     }
 
-    this->connectToDBusSignals();
-
 }
 
 
 void CallRouter::unregisterDBusService(){
 
-    this->disconnectFromDBusSignals();
-
-    QDBusConnection connection = dbusUtility.getConnection();
+    //Disconnect from Session bus
+    QDBusConnection connection = d->dbusUtility->getConnection(false);
 
     connection.unregisterObject(APPLICATION_DBUS_PATH,QDBusConnection::UnregisterTree);
 
     if (!connection.unregisterService(APPLICATION_DBUS_SERVICE)) {
-        qDebug() << dbusUtility.getErrorMessage();
+        qDebug() << d->dbusUtility->getErrorMessage();
         exit(3);
     }
 
 }
 
-void CallRouter::connectToDBusSignals(){
+QString CallRouter::callViaCallingCard(QString strDestinationNumber){
 
-    QDBusConnection connection = dbusUtility.getConnection();
+        d->currentProfile = new org::maemo::vicar::Profile();
+        d->currentProfile->profileID = 0;
 
-    // Connect to the signal to enable call routing
-    bool success = connection.connect(QString(""),QString(""),
-                       APPLICATION_DBUS_INTERFACE,
-                       QString("startOutgoingCallMonitor"),this,
-                       SLOT(startOutgoingCallMonitor()));
-
-    if (success){
-        qDebug() << "Successfully connected to Dbus signal org.maemo.vicar.startOutgoingCallMonitor";
-    }
-    else{
-        qDebug() << "Failed to connect to Dbus signal org.maemo.vicar.startOutgoingCallMonitor";
-        qDebug() <<"DBus Error: "<< qPrintable(dbusUtility.getErrorMessage());
-    }
-
-    // Connect to the signal to disable call routing
-    success = connection.connect(QString(""),QString(""),
-                       APPLICATION_DBUS_INTERFACE,
-                       QString("stopOutgoingCallMonitor"),this,
-                       SLOT(stopOutgoingCallMonitor()));
-
-    if (success){
-        qDebug() << "Successfully connected to Dbus signal org.maemo.vicar.stopOutgoingCallMonitor";
-    }
-    else{
-        qDebug() << "Failed to connect to Dbus signal org.maemo.vicar.stopOutgoingCallMonitor";
-        qDebug() <<"DBus Error: "<< qPrintable(dbusUtility.getErrorMessage());
-    }
-
-}
+        d->databaseUtility->openDatabase();
+        bool result = d->databaseUtility->findProfileByNumber(strDestinationNumber,d->currentProfile);
 
-void CallRouter::disconnectFromDBusSignals(){
-
-    QDBusConnection connection = dbusUtility.getConnection();
-
-    // Disconnect from the signal to enable call routing
-    bool success = connection.disconnect(QString(""),QString(""),
-                       APPLICATION_DBUS_INTERFACE,
-                       QString("startOutgoingCallMonitor"),this,
-                       SLOT(startOutgoingCallMonitor()));
-
-    if (success){
-        qDebug() << "Successfully disconnected from Dbus signal org.maemo.vicar.startOutgoingCallMonitor";
-    }
-    else{
-        qDebug() << "Failed to disconnect from Dbus signal org.maemo.vicar.startOutgoingCallMonitor";
-        qDebug() <<"DBus Error: "<< qPrintable(dbusUtility.getErrorMessage());
-    }
-
-    // Disconnect from the signal to disable call routing
-    success = connection.connect(QString(""),QString(""),
-                       APPLICATION_DBUS_INTERFACE,
-                       QString("stopOutgoingCallMonitor"),this,
-                       SLOT(stopOutgoingCallMonitor()));
-
-    if (success){
-        qDebug() << "Successfully disconnected from Dbus signal org.maemo.vicar.stopOutgoingCallMonitor";
-    }
-    else{
-        qDebug() << "Failed to disconnect from Dbus signal org.maemo.vicar.stopOutgoingCallMonitor";
-        qDebug() <<"DBus Error: "<< qPrintable(dbusUtility.getErrorMessage());
-    }
-
-}
-
-void CallRouter::startOutgoingCallMonitor(){
-
-    // Connect to DBus to monitor all outgoing calls
-
-    QDBusConnection connection = dbusUtility.getConnection();
-
-
-    // Declare the slot to be executed when new calls are placed
-
-    bool success = connection.connect(QString(""),
-                       CSD_CALL_PATH,
-                       CSD_CALL_INTERFACE,
-                       QString("CreateRequested"),this,
-                       SLOT(processOutgoingCall(const QDBusMessage&)));
-
-    if (success){
-        qDebug() << "Successfully connected to Dbus signal CreateRequested in interface "<< CSD_CALL_INTERFACE;
-    }
-    else{
-        qDebug() << "Failed to connect to Dbus signal CreateRequested in interface " << CSD_CALL_INTERFACE;
-        qDebug() <<"DBus Error: "<< dbusUtility.getErrorMessage();
-    }
-
-
-}
-
-void CallRouter::stopOutgoingCallMonitor(){
-
-    this->stopCallStatusMonitors();
-
-    //Disconnect the slots from Dbus signals
-    QDBusConnection connection = dbusUtility.getConnection();
-
-    // Disconnect the slot for new calls
-    bool status = connection.disconnect(QString(""),
-                                           CSD_CALL_PATH,
-                                           CSD_CALL_INTERFACE,
-                                           QString("CreateRequested"),this,
-                                           SLOT(processOutgoingCall(const QDBusMessage&)));
-
-    if (status){
-        qDebug() << "Successfully disconnected from Dbus signal CreateRequested in interface "<< CSD_CALL_INTERFACE;
-    }
-    else{
-        qDebug() << "Failed to disconnect from Dbus signal CreateRequested in interface "<< CSD_CALL_INTERFACE;
-        qDebug() <<"DBus Error: "<< dbusUtility.getErrorMessage();
-    }
-
-}
-
-void CallRouter::processOutgoingCall(const QDBusMessage& dbusMessage){
-
-    //Verify Whether Call Routing is Enabled
-    bool isRoutingEnabled = gconfUtility->getGconfValueBoolean("routing_enabled");
-
-    if (isRoutingEnabled){
-        //User is making a phone call. Get the phone number and verify if it is an international number
-        QList<QVariant> listArguments = dbusMessage.arguments();
-        QString strInternationalNumber =  listArguments.first().toString();
-
-        qDebug() << "New Call Identified. Destination number is " << strInternationalNumber;
-
-         if (strInternationalNumber.startsWith("+") ||
-             strInternationalNumber.startsWith("00"))
-         {
-             qDebug() << "International number "<< strInternationalNumber << " recognized. Starting proceedings..";
-
-             //Check whether this is one of the excluded country codes
-             if (!isExcludedNumber(strInternationalNumber)){
-
-                //International number. Disconnect the current call (A new call will be placed)
-
-                //No arguments required to cancel the current call
-                QList<QVariant> argsToSend;
-                bool status = dbusUtility.sendMethodCall(CSD_CALL_SERVICE,
-                                                         CSD_CALL_PATH,
-                                                         CSD_CALL_INTERFACE,
-                                                         QString("Release"),argsToSend);
-
-                QString strUserMessage;
-                if (status){
-                    strUserMessage = QString("Routing international call via ").append(APPLICATION_FRIENDLY_NAME).append("..");
-                    qDebug() << strUserMessage;
-                    strLastDialedNumber = strInternationalNumber;
-                }
-                else{
-                    strUserMessage = QString("Call could not be cancelled.");
-                    qDebug() << dbusUtility.getErrorMessage();
-                }
-
-                dbusUtility.displayNotification(strUserMessage);
-
-                //Wait for a few seconds before the current call is completely disconnected
-                QTimer *timer = new QTimer(this);
-                timer->setSingleShot(true);
-                connect(timer, SIGNAL(timeout()), this, SLOT(callViaCallingCard()));
-                timer->start(3000);
-             }
+        QString strErrorMessage;
+        if (!result){
+            strErrorMessage = QString("Error finding VICaR profile. %1").arg(d->databaseUtility->lastError().text());
         }
-     }
-}
-
-void CallRouter::callViaCallingCard(){
-        //Now call the calling card number. This is generally a local and/or tollfree number
+        else if (d->currentProfile->profileID == 0){
+            bool routeOnDefault = d->gconfUtility->getGconfValueBoolean("route_on_default");
+            if (routeOnDefault){
+                qDebug() << "Routing directly as per configuration";
+               this->placeCall(strDestinationNumber);
+            }
+            else{
+                qDebug() << "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() << "Initiating call to "<< strCallingCardNumber;
+            bool status = this->placeCall(strCallingCardNumber);
+            d->strLastDialedNumber = strDestinationNumber;
 
-        QString strCallingCardNumber = gconfUtility->getGconfValueString("calling_card_number");
+            QString strUserMessage;
 
-        qDebug() << "Wait time elapsed. Initiating call to "<< strCallingCardNumber;
+            if (status){
+                qDebug() << "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() << strErrorMessage;
+                d->strLastDialedNumber.clear();
+                delete d->currentProfile;
+                d->currentProfile = 0;
+            }
+            d->dbusUtility->displayNotification(strUserMessage);
+        }
 
-        QList<QVariant> argsToSend;
-        argsToSend.append(strCallingCardNumber);
-        argsToSend.append(0);
+        d->databaseUtility->closeDatabase();
+        return strErrorMessage;
+}
 
-        bool status = dbusUtility.sendMethodCall(CSD_SERVICE,
-                                                 CSD_CALL_PATH,
-                                             CSD_CALL_INTERFACE,
-                                             QString("CreateWith"),argsToSend);
+bool CallRouter::placeCall(QString number){
 
-        QString strUserMessage;
-        if (status){
-            qDebug() << "Call initiated successfully. Connecting DBus slot for audio connection monitor";
-             startCallStatusMonitors();
-        }
-        else {
-            strUserMessage = QString("Unable to initiate new call to ").append(strCallingCardNumber);
-            qDebug() << dbusUtility.getErrorMessage();
-            strLastDialedNumber.clear();
-        }
+    QList<QVariant> argsToSend;
+    argsToSend.append(number);
+    argsToSend.append(0);
 
-        dbusUtility.displayNotification(strUserMessage);
+    bool status = d->dbusUtility->sendMethodCall(CSD_SERVICE,
+                                             CSD_CALL_PATH,
+                                         CSD_CALL_INTERFACE,
+                                         QString("CreateWith"),argsToSend);
+    return status;
 
 }
 
@@ -268,7 +171,7 @@ void CallRouter::startCallStatusMonitors(){
        We need this to confirm whether a call went though successfully.
     */
 
-    QDBusConnection connection = dbusUtility.getConnection();
+    QDBusConnection connection = d->dbusUtility->getConnection();
 
     bool success = connection.connect(QString(""),
                            CSD_CALL_INSTANCE_PATH,
@@ -281,7 +184,7 @@ void CallRouter::startCallStatusMonitors(){
     }
     else{
         qDebug() << "Failed to connect to Dbus signal AudioConnect in interface "<< CSD_CALL_INSTANCE_INTERFACE;
-        qDebug() <<"DBus Error: "<< dbusUtility.getErrorMessage();
+        qDebug() <<"DBus Error: "<< d->dbusUtility->getErrorMessage();
     }
 
     /* Declare the slot to be executed when the call is terminated (due to connection errors etc).
@@ -299,7 +202,7 @@ void CallRouter::startCallStatusMonitors(){
     }
     else{
         qDebug() << "Failed to connect to Dbus signal Terminated in interface "<< CSD_CALL_INSTANCE_INTERFACE;
-        qDebug() <<"DBus Error: "<< dbusUtility.getErrorMessage();
+        qDebug() <<"DBus Error: "<< d->dbusUtility->getErrorMessage();
     }
 
     /* Declare the slot to be executed when a call is received
@@ -322,15 +225,17 @@ void CallRouter::startCallStatusMonitors(){
     }
     else{
         qDebug() << "Failed to connect to Dbus signal Coming in interface" << CSD_CALL_INTERFACE;
-        qDebug() <<"DBus Error: "<< dbusUtility.getErrorMessage();
+        qDebug() <<"DBus Error: "<< d->dbusUtility->getErrorMessage();
     }
 }
 
 void CallRouter::stopCallStatusMonitors(){
 
-    strLastDialedNumber.clear();
+    d->strLastDialedNumber.clear();
+    delete d->currentProfile;
+    d->currentProfile = 0;
 
-    QDBusConnection connection = dbusUtility.getConnection();
+    QDBusConnection connection = d->dbusUtility->getConnection();
 
     // Disconnect the slot for audio connection status
     bool status = connection.disconnect(QString(""),
@@ -344,7 +249,7 @@ void CallRouter::stopCallStatusMonitors(){
     }
     else{
         qDebug() << "Failed to disconnect from Dbus signal AudioConnect in interface "<< CSD_CALL_INSTANCE_INTERFACE;
-        qDebug() <<"DBus Error: "<< dbusUtility.getErrorMessage();
+        qDebug() <<"DBus Error: "<< d->dbusUtility->getErrorMessage();
     }
 
     // Disconnect the slot for monitoring terminated calls
@@ -359,7 +264,7 @@ void CallRouter::stopCallStatusMonitors(){
     }
     else{
         qDebug() << "Failed to disconnect from Dbus signal Terminated in interface "<< CSD_CALL_INSTANCE_INTERFACE;
-        qDebug() <<"DBus Error: "<< dbusUtility.getErrorMessage();
+        qDebug() <<"DBus Error: "<< d->dbusUtility->getErrorMessage();
     }
 
     // Disconnect the slot for monitoring incoming calls
@@ -374,13 +279,13 @@ void CallRouter::stopCallStatusMonitors(){
     }
     else{
         qDebug() << "Failed to disconnect from Dbus signal Coming in interface" << CSD_CALL_INTERFACE;
-        qDebug() <<"DBus Error: "<< dbusUtility.getErrorMessage();
+        qDebug() <<"DBus Error: "<< d->dbusUtility->getErrorMessage();
     }
 }
 
 void CallRouter::sendNumberAsDTMFCode(const QDBusMessage& dbusMessage){
 
-    if (!strLastDialedNumber.isEmpty()){
+    if (!d->strLastDialedNumber.isEmpty() && d->currentProfile != 0){
         //Verify whether we have the last dialed number available
 
         QList<QVariant> listArguments = dbusMessage.arguments();
@@ -388,14 +293,14 @@ void CallRouter::sendNumberAsDTMFCode(const QDBusMessage& dbusMessage){
 
         if (audioConnected){
             // Now that the call to Calling card number is successful. We can send the original number as DTMF tones
-            QString strDTMFCode = convertToDTMFCode(strLastDialedNumber);
+            QString strDTMFCode = convertToDTMFCode(d->strLastDialedNumber);
 
             qDebug() << "Audio connection established. Sending DTMF code "<< strDTMFCode;
 
             QList<QVariant> argsToSend;
             argsToSend.append(strDTMFCode);
 
-            bool status = dbusUtility.sendMethodCall(CSD_SERVICE,
+            bool status = d->dbusUtility->sendMethodCall(CSD_SERVICE,
                                                      CSD_CALL_PATH,
                                                  CSD_CALL_INTERFACE,
                                                  QString("SendDTMF"),argsToSend);
@@ -403,7 +308,7 @@ void CallRouter::sendNumberAsDTMFCode(const QDBusMessage& dbusMessage){
             if (status){
                 QString strMessage = strDTMFCode.append(" sent as DTMF code");
                 qDebug() << strMessage;
-                dbusUtility.displayNotification(strMessage);
+                d->dbusUtility->displayNotification(strMessage);
             }
             else{
                 qDebug() << "Unable to send DTMF code.";
@@ -419,6 +324,9 @@ void CallRouter::sendNumberAsDTMFCode(const QDBusMessage& dbusMessage){
             qDebug() << "Now disconnecting from call status monitors..";
             stopCallStatusMonitors();
 
+            d->strLastDialedNumber.clear();
+            delete d->currentProfile;
+            d->currentProfile = 0;
         }
         else{
             qDebug() << "Audio not yet connected.";
@@ -434,16 +342,21 @@ QString CallRouter::convertToDTMFCode(QString strNumber){
     QString strDTMFCode;
 
     if (!strNumber.isEmpty()){
+        int intDTMFDelay = 1;
 
-        //Get the format required by calling card from coniguration
-        QString qstrDTMFFormat = gconfUtility->getGconfValueString("dtmf_format");
-        int intDTMFDelay = gconfUtility->getGconfValueInteger("dtmf_delay");
+        //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);        
 
-        if (intDTMFDelay <1 ) intDTMFDelay = 1;
-        if (qstrDTMFFormat.isEmpty()) qstrDTMFFormat = "<Country Code><Area Code><Phone Number>";
+        //Now check whether we need a prefix
+        QString strDTMFPrefix = d->currentProfile->dtmfPrefix;
 
-        //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);
+        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.
@@ -463,13 +376,12 @@ QString CallRouter::convertToDTMFCode(QString strNumber){
         else if (qstrDTMFFormat.startsWith("011")){
             strDTMFCode = strDTMFCode.append("011");
         }
-        //Default case - we don't need any prefix
 
         strDTMFCode = strDTMFCode.append(strNumber);
 
         //Now check whether we need a suffix
-        QString strDTMFSuffix = gconfUtility->getGconfValueString("dtmf_suffix");
-        if (!strDTMFSuffix.isEmpty() && !strDTMFSuffix.contains("--None--")){
+        QString strDTMFSuffix = d->currentProfile->dtmfSuffix;
+        if (!strDTMFSuffix.isEmpty()){
             strDTMFCode = strDTMFCode.append(strDTMFSuffix);
         }
     }
@@ -477,24 +389,33 @@ QString CallRouter::convertToDTMFCode(QString strNumber){
     return strDTMFCode;
 }
 
-bool CallRouter::isExcludedNumber(QString strInternationalNumber){
+//DBus Method used by external applications to check whether VICaR is enabled and running
+bool CallRouter::isRunning(){
 
-    bool isExcluded = false;
+    return true;
 
-    //Get the list of excluded codes
-    QString qstrExcludedNumbers = gconfUtility->getGconfValueString("numbers_to_exclude");
-    QStringList strExcludedCodeList = qstrExcludedNumbers.split(",");
-    QStringListIterator iterator(strExcludedCodeList);
+    //Verify Whether VICaR telepathy account is online
+    /*
+    if (d->tpUtility->getAccountStatus() == "Connected"){
+        return true;
+    }
+    else{
+        return false;
+    }
+    */
+}
 
-    QRegExp regexp = QRegExp("(^0{2})|[^0-9p]");
+//DBus Method used by external applications to call via VICaR
+QString CallRouter::callInternationalNumber(const QString& strDestinationNumber){
 
-    while (iterator.hasNext()){
-        QString strCode = iterator.next();
-        strCode = strCode.replace(regexp,"");
-        strInternationalNumber = strInternationalNumber.replace(regexp,"");
-        if (!strCode.isEmpty() && strInternationalNumber.startsWith(strCode)){
-            isExcluded = true;
-        }
+    qDebug() << "New call requested by external application. Destination number is " << strDestinationNumber;
+    QString strErrorMessage = this->callViaCallingCard(strDestinationNumber);
+    qDebug() << strErrorMessage;
+
+    if (strErrorMessage.isEmpty()){
+        return QString("Success");
     }
-    return isExcluded;
-}
+    else{
+        return strErrorMessage;
+    }
+ }