--- /dev/null
+/*
+@version: 0.2
+@author: Sudheer K. <scifi.guy@hotmail.com>
+@license: GNU General Public License
+*/
+
+#include "callrouter.h"
+#include <dbusutility.h>
+#include <gconfutility.h>
+#include <QDebug>
+#include <QRegExp>
+#include <QDBusConnection>
+#include <QTimer>
+#include <QStringListIterator>
+
+//Create a statis Dbus utility object that will be shared across all member functions
+static DbusUtility dbusUtility = DbusUtility();
+static QString strLastDialedNumber = QString();
+
+CallRouter::CallRouter(QObject *parent) :
+ QObject(parent)
+{
+ gconfUtility = new GConfUtility();
+}
+
+CallRouter::~CallRouter(){
+ delete gconfUtility;
+ gconfUtility = 0;
+}
+
+void CallRouter::registerDBusService(){
+ QDBusConnection connection = dbusUtility.getConnection();
+
+ if (!connection.registerService(APPLICATION_DBUS_SERVICE)) {
+ qDebug() << dbusUtility.getErrorMessage();
+ exit(1);
+ }
+
+ if (!connection.registerObject(APPLICATION_DBUS_PATH, this,
+ QDBusConnection::ExportScriptableSlots)) {
+ qDebug() << dbusUtility.getErrorMessage();
+ exit(2);
+ }
+
+ this->connectToDBusSignals();
+
+}
+
+
+void CallRouter::unregisterDBusService(){
+
+ this->disconnectFromDBusSignals();
+
+ QDBusConnection connection = dbusUtility.getConnection();
+
+ connection.unregisterObject(APPLICATION_DBUS_PATH,QDBusConnection::UnregisterTree);
+
+ if (!connection.unregisterService(APPLICATION_DBUS_SERVICE)) {
+ qDebug() << dbusUtility.getErrorMessage();
+ exit(3);
+ }
+
+}
+
+void CallRouter::connectToDBusSignals(){
+
+ QDBusConnection connection = dbusUtility.getConnection();
+
+ // 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());
+ }
+
+}
+
+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);
+ }
+ }
+ }
+}
+
+void CallRouter::callViaCallingCard(){
+ //Now call the calling card number. This is generally a local and/or tollfree number
+
+ QString strCallingCardNumber = gconfUtility->getGconfValueString("calling_card_number");
+
+ qDebug() << "Wait time elapsed. Initiating call to "<< strCallingCardNumber;
+
+ QList<QVariant> argsToSend;
+ argsToSend.append(strCallingCardNumber);
+ argsToSend.append(0);
+
+ bool status = dbusUtility.sendMethodCall(CSD_SERVICE,
+ CSD_CALL_PATH,
+ CSD_CALL_INTERFACE,
+ QString("CreateWith"),argsToSend);
+
+ 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();
+ }
+
+ dbusUtility.displayNotification(strUserMessage);
+
+}
+
+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 = 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() << "Successfully connected to Dbus signal AudioConnect in interface "<< CSD_CALL_INSTANCE_INTERFACE;
+ }
+ else{
+ qDebug() << "Failed to connect to Dbus signal AudioConnect in interface "<< CSD_CALL_INSTANCE_INTERFACE;
+ qDebug() <<"DBus Error: "<< 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() << "Successfully connected to Dbus signal Terminated in interface "<< CSD_CALL_INSTANCE_INTERFACE;
+ }
+ else{
+ qDebug() << "Failed to connect to Dbus signal Terminated in interface "<< CSD_CALL_INSTANCE_INTERFACE;
+ qDebug() <<"DBus Error: "<< 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() << "Successfully connected to Dbus signal Coming in interface" << CSD_CALL_INTERFACE;
+ }
+ else{
+ qDebug() << "Failed to connect to Dbus signal Coming in interface" << CSD_CALL_INTERFACE;
+ qDebug() <<"DBus Error: "<< dbusUtility.getErrorMessage();
+ }
+}
+
+void CallRouter::stopCallStatusMonitors(){
+
+ strLastDialedNumber.clear();
+
+ QDBusConnection connection = 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() << "Successfully disconnected from Dbus signal AudioConnect in interface "<< CSD_CALL_INSTANCE_INTERFACE;
+ }
+ else{
+ qDebug() << "Failed to disconnect from Dbus signal AudioConnect in interface "<< CSD_CALL_INSTANCE_INTERFACE;
+ qDebug() <<"DBus Error: "<< 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() << "Successfully disconnected from Dbus signal Terminated in interface "<< CSD_CALL_INSTANCE_INTERFACE;
+ }
+ else{
+ qDebug() << "Failed to disconnect from Dbus signal Terminated in interface "<< CSD_CALL_INSTANCE_INTERFACE;
+ qDebug() <<"DBus Error: "<< 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() << "Successfully disconnected from Dbus signal Coming in interface" << CSD_CALL_INTERFACE;
+ }
+ else{
+ qDebug() << "Failed to disconnect from Dbus signal Coming in interface" << CSD_CALL_INTERFACE;
+ qDebug() <<"DBus Error: "<< dbusUtility.getErrorMessage();
+ }
+}
+
+void CallRouter::sendNumberAsDTMFCode(const QDBusMessage& dbusMessage){
+
+ if (!strLastDialedNumber.isEmpty()){
+ //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(strLastDialedNumber);
+
+ qDebug() << "Audio connection established. Sending DTMF code "<< strDTMFCode;
+
+ QList<QVariant> argsToSend;
+ argsToSend.append(strDTMFCode);
+
+ bool status = dbusUtility.sendMethodCall(CSD_SERVICE,
+ CSD_CALL_PATH,
+ CSD_CALL_INTERFACE,
+ QString("SendDTMF"),argsToSend);
+
+ if (status){
+ QString strMessage = strDTMFCode.append(" sent as DTMF code");
+ qDebug() << strMessage;
+ dbusUtility.displayNotification(strMessage);
+ }
+ else{
+ qDebug() << "Unable to send 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() << "Now disconnecting from call status monitors..";
+ stopCallStatusMonitors();
+
+ }
+ else{
+ qDebug() << "Audio not yet connected.";
+ }
+ }
+ else
+ {
+ qDebug() << "Last dialed number is empty.";
+ }
+}
+
+QString CallRouter::convertToDTMFCode(QString strNumber){
+ QString strDTMFCode;
+
+ if (!strNumber.isEmpty()){
+
+ //Get the format required by calling card from coniguration
+ QString qstrDTMFFormat = gconfUtility->getGconfValueString("dtmf_format");
+ int intDTMFDelay = gconfUtility->getGconfValueInteger("dtmf_delay");
+
+ if (intDTMFDelay <1 ) intDTMFDelay = 1;
+ if (qstrDTMFFormat.isEmpty()) qstrDTMFFormat = "<Country Code><Area Code><Phone Number>";
+
+ //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);
+
+ /* 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");
+ }
+ //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--")){
+ strDTMFCode = strDTMFCode.append(strDTMFSuffix);
+ }
+ }
+
+ return strDTMFCode;
+}
+
+bool CallRouter::isExcludedNumber(QString strInternationalNumber){
+
+ bool isExcluded = false;
+
+ //Get the list of excluded codes
+ QString qstrExcludedNumbers = gconfUtility->getGconfValueString("numbers_to_exclude");
+ QStringList strExcludedCodeList = qstrExcludedNumbers.split(",");
+ QStringListIterator iterator(strExcludedCodeList);
+
+ QRegExp regexp = QRegExp("(^0{2})|[^0-9p]");
+
+ while (iterator.hasNext()){
+ QString strCode = iterator.next();
+ strCode = strCode.replace(regexp,"");
+ strInternationalNumber = strInternationalNumber.replace(regexp,"");
+ if (!strCode.isEmpty() && strInternationalNumber.startsWith(strCode)){
+ isExcluded = true;
+ }
+ }
+ return isExcluded;
+}