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 <gconfutility.h>
11 #include <databaseutility.h>
12 #include <telepathyutility.h>
15 #include <QDBusConnection>
16 #include <QDBusMessage>
17 #include <QStringListIterator>
20 //static QString strLastDialedNumber = QString();
21 //static org::maemo::vicar::Profile currentProfile;
23 class CallRouterPrivate
26 CallRouterPrivate(CallRouter * p) :
27 databaseUtility(new DatabaseUtility(p)),
28 dbusAdaptor(new VicarDbusAdaptor(p)),
29 dbusUtility(new DbusUtility(p)),
30 gconfUtility(new GConfUtility(p)),
31 tpUtility(new TelepathyUtility(p)),
34 Q_ASSERT(0 != dbusAdaptor);
35 //Do not open here - Unable to capture changes to DB if it is open too early and closed late.
36 //databaseUtility->openDatabase();
41 qDebug() << "VICaR: Call Router Destructing";
42 //databaseUtility->closeDatabase();
45 DatabaseUtility *databaseUtility;
46 VicarDbusAdaptor * dbusAdaptor;
47 DbusUtility * dbusUtility;
48 GConfUtility * gconfUtility;
49 TelepathyUtility *tpUtility;
50 QString strLastDialedNumber;
51 org::maemo::vicar::Profile *currentProfile;
52 CallRouter * const parent;
55 // ---------------------------------------------------------------------------
57 CallRouter::CallRouter(QObject *parent) :
59 d(new CallRouterPrivate(this))
62 this->registerDBusService();
63 qDebug() << "Registered DBus Service " << APPLICATION_DBUS_SERVICE;
66 CallRouter::~CallRouter(){
69 void CallRouter::registerDBusService(){
70 //Connect to Session Bus
71 QDBusConnection connection = d->dbusUtility->getConnection(false);
73 if (!connection.interface()->isServiceRegistered(APPLICATION_DBUS_SERVICE)){
75 if (!connection.registerService(APPLICATION_DBUS_SERVICE)) {
76 qDebug() << d->dbusUtility->getErrorMessage();
81 if (!connection.registerObject(APPLICATION_DBUS_PATH, this, QDBusConnection::ExportAdaptors)) {
82 qDebug() << d->dbusUtility->getErrorMessage();
89 void CallRouter::unregisterDBusService(){
91 //Disconnect from Session bus
92 QDBusConnection connection = d->dbusUtility->getConnection(false);
94 connection.unregisterObject(APPLICATION_DBUS_PATH,QDBusConnection::UnregisterTree);
96 if (!connection.unregisterService(APPLICATION_DBUS_SERVICE)) {
97 qDebug() << d->dbusUtility->getErrorMessage();
103 QString CallRouter::callViaCallingCard(QString strDestinationNumber){
105 d->currentProfile = new org::maemo::vicar::Profile();
106 d->currentProfile->profileID = 0;
108 d->databaseUtility->openDatabase();
109 bool result = d->databaseUtility->findProfileByNumber(strDestinationNumber,d->currentProfile);
111 QString strErrorMessage;
113 strErrorMessage = QString("Error finding VICaR profile. %1").arg(d->databaseUtility->lastError().text());
115 else if (d->currentProfile->profileID == 0){
116 bool routeOnDefault = d->gconfUtility->getGconfValueBoolean("route_on_default");
118 qDebug() << "Routing directly as per configuration";
119 this->placeCall(strDestinationNumber);
122 qDebug() << "No profile found. Stopping..";
123 strErrorMessage = "VICaR: No routing profile defined for this number.";
124 d->dbusUtility->displayNotification(strErrorMessage );
128 //Now call the calling card number. This is generally a local and/or tollfree number
129 QString strCallingCardNumber = d->currentProfile->gatewayNumber;
130 qDebug() << "Initiating call to "<< strCallingCardNumber;
131 bool status = this->placeCall(strCallingCardNumber);
132 d->strLastDialedNumber = strDestinationNumber;
134 QString strUserMessage;
137 qDebug() << "Call initiated successfully. Connecting DBus slot for audio connection monitor";
138 startCallStatusMonitors();
141 strUserMessage = QString("Unable to initiate new call to ").append(strCallingCardNumber);
142 strErrorMessage = d->dbusUtility->getErrorMessage();
143 qDebug() << strErrorMessage;
144 d->strLastDialedNumber.clear();
145 delete d->currentProfile;
146 d->currentProfile = 0;
148 d->dbusUtility->displayNotification(strUserMessage);
151 d->databaseUtility->closeDatabase();
152 return strErrorMessage;
155 bool CallRouter::placeCall(QString number){
157 QList<QVariant> argsToSend;
158 argsToSend.append(number);
159 argsToSend.append(0);
161 bool status = d->dbusUtility->sendMethodCall(CSD_SERVICE,
164 QString("CreateWith"),argsToSend);
169 void CallRouter::startCallStatusMonitors(){
170 /* Declare the slot to be executed when a call is picked up by other party (Audio connection established).
171 We need this to confirm whether a call went though successfully.
174 QDBusConnection connection = d->dbusUtility->getConnection();
176 bool success = connection.connect(QString(""),
177 CSD_CALL_INSTANCE_PATH,
178 CSD_CALL_INSTANCE_INTERFACE,
179 QString("AudioConnect"),this,
180 SLOT(sendNumberAsDTMFCode(const QDBusMessage&)));
183 qDebug() << "Successfully connected to Dbus signal AudioConnect in interface "<< CSD_CALL_INSTANCE_INTERFACE;
186 qDebug() << "Failed to connect to Dbus signal AudioConnect in interface "<< CSD_CALL_INSTANCE_INTERFACE;
187 qDebug() <<"DBus Error: "<< d->dbusUtility->getErrorMessage();
190 /* Declare the slot to be executed when the call is terminated (due to connection errors etc).
191 We need this to avoid sending DTMF code on wrong calls.
194 success = connection.connect(QString(""),
195 CSD_CALL_INSTANCE_PATH,
196 CSD_CALL_INSTANCE_INTERFACE,
197 QString("Terminated"),this,
198 SLOT(stopCallStatusMonitors()));
201 qDebug() << "Successfully connected to Dbus signal Terminated in interface "<< CSD_CALL_INSTANCE_INTERFACE;
204 qDebug() << "Failed to connect to Dbus signal Terminated in interface "<< CSD_CALL_INSTANCE_INTERFACE;
205 qDebug() <<"DBus Error: "<< d->dbusUtility->getErrorMessage();
208 /* Declare the slot to be executed when a call is received
209 (before we can place the call to calling card number).
210 It is extremely rare that somebody should get a call within these few seconds.
211 In any case, we need this to avoid sending DTMF code on the received call.
213 Btw - I don't care for the incoming number here. If anyone is calling the user before we can send DTMF code,
214 then we stop sending the DTMF code even if user does not respond to the call.
217 success = connection.connect(QString(""),
220 QString("Coming"),this,
221 SLOT(stopCallStatusMonitors()));
224 qDebug() << "Successfully connected to Dbus signal Coming in interface" << CSD_CALL_INTERFACE;
227 qDebug() << "Failed to connect to Dbus signal Coming in interface" << CSD_CALL_INTERFACE;
228 qDebug() <<"DBus Error: "<< d->dbusUtility->getErrorMessage();
232 void CallRouter::stopCallStatusMonitors(){
234 d->strLastDialedNumber.clear();
235 delete d->currentProfile;
236 d->currentProfile = 0;
238 QDBusConnection connection = d->dbusUtility->getConnection();
240 // Disconnect the slot for audio connection status
241 bool status = connection.disconnect(QString(""),
242 CSD_CALL_INSTANCE_PATH,
243 CSD_CALL_INSTANCE_INTERFACE,
244 QString("AudioConnect"),this,
245 SLOT(sendNumberAsDTMFCode(const QDBusMessage&)));
248 qDebug() << "Successfully disconnected from Dbus signal AudioConnect in interface "<< CSD_CALL_INSTANCE_INTERFACE;
251 qDebug() << "Failed to disconnect from Dbus signal AudioConnect in interface "<< CSD_CALL_INSTANCE_INTERFACE;
252 qDebug() <<"DBus Error: "<< d->dbusUtility->getErrorMessage();
255 // Disconnect the slot for monitoring terminated calls
256 status = connection.disconnect(QString(""),
257 CSD_CALL_INSTANCE_PATH,
258 CSD_CALL_INSTANCE_INTERFACE,
259 QString("Terminated"),this,
260 SLOT(stopCallStatusMonitors()));
263 qDebug() << "Successfully disconnected from Dbus signal Terminated in interface "<< CSD_CALL_INSTANCE_INTERFACE;
266 qDebug() << "Failed to disconnect from Dbus signal Terminated in interface "<< CSD_CALL_INSTANCE_INTERFACE;
267 qDebug() <<"DBus Error: "<< d->dbusUtility->getErrorMessage();
270 // Disconnect the slot for monitoring incoming calls
271 status = connection.disconnect(QString(""),
274 QString("Coming"),this,
275 SLOT(stopCallStatusMonitors()));
278 qDebug() << "Successfully disconnected from Dbus signal Coming in interface" << CSD_CALL_INTERFACE;
281 qDebug() << "Failed to disconnect from Dbus signal Coming in interface" << CSD_CALL_INTERFACE;
282 qDebug() <<"DBus Error: "<< d->dbusUtility->getErrorMessage();
286 void CallRouter::sendNumberAsDTMFCode(const QDBusMessage& dbusMessage){
288 if (!d->strLastDialedNumber.isEmpty() && d->currentProfile != 0){
289 //Verify whether we have the last dialed number available
291 QList<QVariant> listArguments = dbusMessage.arguments();
292 bool audioConnected = listArguments.first().toBool();
295 // Now that the call to Calling card number is successful. We can send the original number as DTMF tones
296 QString strDTMFCode = convertToDTMFCode(d->strLastDialedNumber);
298 qDebug() << "Audio connection established. Sending DTMF code "<< strDTMFCode;
300 QList<QVariant> argsToSend;
301 argsToSend.append(strDTMFCode);
303 bool status = d->dbusUtility->sendMethodCall(CSD_SERVICE,
306 QString("SendDTMF"),argsToSend);
309 QString strMessage = strDTMFCode.append(" sent as DTMF code");
310 qDebug() << strMessage;
311 d->dbusUtility->displayNotification(strMessage);
314 qDebug() << "Unable to send DTMF code.";
319 Connecting and Disconnecting from/to DBus signal for each international call
320 may not be the most efficient way of handling this. But we need to make sure
321 that the DTMF codes are sent only for the calls placed by this app (i.e calls to Calling card number).
324 qDebug() << "Now disconnecting from call status monitors..";
325 stopCallStatusMonitors();
327 d->strLastDialedNumber.clear();
328 delete d->currentProfile;
329 d->currentProfile = 0;
332 qDebug() << "Audio not yet connected.";
337 qDebug() << "Last dialed number is empty.";
341 QString CallRouter::convertToDTMFCode(QString strNumber){
344 if (!strNumber.isEmpty()){
345 int intDTMFDelay = 1;
347 //Add the prefix p so that there is some delay after the call is picked up by the automated system to send DTMF tones.
348 strDTMFCode = QString("").fill('p',intDTMFDelay);
350 //Now check whether we need a prefix
351 QString strDTMFPrefix = d->currentProfile->dtmfPrefix;
353 if (!strDTMFPrefix.isEmpty()){
354 strDTMFCode = strDTMFCode.append(strDTMFPrefix);
357 //Get the format required by calling card from coniguration
358 QString qstrDTMFFormat = d->currentProfile->dtmfFormat;
359 if (qstrDTMFFormat.isEmpty()) qstrDTMFFormat = "<Country Code><Area Code><Phone Number>";
361 /* Replace 00 (international dialing code) at the beginning
362 and also replace any character other than the numbers 0-9 and p.
364 QRegExp regexp = QRegExp("(^0{2})|[^0-9p]");
365 strNumber = strNumber.replace(regexp,"");
367 /* Now we have a clean number with only country code, area code and phone number,
368 lets convert it to the calling card friendly format
370 if (qstrDTMFFormat.startsWith("+")){
371 strDTMFCode = strDTMFCode.append("+");
373 else if (qstrDTMFFormat.startsWith("00")){
374 strDTMFCode = strDTMFCode.append("00");
376 else if (qstrDTMFFormat.startsWith("011")){
377 strDTMFCode = strDTMFCode.append("011");
380 strDTMFCode = strDTMFCode.append(strNumber);
382 //Now check whether we need a suffix
383 QString strDTMFSuffix = d->currentProfile->dtmfSuffix;
384 if (!strDTMFSuffix.isEmpty()){
385 strDTMFCode = strDTMFCode.append(strDTMFSuffix);
392 //DBus Method used by external applications to check whether VICaR is enabled and running
393 bool CallRouter::isRunning(){
397 //Verify Whether VICaR telepathy account is online
399 if (d->tpUtility->getAccountStatus() == "Connected"){
408 //DBus Method used by external applications to call via VICaR
409 QString CallRouter::callInternationalNumber(const QString& strDestinationNumber){
411 qDebug() << "New call requested by external application. Destination number is " << strDestinationNumber;
412 QString strErrorMessage = this->callViaCallingCard(strDestinationNumber);
413 qDebug() << strErrorMessage;
415 if (strErrorMessage.isEmpty()){
416 return QString("Success");
419 return strErrorMessage;