Added option to skip the leading zero of an incoming call.
[jenirok] / src / daemon / calllistener.cpp
1 /*
2  * This file is part of Jenirok.
3  *
4  * Jenirok is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * Jenirok is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with Jenirok.  If not, see <http://www.gnu.org/licenses/>.
16  *
17  */
18
19 #include <QtCore/QDebug>
20 #include <QtCore/QTimer>
21 #include <QtCore/QDateTime>
22 #include "calllistener.h"
23 #include "settings.h"
24 #include "cache.h"
25 #include "contactmanager.h"
26 #include "connectionmanager.h"
27 #include "sourcecoreconfig.h"
28 #include "db.h"
29
30 namespace
31 {
32     const QString CALL_SERVICE_NAME = "com.nokia.csd";
33     const QString CALL_SERVICE_PATH = "/com/nokia/csd/call";
34     const QString CALL_SERVICE_INTERFACE = "com.nokia.csd.Call";
35     const QString CALL_SERVICE_INSTANCE_NAME = "com.nokia.csd.Call.Instance";
36     const QString CALL_SIGNAL_INCOMING = "Coming";
37     const QString CALL_SIGNAL_RELEASE = "Release";
38     const QString CALL_SIGNAL_TERMINATED = "Terminated";
39     const QString CALL_SIGNAL_ANSWERED = "AudioConnect";
40 }
41
42 QDBusConnection CallListener::systemBus_ = QDBusConnection::systemBus();
43
44 CallListener::CallListener(): source_(0),
45 closeConnection_(false), initialized_(false), box_(0), label_(0),
46 retries_(-1), timer_(0), currentCall_(0), ignoreFirstZero_(false)
47 {
48 }
49
50 CallListener::~CallListener()
51 {
52     end();
53     DB::removeDatabase();
54 }
55
56 bool CallListener::begin()
57 {
58     if(Settings::instance()->getConnectionType() == Settings::ALWAYS_ASK)
59     {
60         qDebug() << "Bad connection settings, unable to start";
61         return false;
62     }
63
64     sourceId_ = Source::stringToId(Settings::instance()->get("source"));
65     QMap<QString, QString> tmpConfig;
66     SourceCoreConfig* config = SourceCoreConfig::getCoreConfig(sourceId_);
67     config->getConfig(tmpConfig);
68     sourceConfig_ = tmpConfig;
69     delete config;
70
71     ignoreFirstZero_ = (Settings::instance()->get("ignore_first_zero") == "1");
72
73     systemBus_.connect(CALL_SERVICE_NAME,
74                        CALL_SERVICE_PATH,
75                        CALL_SERVICE_INTERFACE,
76                        CALL_SIGNAL_INCOMING,
77                        this,
78                        SLOT(incomingCall(QDBusObjectPath, QString)));
79
80     systemBus_.connect(CALL_SERVICE_NAME,
81                        CALL_SERVICE_PATH,
82                        CALL_SERVICE_INTERFACE,
83                        CALL_SIGNAL_RELEASE,
84                        this,
85                        SLOT(callTerminate()));
86
87     qDebug() << "Starting...";
88
89     return true;
90
91 }
92
93 void CallListener::end()
94 {
95     systemBus_.disconnect(CALL_SERVICE_NAME,
96                           CALL_SERVICE_PATH,
97                           CALL_SERVICE_INTERFACE,
98                           CALL_SIGNAL_INCOMING,
99                           this,
100                           SLOT(incomingCall(QDBusObjectPath, QString)));
101
102     systemBus_.disconnect(CALL_SERVICE_NAME,
103                           CALL_SERVICE_PATH,
104                           CALL_SERVICE_INTERFACE,
105                           CALL_SIGNAL_RELEASE,
106                           this,
107                           SLOT(callTerminate()));
108
109     searchClose();
110     sourceConfig_.clear();
111
112 }
113
114 void CallListener::search(Source::SearchDetails const& details)
115 {
116     if(currentCall_)
117     {
118         delete currentCall_;
119     }
120
121     currentCall_ = new CallDetails;
122     currentCall_->number = details.query;
123     currentCall_->time = QDateTime::currentDateTime().toTime_t();
124     currentCall_->answered = false;
125
126     searchInit();
127
128     Source::Result result;
129
130     if(Cache::instance().findItem(details.query, result))
131     {
132
133         qDebug() << "Found from cache";
134
135         showDelayedResult(createResult(result.name,
136                                        result.street,
137                                        result.city), BANNER_DELAY);
138
139         currentCall_->result = result;
140     }
141     else
142     {
143         retries_ = 0;
144
145         if(!handleConnection())
146         {
147             qDebug() << "Unable to connect";
148             return;
149         }
150
151         showDelayedResult(tr("Searching..."), BANNER_DELAY);
152
153         source_->search(details);
154     }
155
156 }
157
158 void CallListener::requestFinished(QVector <Source::Result> const& results,
159                                    Source::SearchDetails const& details,
160                                    bool error)
161 {
162     /*if(closeConnection_)
163     {
164         closeConnection_ = false;
165         ConnectionManager cm;
166         cm.disconnect(true);
167     }*/
168
169     qDebug() << "Request finished";
170
171     // If box is not visible, the call must have been terminated already
172     if(!initialized_ || !box_->isVisible() || !currentCall_)
173     {
174         return;
175     }
176
177     QString message;
178
179     if(error)
180     {
181         qDebug() << "Error: " << source_->errorString();
182
183         if(retries_ < SEARCH_RETRIES && retries_ >= 0)
184         {
185             retries_++;
186             source_->search(Source::SearchDetails(currentCall_->number, "", Source::BOTH));
187             return;
188         }
189         else
190         {
191             timedMessage_ = "";
192             QString errorString;
193             Source::Error error = source_->error();
194
195             switch(error)
196             {
197             case Source::TIMEOUT:
198                 errorString = tr("Request timed out");
199                 break;
200             default:
201                 errorString = source_->errorString();
202                 break;
203             }
204
205             showError(tr("Searching failed:") + " " + errorString + ".");
206         }
207     }
208     else
209     {
210         timedMessage_ = "";
211
212         if(results.size() == 0)
213         {
214             message = tr("Phone number was not found");
215             showResult(message);
216         }
217         else
218         {
219             message = createResult(results.at(0).name, results.at(0).street,
220                                    results.at(0).city);
221             showResult(message);
222             Source::Result result = results.at(0);
223             result.number = details.query;
224             Cache::instance().addItem(result);
225             currentCall_->result = results.at(0);
226         }
227     }
228
229     retries_ = -1;
230 }
231
232 QString CallListener::createResult(QString const& name, QString const& street, QString const& city)
233 {
234     QString result = "<b>" + name + "</b>";
235
236     if(!street.isEmpty() || !city.isEmpty())
237     {
238         result += "<br>";
239
240         if(!street.isEmpty())
241         {
242             result += street + ", ";
243         }
244
245         result += city;
246     }
247
248     return result;
249 }
250
251 void CallListener::showResult(QString const& text)
252 {
253     if(!initialized_)
254     {
255         return;
256     }
257
258     label_->setText("<font color='black'>" + text + "</font>");
259
260     if(box_->isVisible())
261     {
262         box_->hide();
263     }
264
265     box_->show();
266 }
267
268 void CallListener::incomingCall(QDBusObjectPath path, QString number)
269 {
270     if(number.isEmpty())
271     {
272         qDebug() << "Unknown caller without number";
273         return;
274     }
275
276     // If the call has come through some kind of switch board
277     // there might be a leading zero added
278     if(ignoreFirstZero_)
279     {
280         number = number.replace(QRegExp("^00"), "0");
281     }
282
283     ContactManager cm;
284
285     if(!cm.numberExists(number))
286     {
287         qDebug() << "Number doesn't exist: " << number;
288
289         systemBus_.connect(CALL_SERVICE_NAME,
290                            path.path(),
291                            CALL_SERVICE_INSTANCE_NAME,
292                            CALL_SIGNAL_TERMINATED,
293                            this,
294                            SLOT(callTerminate()));
295
296         systemBus_.connect(CALL_SERVICE_NAME,
297                            path.path(),
298                            CALL_SERVICE_INSTANCE_NAME,
299                            CALL_SIGNAL_ANSWERED,
300                            this,
301                            SLOT(handleAnswer()));
302
303         search(Source::SearchDetails(number, "", Source::BOTH));
304     }
305     else
306     {
307         qDebug() << "Number exists: " << number;
308     }
309 }
310
311 void CallListener::callTerminate()
312 {
313     if(box_ && box_->isVisible())
314     {
315         box_->hide();
316     }
317
318     if(currentCall_)
319     {
320         currentCall_->result.number = currentCall_->number;
321         Cache::instance().logItem(currentCall_->result, !currentCall_->answered, currentCall_->time);
322         delete currentCall_;
323         currentCall_ = 0;
324     }
325
326     searchClose();
327 }
328
329 void CallListener::handleAnswer()
330 {
331     qDebug() << "Answered";
332
333     if(currentCall_)
334     {
335         currentCall_->answered = true;
336     }
337 }
338
339 void CallListener::showDelayedResult(QString const& text, int delay)
340 {
341     timedMessage_ = text;
342     QTimer::singleShot(delay, this, SLOT(showTimedMessage()));
343 }
344
345 void CallListener::showTimedMessage()
346 {
347     if(timedMessage_.size() == 0 || !initialized_)
348     {
349         return;
350     }
351
352     showResult(timedMessage_);
353
354     timedMessage_ = "";
355 }
356
357 void CallListener::searchInit()
358 {
359     qDebug() << "Initializing search...";
360
361     if(initialized_)
362     {
363         qDebug() << "Already initialized";
364         return;
365     }
366
367     source_ = Source::getSource(sourceId_);
368     SourceCoreConfig* config = SourceCoreConfig::getCoreConfig(sourceId_);
369     config->loadFromConfig(sourceConfig_);
370     config->apply(source_);
371     delete config;
372     source_->setMaxResults(1);
373     source_->setFindNumber(false);
374     source_->setTimeout(REQUEST_TIMEOUT);
375
376     connect(source_, SIGNAL(requestFinished(QVector <Source::Result> const&,
377                                            Source::SearchDetails const&, bool)),
378                                            this, SLOT(requestFinished(QVector <Source::Result> const&,
379                                                                       Source::SearchDetails const&, bool)));
380     box_ = new InformationBox;
381     label_ = new QLabel("", box_);
382     label_->setMargin(8);
383     label_->setWordWrap(true);
384     box_->setWidget(label_);
385     initialized_ = true;
386 }
387
388 void CallListener::searchClose()
389 {
390     if(!initialized_)
391     {
392         return;
393     }
394
395     initialized_ = false;
396
397     qDebug() << "Closing search...";
398
399     if(source_)
400     {
401         disconnect(source_, SIGNAL(requestFinished(QVector <Source::Result> const&,
402                                                   Source::SearchDetails const&, bool)),
403                                                   this, SLOT(requestFinished(QVector <Source::Result> const&,
404                                                                              Source::SearchDetails const&, bool)));
405     }
406
407     delete source_;
408     source_ = 0;
409     delete box_;
410     box_ = 0;
411     label_ = 0;
412
413     if(closeConnection_)
414     {
415         QTimer::singleShot(500, this, SLOT(closeConnection()));
416     }
417 }
418
419 void CallListener::closeConnection()
420 {
421     if(closeConnection_)
422     {
423         closeConnection_ = false;
424         ConnectionManager cm;
425         cm.disconnect(true);
426     }
427 }
428
429 bool CallListener::handleConnection()
430 {
431     if(!initialized_)
432     {
433         return false;
434     }
435
436     ConnectionManager cm;
437
438     if(cm.isConnected())
439     {
440         cm.connect();
441         closeConnection_ = false;
442         return true;
443     }
444
445     closeConnection_ = true;
446
447     Settings::ConnectionType configType = Settings::instance()->getConnectionType();
448
449     if(configType == Settings::ALWAYS_ASK)
450     {
451         showError(tr("Automatic connecting is not allowed by settings."), BANNER_DELAY);
452         return false;
453     }
454
455     showDelayedResult(tr("Connecting..."), BANNER_DELAY);
456
457     ConnectionManager::Connection best;
458
459     ConnectionManager::ConnectionType lookupType = ConnectionManager::NO_TYPE;
460
461     switch(configType)
462     {
463     case Settings::WLAN:
464         lookupType = ConnectionManager::WLAN;
465         break;
466     case Settings::GPRS:
467         lookupType = ConnectionManager::GPRS;
468         break;
469     default:
470         lookupType = ConnectionManager::NO_TYPE;
471         break;
472     }
473
474     int cretries = 0;
475     int scans = 0;
476     bool found = false;
477     int maxScans = GPRS_SCANS;
478
479     if(lookupType != ConnectionManager::GPRS)
480     {
481         maxScans = WLAN_SCANS;
482     }
483
484     while(cretries < CONNECTION_LOOKUP_RETRIES)
485     {
486         if(!initialized_)
487         {
488             return false;
489         }
490
491         if(scans < maxScans)
492         {
493             if(cm.getBestConnection(best, lookupType))
494             {
495                 found = true;
496             }
497
498             scans++;
499         }
500
501         // If there is only gprs connection available,
502         // make sure that we are on 3g network
503         if(found && (best.type != ConnectionManager::GPRS || is3g()))
504         {
505             break;
506         }
507
508         if(found)
509         {
510             sleep(WAIT_BETWEEN_RETRIES);
511         }
512
513         qDebug() << "No connections found, retrying...";
514
515         cretries++;
516
517     }
518
519     if(cretries >= CONNECTION_LOOKUP_RETRIES)
520     {
521         showError(tr("No available 3G or WLAN networks found."));
522         return false;
523     }
524
525     int retries = 0;
526
527     while(retries < CONNECT_RETRIES)
528     {
529         if(!initialized_)
530         {
531             return false;
532         }
533
534         qDebug() << "Connecting to " << best.name << " (" << best.id << ")";
535
536         if(cm.connect(best.id))
537         {
538             break;
539         }
540         else if(cm.error() == ConnectionManager::INVALID_IAP)
541         {
542             showError(tr("Selected access point doesn't exist."));
543             return false;
544         }
545
546         retries++;
547
548         qDebug() << "Unable to connect, retrying...";
549
550         if(retries < CONNECT_RETRIES)
551         {
552             sendRetrySignal(best.id, false);
553             sleep(WAIT_BETWEEN_RETRIES);
554         }
555
556     }
557
558     if(retries >= CONNECT_RETRIES)
559     {
560         sendRetrySignal(best.id, false);
561
562         if(initialized_)
563         {
564             showError(tr("Unable to connect to network."));
565         }
566
567         return false;
568     }
569
570     return initialized_;
571 }
572
573 void CallListener::showError(QString const& msg, int timeout)
574 {
575     qDebug() << "Error: " << msg;
576
577     if(!initialized_ || !box_)
578     {
579         return;
580     }
581
582     box_->setTimeout(ERROR_BANNER_TIMEOUT);
583
584     if(timeout)
585     {
586         showDelayedResult(msg, timeout);
587     }
588     else
589     {
590         showResult(msg);
591     }
592 }
593
594 bool CallListener::is3g()
595 {
596     QDBusMessage msg = QDBusMessage::createMethodCall("com.nokia.phone.net",
597                                                       "/com/nokia/phone/net",
598                                                       "Phone.Net",
599                                                       "get_registration_status");
600
601     QDBusMessage rep = systemBus_.call(msg);
602
603     if(rep.type() == QDBusMessage::ErrorMessage)
604     {
605         qDebug() << "Unable to get network status";
606         return false;
607     }
608
609     uint status = rep.arguments().value(6).toUInt();
610
611     if(status & 0x10 || status & 0x08)
612     {
613         return true;
614     }
615
616     return false;
617 }
618
619 void CallListener::sendRetrySignal(QString const& iap, bool retry)
620 {
621    QDBusMessage msg = QDBusMessage::createSignal("/com/nokia/icd_ui",
622                                                   "com.nokia.icd_ui",
623                                                   "retry");
624
625     QList<QVariant> arguments;
626     arguments.append(QVariant(iap));
627     arguments.append(QVariant(retry));
628     msg.setArguments(arguments);
629
630     QDBusConnection::systemBus().send(msg);
631
632     qDebug() << "Retry signal sent";
633 }
634
635 void CallListener::timerEvent(QTimerEvent* event)
636 {
637     Q_UNUSED(event);
638     killTimer(timer_);
639     timer_ = 0;
640 }
641
642 void CallListener::sleep(int ms)
643 {
644     if(timer_)
645     {
646         killTimer(timer_);
647     }
648
649     timer_ = startTimer(ms);
650
651     while(timer_)
652     {
653         QCoreApplication::processEvents(QEventLoop::WaitForMoreEvents);
654     }
655 }