~ubuntu-branches/ubuntu/wily/sflphone/wily

« back to all changes in this revision

Viewing changes to kde/src/klib/akonadibackend.cpp

  • Committer: Package Import Robot
  • Author(s): Mark Purcell
  • Date: 2014-01-28 18:23:36 UTC
  • mfrom: (1.1.11)
  • mto: This revision was merged to the branch mainline in revision 24.
  • Revision ID: package-import@ubuntu.com-20140128182336-3xenud1kbnwmf3mz
* New upstream release 
  - Fixes "New Upstream Release" (Closes: #735846)
  - Fixes "Ringtone does not stop" (Closes: #727164)
  - Fixes "[sflphone-kde] crash on startup" (Closes: #718178)
  - Fixes "sflphone GUI crashes when call is hung up" (Closes: #736583)
* Build-Depends: ensure GnuTLS 2.6
  - libucommon-dev (>= 6.0.7-1.1), libccrtp-dev (>= 2.0.6-3)
  - Fixes "FTBFS Build-Depends libgnutls{26,28}-dev" (Closes: #722040)
* Fix "boost 1.49 is going away" unversioned Build-Depends: (Closes: #736746)
* Add Build-Depends: libsndfile-dev, nepomuk-core-dev

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
/****************************************************************************
2
 
 *   Copyright (C) 2011-2013 by Savoir-Faire Linux                          *
 
2
 *   Copyright (C) 2011-2014 by Savoir-Faire Linux                          *
3
3
 *   Author : Emmanuel Lepage Vallee <emmanuel.lepage@savoirfairelinux.com> *
4
4
 *                                                                          *
5
5
 *   This library is free software; you can redistribute it and/or          *
37
37
#include <akonadi/contact/contacteditor.h>
38
38
#include <akonadi/contact/contacteditordialog.h>
39
39
#include <akonadi/session.h>
 
40
#include <akonadi/monitor.h>
40
41
#include <kabc/addressee.h>
41
42
#include <kabc/addresseelist.h>
42
43
#include <kabc/contactgroup.h>
43
 
#include <kabc/phonenumber.h>
44
44
 
45
45
//SFLPhone library
46
46
#include "../lib/contact.h"
47
 
#include "../lib/accountlist.h"
 
47
#include "../lib/accountlistmodel.h"
48
48
#include "../lib/account.h"
49
 
#include "configurationskeleton.h"
 
49
#include "../lib/call.h"
 
50
#include "../lib/callmodel.h"
 
51
#include "../lib/phonenumber.h"
 
52
#include "../lib/phonedirectorymodel.h"
 
53
#include "../lib/numbercategorymodel.h"
 
54
#include "../lib/numbercategory.h"
 
55
#include "kcfg_settings.h"
50
56
 
51
57
///Init static attributes
52
58
AkonadiBackend*  AkonadiBackend::m_pInstance = nullptr;
53
59
 
54
60
///Constructor
55
 
AkonadiBackend::AkonadiBackend(QObject* parent) : ContactBackend(parent)
 
61
AkonadiBackend::AkonadiBackend(QObject* parent) : AbstractContactBackend(parent)
56
62
{
57
63
   m_pSession = new Akonadi::Session( "SFLPhone::instance" );
58
64
 
59
 
   // fetching all collections containing emails recursively, starting at the root collection
60
 
   Akonadi::CollectionFetchJob *job = new Akonadi::CollectionFetchJob( Akonadi::Collection::root(), Akonadi::CollectionFetchJob::Recursive, this );
61
 
   job->fetchScope().setContentMimeTypes( QStringList() << "text/directory" );
62
 
   connect( job, SIGNAL(collectionsReceived(Akonadi::Collection::List)), this, SLOT(collectionsReceived(Akonadi::Collection::List)) );
 
65
   // fetching all collections recursively, starting at the root collection
 
66
   m_pJob = new Akonadi::CollectionFetchJob( Akonadi::Collection::root(), Akonadi::CollectionFetchJob::Recursive, this );
 
67
   m_pJob->fetchScope().setContentMimeTypes( QStringList() << "text/directory" );
 
68
   connect( m_pJob, SIGNAL(collectionsReceived(Akonadi::Collection::List)), this, SLOT(collectionsReceived(Akonadi::Collection::List)) );
 
69
 
 
70
   //Configure change monitor
 
71
   m_pMonitor = new Akonadi::Monitor(this);
 
72
   m_pMonitor->fetchCollectionStatistics(false);
 
73
   Akonadi::ItemFetchScope scope;
 
74
   scope.fetchFullPayload(true);
 
75
   m_pMonitor->setItemFetchScope(scope);
 
76
   connect(m_pMonitor,SIGNAL(itemAdded(Akonadi::Item,Akonadi::Collection)),
 
77
      this,SLOT(slotItemAdded(Akonadi::Item,Akonadi::Collection)));
 
78
   connect(m_pMonitor,SIGNAL(itemChanged(const Akonadi::Item,const QSet<QByteArray>)),
 
79
      this,SLOT(slotItemChanged(const Akonadi::Item,const QSet<QByteArray>)));
 
80
   connect(m_pMonitor,SIGNAL(itemRemoved(const Akonadi::Item)),
 
81
      this,SLOT(slotItemRemoved(const Akonadi::Item)));
63
82
} //AkonadiBackend
64
83
 
65
84
///Destructor
66
85
AkonadiBackend::~AkonadiBackend()
67
86
{
68
 
   CallModel<>::destroy();
69
87
   delete m_pSession;
 
88
   if (Call::contactBackend() == this)
 
89
      Call::setContactBackend(nullptr);
 
90
   delete m_pJob;
 
91
   delete m_pMonitor;
70
92
}
71
93
 
72
94
 
77
99
 ****************************************************************************/
78
100
 
79
101
///Singleton
80
 
ContactBackend* AkonadiBackend::getInstance()
 
102
AbstractContactBackend* AkonadiBackend::instance()
81
103
{
82
104
   if (m_pInstance == nullptr) {
83
105
      m_pInstance = new AkonadiBackend(0);
85
107
   return m_pInstance;
86
108
}
87
109
 
88
 
///Find contact using a phone number
89
 
///@param resolveDNS check if the DNS is used by an account, then assume contact with that phone number / extension is the same as the caller
90
 
Contact* AkonadiBackend::getContactByPhone(const QString& phoneNumber,bool resolveDNS,Account* a)
91
 
{
92
 
   //Remove protocol dependant prefix and suffix
93
 
   QString number = phoneNumber;
94
 
   if (number.left(5) == "<sip:")
95
 
      number = number.remove(0,5);
96
 
   if (number.right(1) == ">")
97
 
      number = number.remove(number.size()-1,1);
98
 
 
99
 
   //Try direct match
100
 
   Contact* c = m_ContactByPhone[number];
101
 
   if (c) {
102
 
      return c;
103
 
   }
104
 
   if (!a)
105
 
      a = AccountList::getInstance()->getDefaultAccount();
106
 
   else if (number.indexOf('@') == -1 && a)
107
 
      return m_ContactByPhone[number+'@'+a->getAccountHostname()];
108
 
 
109
 
   //Use default resolve account to trim hostname away from the number
110
 
   Contact* userOnly = m_ContactByPhone[getUserFromPhone(number).trimmed()];
111
 
   QString defaultResolveAccount = ConfigurationSkeleton::defaultAccountId();
112
 
   if (resolveDNS && !defaultResolveAccount.isEmpty() && number.indexOf('@') != -1) {
113
 
      Account* defResolveAcc = AccountList::getInstance()->getAccountById(defaultResolveAccount);
114
 
      QString hostname = defResolveAcc?defResolveAcc->getAccountHostname():QString();
115
 
      if (defResolveAcc && hostname == number.right(hostname.size())) {
116
 
         return userOnly;
117
 
      }
118
 
   }
119
 
 
120
 
   //Try to find something matching, but at this point it is not 100% sure it is the right one
121
 
   if (resolveDNS && number.indexOf('@') != -1 && !getHostNameFromPhone(number).isEmpty() && userOnly) {
122
 
      foreach (Account* a, AccountList::getInstance()->getAccounts()) {
123
 
         if (a->getAccountHostname() == getHostNameFromPhone(number) && userOnly)
124
 
            return userOnly;
125
 
      }
126
 
   }
127
 
 
128
 
   //Give up
129
 
   return nullptr;
130
 
} //getContactByPhone
131
 
 
132
110
///Find contact by UID
133
111
Contact* AkonadiBackend::getContactByUid(const QString& uid)
134
112
{
135
113
   return m_ContactByUid[uid];
136
114
}
137
115
 
 
116
///Return contact list
 
117
const ContactList& AkonadiBackend::getContactList() const
 
118
{
 
119
   return m_pContacts;
 
120
}
 
121
 
138
122
 
139
123
/*****************************************************************************
140
124
 *                                                                           *
142
126
 *                                                                           *
143
127
 ****************************************************************************/
144
128
 
145
 
KABC::PhoneNumber::Type nameToType(QString name)
 
129
///Convert string to akonadi KABC::PhoneNumber
 
130
KABC::PhoneNumber::Type AkonadiBackend::nameToType(const QString& name)
146
131
{
147
132
   if      (name == "Home"   ) return KABC::PhoneNumber::Home ;
148
133
   else if (name == "Work"   ) return KABC::PhoneNumber::Work ;
161
146
   return KABC::PhoneNumber::Home;
162
147
}
163
148
 
 
149
void AkonadiBackend::fillContact(Contact* c, const KABC::Addressee& addr) const
 
150
{
 
151
   c->setNickName       (addr.nickName()       );
 
152
   c->setFormattedName  (addr.formattedName()  );
 
153
   c->setFirstName      (addr.givenName()      );
 
154
   c->setFamilyName     (addr.familyName()     );
 
155
   c->setOrganization   (addr.organization()   );
 
156
   c->setPreferredEmail (addr.preferredEmail() );
 
157
   c->setDepartment     (addr.department()     );
 
158
   c->setUid            (addr.uid()            );
 
159
 
 
160
   const KABC::PhoneNumber::List numbers = addr.phoneNumbers();
 
161
   Contact::PhoneNumbers newNumbers(c);
 
162
   foreach (const KABC::PhoneNumber& number, numbers) {
 
163
      newNumbers << PhoneDirectoryModel::instance()->getNumber(number.number(),c,nullptr,number.typeLabel());
 
164
      QString number2 = number.number();
 
165
      if (number2.left (5) == "<sip:")
 
166
         number2 = number2.remove(0,5);
 
167
      if (number2.right(1) == ">"    )
 
168
         number2 = number2.remove(number2.size()-2,1);
 
169
   }
 
170
   c->setPhoneNumbers   (newNumbers           );
 
171
}
 
172
 
 
173
Contact* AkonadiBackend::addItem(Akonadi::Item item, bool ignoreEmpty)
 
174
{
 
175
   Contact* aContact = nullptr;
 
176
   if ( item.hasPayload<KABC::Addressee>() ) {
 
177
      m_pMonitor->setItemMonitored(item,true);
 
178
      KABC::Addressee tmp = item.payload<KABC::Addressee>();
 
179
      const KABC::PhoneNumber::List numbers = tmp.phoneNumbers();
 
180
      const QString uid = tmp.uid();
 
181
 
 
182
      if (numbers.size() || !ignoreEmpty) {
 
183
         aContact   = new Contact(this);
 
184
 
 
185
         //This need to be done first because of the phone numbers indexes
 
186
         fillContact(aContact,tmp);
 
187
 
 
188
         m_ContactByUid[uid] = aContact;
 
189
 
 
190
         if (!tmp.photo().data().isNull())
 
191
            aContact->setPhoto(new QPixmap(QPixmap::fromImage( tmp.photo().data()).scaled(QSize(48,48))));
 
192
         else
 
193
            aContact->setPhoto(0);
 
194
 
 
195
         m_AddrHash[ uid ] = tmp ;
 
196
         m_ItemHash[ uid ] = item;
 
197
      }
 
198
   }
 
199
   return aContact;
 
200
}
 
201
 
164
202
 
165
203
/*****************************************************************************
166
204
 *                                                                           *
171
209
///Update the contact list when a new Akonadi collection is added
172
210
ContactList AkonadiBackend::update(Akonadi::Collection collection)
173
211
{
174
 
   Account* defaultAccount = AccountList::getInstance()->getDefaultAccount();
175
 
   m_Collection = collection;
176
212
   if ( !collection.isValid() ) {
177
213
      kDebug() << "The current collection is not valid";
178
214
      return ContactList();
179
215
   }
180
216
 
 
217
   const bool onlyWithNumber =  ConfigurationSkeleton::hideContactWithoutPhone();
 
218
 
181
219
   Akonadi::RecursiveItemFetchJob *job = new Akonadi::RecursiveItemFetchJob( collection, QStringList() << KABC::Addressee::mimeType() << KABC::ContactGroup::mimeType());
182
220
   job->fetchScope().fetchFullPayload();
183
221
   if ( job->exec() ) {
184
 
 
185
222
      const Akonadi::Item::List items = job->items();
186
223
 
187
224
      foreach ( const Akonadi::Item &item, items ) {
188
 
         if ( item.hasPayload<KABC::ContactGroup>() ) {
189
 
            kDebug() << "Group:" << item.payload<KABC::ContactGroup>().name();
190
 
         }
191
 
 
192
 
         if ( item.hasPayload<KABC::Addressee>() ) {
193
 
            KABC::Addressee tmp = item.payload<KABC::Addressee>();
194
 
            Contact* aContact   = new Contact();
195
 
 
196
 
            const KABC::PhoneNumber::List numbers = tmp.phoneNumbers();
197
 
            PhoneNumbers newNumbers;
198
 
            foreach (const KABC::PhoneNumber& number, numbers) {
199
 
               newNumbers << new Contact::PhoneNumber(number.number(),number.typeLabel());
200
 
               QString number2 = number.number();
201
 
               if (number2.left (5) == "<sip:")
202
 
                  number2 = number2.remove(0,5);
203
 
               if (number2.right(1) == ">"    )
204
 
                  number2 = number2.remove(number2.size()-1,1);
205
 
 
206
 
               m_ContactByPhone[number2] = aContact;
207
 
 
208
 
               if (number2.size() <= 6 && defaultAccount && !defaultAccount->getAccountHostname().isEmpty())
209
 
                  m_ContactByPhone[number2+'@'+defaultAccount->getAccountHostname()] = aContact;
210
 
            }
211
 
            m_ContactByUid[tmp.uid()] = aContact;
212
 
 
213
 
            aContact->setNickName       (tmp.nickName()       );
214
 
            aContact->setFormattedName  (tmp.formattedName()  );
215
 
            aContact->setFirstName      (tmp.givenName()      );
216
 
            aContact->setFamilyName     (tmp.familyName()     );
217
 
            aContact->setOrganization   (tmp.organization()   );
218
 
            aContact->setPreferredEmail (tmp.preferredEmail() );
219
 
            aContact->setDepartment     (tmp.department()     );
220
 
            aContact->setUid            (tmp.uid()            );
221
 
            aContact->setPhoneNumbers   (newNumbers           );
222
 
 
223
 
            if (!tmp.photo().data().isNull())
224
 
               aContact->setPhoto(new QPixmap(QPixmap::fromImage( tmp.photo().data()).scaled(QSize(48,48))));
225
 
            else
226
 
               aContact->setPhoto(0);
227
 
 
228
 
            m_AddrHash[ tmp.uid() ] = tmp ;
229
 
            m_ItemHash[ tmp.uid() ] = item;
230
 
         }
 
225
         addItem(item,onlyWithNumber);
231
226
      }
 
227
      beginResetModel();
232
228
      m_pContacts = m_ContactByUid.values();
 
229
      endResetModel();
233
230
   }
234
231
   return m_ContactByUid.values();
235
232
} //update
237
234
///Edit backend value using an updated frontend contact
238
235
void AkonadiBackend::editContact(Contact* contact,QWidget* parent)
239
236
{
240
 
   Akonadi::Item item = m_ItemHash[contact->getUid()];
241
 
   if (!(item.hasPayload<KABC::Addressee>() && item.payload<KABC::Addressee>().uid() == contact->getUid())) {
 
237
   Akonadi::Item item = m_ItemHash[contact->uid()];
 
238
   if (!(item.hasPayload<KABC::Addressee>() && item.payload<KABC::Addressee>().uid() == contact->uid())) {
242
239
      kDebug() << "Contact not found";
243
240
      return;
244
241
   }
248
245
      editor->loadContact(item);
249
246
      QPointer<KDialog> dlg = new KDialog(parent);
250
247
      dlg->setMainWidget(editor);
251
 
      dlg->exec();
252
 
      if ( !editor->saveContact() ) {
253
 
         kDebug() << "Unable to save new contact to storage";
254
 
         return;
 
248
      if ( dlg->exec() == QDialog::Accepted ) {
 
249
         if ( !editor->saveContact() ) {
 
250
            delete dlg;
 
251
            kDebug() << "Unable to save new contact to storage";
 
252
            return;
 
253
         }
255
254
      }
256
255
      delete editor;
257
256
      delete dlg   ;
262
261
void AkonadiBackend::addNewContact(Contact* contact,QWidget* parent)
263
262
{
264
263
   KABC::Addressee newContact;
265
 
   newContact.setNickName       ( contact->getNickName()        );
266
 
   newContact.setFormattedName  ( contact->getFormattedName()   );
267
 
   newContact.setGivenName      ( contact->getFirstName()       );
268
 
   newContact.setFamilyName     ( contact->getSecondName()      );
269
 
   newContact.setOrganization   ( contact->getOrganization()    );
270
 
   newContact.setDepartment     ( contact->getDepartment()      );
 
264
   newContact.setNickName       ( contact->nickName()        );
 
265
   newContact.setFormattedName  ( contact->formattedName()   );
 
266
   newContact.setGivenName      ( contact->firstName()       );
 
267
   newContact.setFamilyName     ( contact->secondName()      );
 
268
   newContact.setOrganization   ( contact->organization()    );
 
269
   newContact.setDepartment     ( contact->department()      );
271
270
   //newContact.setPreferredEmail ( contact->getPreferredEmail()  );//TODO
272
271
 
273
 
   foreach (Contact::PhoneNumber* nb, contact->getPhoneNumbers()) {
 
272
   foreach (PhoneNumber* nb, contact->phoneNumbers()) {
274
273
      KABC::PhoneNumber pn;
275
 
      pn.setType(nameToType(nb->getType()));
 
274
      pn.setType(nameToType(nb->category()->name()));
276
275
 
277
 
      pn.setNumber(nb->getNumber());
 
276
      pn.setNumber(nb->uri());
278
277
      newContact.insertPhoneNumber(pn);
279
278
   }
280
279
 
286
285
 
287
286
   QPointer<KDialog> dlg = new KDialog(parent);
288
287
   dlg->setMainWidget(editor);
289
 
   dlg->exec();
290
 
 
291
 
   if ( !editor->saveContact() ) {
292
 
      kDebug() << "Unable to save new contact to storage";
293
 
      return;
 
288
   if ( dlg->exec() == QDialog::Accepted ) {
 
289
      if ( !editor->saveContact() ) {
 
290
         delete dlg;
 
291
         kDebug() << "Unable to save new contact to storage";
 
292
         return;
 
293
      }
294
294
   }
295
295
   delete dlg;
296
296
} //addNewContact
298
298
///Implement virtual pure method
299
299
void AkonadiBackend::editContact(Contact* contact)
300
300
{
301
 
   editContact(contact,0);
 
301
   editContact(contact,nullptr);
302
302
}
303
303
 
304
304
///Implement virtual pure method
305
305
void AkonadiBackend::addNewContact(Contact* contact)
306
306
{
307
 
   addNewContact(contact,0);
 
307
   addNewContact(contact,nullptr);
308
308
}
309
309
 
310
310
///Add a new phone number to an existing contact
311
311
void AkonadiBackend::addPhoneNumber(Contact* contact, QString number, QString type)
312
312
{
313
 
   Akonadi::Item item = m_ItemHash[contact->getUid()];
314
 
   if (!(item.hasPayload<KABC::Addressee>() && item.payload<KABC::Addressee>().uid() == contact->getUid())) {
 
313
   Akonadi::Item item = m_ItemHash[contact->uid()];
 
314
   if (!(item.hasPayload<KABC::Addressee>() && item.payload<KABC::Addressee>().uid() == contact->uid())) {
315
315
      kDebug() << "Contact not found";
316
316
      return;
317
317
   }
322
322
      QPointer<Akonadi::ContactEditor> editor = new Akonadi::ContactEditor( Akonadi::ContactEditor::EditMode, (QWidget*)nullptr );
323
323
      editor->loadContact(item);
324
324
 
325
 
      QPointer<KDialog> dlg = new KDialog(0);
 
325
      QPointer<KDialog> dlg = new KDialog(nullptr);
326
326
      dlg->setMainWidget(editor);
327
 
      dlg->exec();
328
 
      if ( !editor->saveContact() ) {
329
 
         kDebug() << "Unable to save new contact to storage";
330
 
         return;
 
327
      if ( dlg->exec() == QDialog::Accepted ) {
 
328
         if ( !editor->saveContact() ) {
 
329
            delete dlg;
 
330
            kDebug() << "Unable to save new contact to storage";
 
331
            return;
 
332
         }
331
333
      }
332
334
      delete dlg   ;
333
335
      delete editor;
349
351
{
350
352
   foreach (const Akonadi::Collection& coll, list) {
351
353
      update(coll);
 
354
      m_pMonitor->setCollectionMonitored(coll,true);
352
355
      emit collectionChanged();
353
356
   }
354
357
}
355
358
 
 
359
///Callback when a new item is added
 
360
void AkonadiBackend::slotItemAdded(Akonadi::Item item,Akonadi::Collection coll)
 
361
{
 
362
   Q_UNUSED(coll)
 
363
   Contact* c = addItem(item,ConfigurationSkeleton::hideContactWithoutPhone());
 
364
   if (c) { //Not all items will have an addressee payload
 
365
      beginInsertRows(QModelIndex(),m_pContacts.size()-1,m_pContacts.size());
 
366
      m_pContacts << c;
 
367
      endInsertRows();
 
368
      emit newContactAdded(c);
 
369
      emit layoutChanged();
 
370
   }
 
371
}
 
372
 
 
373
///Callback when an item change
 
374
void AkonadiBackend::slotItemChanged(const Akonadi::Item &item, const QSet< QByteArray > &part)
 
375
{
 
376
   Q_UNUSED(part)
 
377
   if (item.hasPayload<KABC::Addressee>()) {
 
378
      KABC::Addressee tmp = item.payload<KABC::Addressee>();
 
379
      Contact* c = getContactByUid(tmp.uid());
 
380
      if (c)
 
381
         fillContact(c,tmp);
 
382
   }
 
383
}
 
384
 
 
385
///Callback when a contact is removed
 
386
void AkonadiBackend::slotItemRemoved(const Akonadi::Item &item)
 
387
{
 
388
   Contact* c = getContactByUid(item.remoteId());
 
389
   if (c)
 
390
      c->setActive(false);
 
391
}
 
392
 
356
393
///Update the contact list even without a new collection
357
394
ContactList AkonadiBackend::update_slot()
358
395
{