71b1fa7493f6a8ea560d4336aea03fce1737fa15
[vicar] / src / vicar-daemon / src / callrouter.cpp
1 /*
2 @version: 0.5
3 @author: Sudheer K. <scifi1947 at gmail.com>
4 @license: GNU General Public License
5 */
6
7 #include "callrouter.h"
8 #include "vicardbusadaptor.h"
9 #include <dbusutility.h>
10 #include <gconfutility.h>
11 #include <databaseutility.h>
12 #include <telepathyutility.h>
13 #include <QDebug>
14 #include <QRegExp>
15 #include <QDBusConnection>
16 #include <QDBusMessage>
17 #include <QStringListIterator>
18
19
20 //static QString strLastDialedNumber = QString();
21 //static org::maemo::vicar::Profile currentProfile;
22
23 class CallRouterPrivate
24 {
25 public:
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)),
32         parent(p)
33     {
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();
37     }
38
39     ~CallRouterPrivate()
40     {
41         qDebug() << "VICaR: Call Router Destructing";
42         //databaseUtility->closeDatabase();
43     }
44
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;
53 };
54
55 // ---------------------------------------------------------------------------
56
57 CallRouter::CallRouter(QObject *parent) :
58     QObject(parent),
59     d(new CallRouterPrivate(this))
60 {
61         Q_ASSERT(0 != d);
62         this->registerDBusService();
63         qDebug() << "Vicar-Daemon: Registered DBus Service " << APPLICATION_DBUS_SERVICE;
64 }
65
66 CallRouter::~CallRouter(){
67 }
68
69 void CallRouter::registerDBusService(){
70     //Connect to Session Bus
71     QDBusConnection connection = d->dbusUtility->getConnection(false);
72
73     if (!connection.interface()->isServiceRegistered(APPLICATION_DBUS_SERVICE)){
74
75         if (!connection.registerService(APPLICATION_DBUS_SERVICE)) {
76             qDebug() << "Vicar-Daemon: " << d->dbusUtility->getErrorMessage();
77             exit(1);
78         }
79     }
80
81     if (!connection.registerObject(APPLICATION_DBUS_PATH, this, QDBusConnection::ExportAdaptors)) {
82         qDebug() << "Vicar-Daemon: " << d->dbusUtility->getErrorMessage();
83         exit(2);
84     }
85
86 }
87
88
89 void CallRouter::unregisterDBusService(){
90
91     //Disconnect from Session bus
92     QDBusConnection connection = d->dbusUtility->getConnection(false);
93
94     connection.unregisterObject(APPLICATION_DBUS_PATH,QDBusConnection::UnregisterTree);
95
96     if (!connection.unregisterService(APPLICATION_DBUS_SERVICE)) {
97         qDebug() << "Vicar-Daemon: " << d->dbusUtility->getErrorMessage();
98         exit(3);
99     }
100
101 }
102
103 QString CallRouter::callViaCallingCard(QString strDestinationNumber){
104
105         d->currentProfile = new org::maemo::vicar::Profile();
106         d->currentProfile->profileID = 0;
107
108         d->databaseUtility->openDatabase();
109         bool result = d->databaseUtility->findProfileByNumber(strDestinationNumber,d->currentProfile);
110
111         QString strErrorMessage;
112         if (!result){
113             strErrorMessage = QString("Vicar-Daemon: Error finding VICaR profile. %1").arg(d->databaseUtility->lastError().text());
114         }
115         else if (d->currentProfile->profileID == 0){
116             bool routeOnDefault = d->gconfUtility->getGconfValueBoolean("route_on_default");
117             if (routeOnDefault){
118                 qDebug() << "Vicar-Daemon: Routing directly as per configuration";
119                this->placeCall(strDestinationNumber);
120             }
121             else{
122                 qDebug() << "Vicar-Daemon: No profile found. Stopping..";
123                 strErrorMessage  = "Vicar: No routing profile defined for this number.";
124                 d->dbusUtility->displayNotification(strErrorMessage );
125             }
126         }
127         else{
128             //Now call the calling card number. This is generally a local and/or tollfree number
129             QString strCallingCardNumber = d->currentProfile->gatewayNumber;
130             qDebug() << "Vicar-Daemon: Initiating call to "<< strCallingCardNumber;
131             bool status = this->placeCall(strCallingCardNumber);
132             d->strLastDialedNumber = strDestinationNumber;
133
134             QString strUserMessage;
135
136             if (status){
137                 qDebug() << "Vicar-Daemon: Call initiated successfully. Connecting DBus slot for audio connection monitor";
138                  startCallStatusMonitors();
139             }
140             else {
141                 strUserMessage = QString("Unable to initiate new call to ").append(strCallingCardNumber);
142                 strErrorMessage = d->dbusUtility->getErrorMessage();
143                 qDebug() << "Vicar-Daemon: " << strErrorMessage;
144                 d->strLastDialedNumber.clear();
145                 delete d->currentProfile;
146                 d->currentProfile = 0;
147             }
148             d->dbusUtility->displayNotification(strUserMessage);
149         }
150
151         d->databaseUtility->closeDatabase();
152         return strErrorMessage;
153 }
154
155 bool CallRouter::placeCall(QString number){
156
157     QList<QVariant> argsToSend;
158     argsToSend.append(number);
159     argsToSend.append(0);
160
161     bool status = d->dbusUtility->sendMethodCall(CSD_SERVICE,
162                                              CSD_CALL_PATH,
163                                          CSD_CALL_INTERFACE,
164                                          QString("CreateWith"),argsToSend);
165     return status;
166
167 }
168
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.
172     */
173
174     QDBusConnection connection = d->dbusUtility->getConnection();
175
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&)));
181
182     if (success){
183         qDebug() << "Vicar-Daemon: Successfully connected to Dbus signal AudioConnect in interface "<< CSD_CALL_INSTANCE_INTERFACE;
184     }
185     else{
186         qDebug() << "Vicar-Daemon: Failed to connect to Dbus signal AudioConnect in interface "<< CSD_CALL_INSTANCE_INTERFACE;
187         qDebug() <<"Vicar-Daemon: DBus Error: "<< d->dbusUtility->getErrorMessage();
188     }
189
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.
192     */
193
194     success = connection.connect(QString(""),
195                                CSD_CALL_INSTANCE_PATH,
196                                CSD_CALL_INSTANCE_INTERFACE,
197                                QString("Terminated"),this,
198                                SLOT(stopCallStatusMonitors()));
199
200     if (success){
201         qDebug() << "Vicar-Daemon: Successfully connected to Dbus signal Terminated in interface "<< CSD_CALL_INSTANCE_INTERFACE;
202     }
203     else{
204         qDebug() << "Vicar-Daemon: Failed to connect to Dbus signal Terminated in interface "<< CSD_CALL_INSTANCE_INTERFACE;
205         qDebug() <<"Vicar-Daemon: DBus Error: "<< d->dbusUtility->getErrorMessage();
206     }
207
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.
212
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.
215     */
216
217     success = connection.connect(QString(""),
218                                CSD_CALL_PATH,
219                                CSD_CALL_INTERFACE,
220                                QString("Coming"),this,
221                                SLOT(stopCallStatusMonitors()));
222
223     if (success){
224         qDebug() << "Vicar-Daemon: Successfully connected to Dbus signal Coming in interface" << CSD_CALL_INTERFACE;
225     }
226     else{
227         qDebug() << "Vicar-Daemon: Failed to connect to Dbus signal Coming in interface" << CSD_CALL_INTERFACE;
228         qDebug() <<"Vicar-Daemon: DBus Error: "<< d->dbusUtility->getErrorMessage();
229     }
230 }
231
232 void CallRouter::stopCallStatusMonitors(){
233
234     d->strLastDialedNumber.clear();
235     delete d->currentProfile;
236     d->currentProfile = 0;
237
238     QDBusConnection connection = d->dbusUtility->getConnection();
239
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&)));
246
247     if (status){
248         qDebug() << "Vicar-Daemon: Successfully disconnected from Dbus signal AudioConnect in interface "<< CSD_CALL_INSTANCE_INTERFACE;
249     }
250     else{
251         qDebug() << "Vicar-Daemon: Failed to disconnect from Dbus signal AudioConnect in interface "<< CSD_CALL_INSTANCE_INTERFACE;
252         qDebug() <<"Vicar-Daemon: DBus Error: "<< d->dbusUtility->getErrorMessage();
253     }
254
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()));
261
262     if (status){
263         qDebug() << "Vicar-Daemon: Successfully disconnected from Dbus signal Terminated in interface "<< CSD_CALL_INSTANCE_INTERFACE;
264     }
265     else{
266         qDebug() << "Vicar-Daemon: Failed to disconnect from Dbus signal Terminated in interface "<< CSD_CALL_INSTANCE_INTERFACE;
267         qDebug() <<"Vicar-Daemon: DBus Error: "<< d->dbusUtility->getErrorMessage();
268     }
269
270     // Disconnect the slot for monitoring incoming calls
271     status = connection.disconnect(QString(""),
272                                    CSD_CALL_PATH,
273                                    CSD_CALL_INTERFACE,
274                                    QString("Coming"),this,
275                                    SLOT(stopCallStatusMonitors()));
276
277     if (status){
278         qDebug() << "Vicar-Daemon: Successfully disconnected from Dbus signal Coming in interface" << CSD_CALL_INTERFACE;
279     }
280     else{
281         qDebug() << "Vicar-Daemon: Failed to disconnect from Dbus signal Coming in interface" << CSD_CALL_INTERFACE;
282         qDebug() <<"Vicar-Daemon: DBus Error: "<< d->dbusUtility->getErrorMessage();
283     }
284 }
285
286 void CallRouter::sendNumberAsDTMFCode(const QDBusMessage& dbusMessage){
287
288     if (!d->strLastDialedNumber.isEmpty() && d->currentProfile != 0){
289         //Verify whether we have the last dialed number available
290
291         QList<QVariant> listArguments = dbusMessage.arguments();
292         bool audioConnected =  listArguments.first().toBool();
293
294         if (audioConnected){
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);
297
298             qDebug() << "Vicar-Daemon: Audio connection established. Sending DTMF code "<< strDTMFCode;
299
300             QList<QVariant> argsToSend;
301             argsToSend.append(strDTMFCode);
302
303             bool status = d->dbusUtility->sendMethodCall(CSD_SERVICE,
304                                                      CSD_CALL_PATH,
305                                                  CSD_CALL_INTERFACE,
306                                                  QString("SendDTMF"),argsToSend);
307
308             if (status){
309                 QString strMessage = strDTMFCode.append(" sent as DTMF code");
310                 qDebug() << strMessage;
311                 d->dbusUtility->displayNotification(strMessage);
312             }
313             else{
314                 qDebug() << "Vicar-Daemon: Unable to send DTMF code.";
315             }
316
317
318             /*
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).
322              */
323
324             qDebug() << "Vicar-Daemon: Now disconnecting from call status monitors..";
325             stopCallStatusMonitors();
326
327             d->strLastDialedNumber.clear();
328             delete d->currentProfile;
329             d->currentProfile = 0;
330         }
331         else{
332             qDebug() << "Vicar-Daemon: Audio not yet connected.";
333         }
334     }
335     else
336     {
337         qDebug() << "Vicar-Daemon: Last dialed number is empty.";
338     }
339 }
340
341 QString CallRouter::convertToDTMFCode(QString strNumber){
342     QString strDTMFCode;
343
344     if (!strNumber.isEmpty()){
345         int intDTMFDelay = 1;
346
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);        
349
350         //Now check whether we need a prefix
351         QString strDTMFPrefix = d->currentProfile->dtmfPrefix;
352
353         if (!strDTMFPrefix.isEmpty()){
354             strDTMFCode = strDTMFCode.append(strDTMFPrefix);
355         }
356
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>";
360
361         /* Replace 00 (international dialing code) at the beginning
362            and also replace any character other than the numbers 0-9 and p.
363            */
364         QRegExp regexp = QRegExp("(^0{2})|[^0-9p]");        
365         strNumber = strNumber.replace(regexp,"");                
366
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
369            */
370         if (qstrDTMFFormat.startsWith("+")){
371             strDTMFCode = strDTMFCode.append("+");
372         }
373         else if (qstrDTMFFormat.startsWith("00")){
374             strDTMFCode = strDTMFCode.append("00");
375         }
376         else if (qstrDTMFFormat.startsWith("011")){
377             strDTMFCode = strDTMFCode.append("011");
378         }
379
380         strDTMFCode = strDTMFCode.append(strNumber);
381
382         //Now check whether we need a suffix
383         QString strDTMFSuffix = d->currentProfile->dtmfSuffix;
384         if (!strDTMFSuffix.isEmpty()){
385             strDTMFCode = strDTMFCode.append(strDTMFSuffix);
386         }
387     }
388
389     return strDTMFCode;
390 }
391
392 //DBus Method used by external applications to check whether VICaR is enabled and running
393 bool CallRouter::isRunning(){
394
395     return true;
396
397     //Verify Whether VICaR telepathy account is online
398     /*
399     if (d->tpUtility->getAccountStatus() == "Connected"){
400         return true;
401     }
402     else{
403         return false;
404     }
405     */
406 }
407
408 //DBus Method used by external applications to call via VICaR
409 QString CallRouter::callInternationalNumber(const QString& strDestinationNumber){
410
411     QString strErrorMessage;
412
413     qDebug() << "Vicar-Daemon: New call requested by external application. Destination number is " << strDestinationNumber;
414
415     if (isValidPhoneNumber(strDestinationNumber)){
416
417         strErrorMessage = this->callViaCallingCard(strDestinationNumber);
418     }
419     else{
420         strErrorMessage = QString("Vicar-Daemon: %1 is not a valid number").arg(strDestinationNumber);
421     }
422
423     qDebug() << strErrorMessage;
424
425     if (strErrorMessage.isEmpty()){
426         return QString("Success");
427     }
428     else{
429         return strErrorMessage;
430     }
431  }
432
433 //Check whether a string is valid phone number
434 bool CallRouter::isValidPhoneNumber(QString strPhoneNumber){
435
436
437 /* Remove all dialble characters. The resulting string should be a valid number */    
438     QRegExp regexp = QRegExp("[p+*#]");
439
440     strPhoneNumber = strPhoneNumber.replace(regexp,"");
441
442     qDebug() << "Vicar Daemon: Cleaned up phone number is " << strPhoneNumber;
443
444 /* Now remove all digits, the resulting string should be empty, then it is a valid number */
445     regexp = QRegExp("[0-9]");
446
447     strPhoneNumber = strPhoneNumber.replace(regexp,"");
448
449     bool isNumber = strPhoneNumber.isEmpty();
450     return isNumber;
451 }