Fixes: NB#247133 - Skype contacts should not get displayed in contact list when the...
[qtcontacts-tracker:jensg-contactsd.git] / plugins / telepathy / cdtpaccount.cpp
1 /** This file is part of Contacts daemon
2  **
3  ** Copyright (c) 2010-2011 Nokia Corporation and/or its subsidiary(-ies).
4  **
5  ** Contact:  Nokia Corporation (info@qt.nokia.com)
6  **
7  ** GNU Lesser General Public License Usage
8  ** This file may be used under the terms of the GNU Lesser General Public License
9  ** version 2.1 as published by the Free Software Foundation and appearing in the
10  ** file LICENSE.LGPL included in the packaging of this file.  Please review the
11  ** following information to ensure the GNU Lesser General Public License version
12  ** 2.1 requirements will be met:
13  ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
14  **
15  ** In addition, as a special exception, Nokia gives you certain additional rights.
16  ** These rights are described in the Nokia Qt LGPL Exception version 1.1, included
17  ** in the file LGPL_EXCEPTION.txt in this package.
18  **
19  ** Other Usage
20  ** Alternatively, this file may be used in accordance with the terms and
21  ** conditions contained in a signed written agreement between you and Nokia.
22  **/
23
24 #include <TelepathyQt4/ContactManager>
25 #include <TelepathyQt4/PendingContacts>
26 #include <TelepathyQt4/PendingOperation>
27 #include <TelepathyQt4/PendingReady>
28 #include <TelepathyQt4/Profile>
29
30 #include "cdtpaccount.h"
31 #include "cdtpcontact.h"
32 #include "debug.h"
33
34 using namespace Contactsd;
35
36 CDTpAccount::CDTpAccount(const Tp::AccountPtr &account, const QStringList &toAvoid, bool newAccount, QObject *parent)
37     : QObject(parent),
38       mAccount(account),
39       mContactsToAvoid(toAvoid),
40       mHasRoster(false),
41       mNewAccount(newAccount)
42 {
43     // connect all signals we care about, so we can signal that the account
44     // changed accordingly
45     connect(mAccount.data(),
46             SIGNAL(displayNameChanged(const QString &)),
47             SLOT(onAccountDisplayNameChanged()));
48     connect(mAccount.data(),
49             SIGNAL(nicknameChanged(const QString &)),
50             SLOT(onAccountNicknameChanged()));
51     connect(mAccount.data(),
52             SIGNAL(currentPresenceChanged(const Tp::Presence &)),
53             SLOT(onAccountCurrentPresenceChanged()));
54     connect(mAccount.data(),
55             SIGNAL(avatarChanged(const Tp::Avatar &)),
56             SLOT(onAccountAvatarChanged()));
57     connect(mAccount.data(),
58             SIGNAL(connectionChanged(const Tp::ConnectionPtr &)),
59             SLOT(onAccountConnectionChanged(const Tp::ConnectionPtr &)));
60     connect(mAccount.data(),
61             SIGNAL(stateChanged(bool)),
62             SLOT(onAccountStateChanged()));
63
64     setConnection(mAccount->connection());
65 }
66
67 CDTpAccount::~CDTpAccount()
68 {
69 }
70
71 QList<CDTpContactPtr> CDTpAccount::contacts() const
72 {
73     QList<CDTpContactPtr> contacts;
74     Q_FOREACH (const CDTpContactPtr &contactWrapper, mContacts) {
75         if (contactWrapper->isVisible()) {
76             contacts << contactWrapper;
77         }
78     }
79
80     return contacts;
81 }
82
83 void CDTpAccount::setContactsToAvoid(const QStringList &contactIds)
84 {
85     mContactsToAvoid = contactIds;
86     Q_FOREACH (const QString &id, contactIds) {
87         CDTpContactPtr contactWrapper = mContacts.take(id);
88         if (contactWrapper) {
89             contactWrapper->setRemoved(true);
90         }
91     }
92 }
93
94 void CDTpAccount::onAccountDisplayNameChanged()
95 {
96     Q_EMIT changed(CDTpAccountPtr(this), DisplayName);
97 }
98
99 void CDTpAccount::onAccountNicknameChanged()
100 {
101     Q_EMIT changed(CDTpAccountPtr(this), Nickname);
102 }
103
104 void CDTpAccount::onAccountCurrentPresenceChanged()
105 {
106     Q_EMIT changed(CDTpAccountPtr(this), Presence);
107 }
108
109 void CDTpAccount::onAccountAvatarChanged()
110 {
111     Q_EMIT changed(CDTpAccountPtr(this), Avatar);
112 }
113
114 void CDTpAccount::onAccountStateChanged()
115 {
116     Q_EMIT changed(CDTpAccountPtr(this), Enabled);
117
118     if (!isEnabled()) {
119         setConnection(Tp::ConnectionPtr());
120     } else {
121         /* Since contacts got removed when we disabled the account, we need
122          * to threat this account as new now that it is enabled again */
123         mNewAccount = true;
124     }
125 }
126
127 void CDTpAccount::onAccountConnectionChanged(const Tp::ConnectionPtr &connection)
128 {
129     bool oldHasRoster = mHasRoster;
130     setConnection(connection);
131     if (oldHasRoster != mHasRoster) {
132         Q_EMIT rosterChanged(CDTpAccountPtr(this));
133         mNewAccount = false;
134     }
135 }
136
137 void CDTpAccount::setConnection(const Tp::ConnectionPtr &connection)
138 {
139     debug() << "Account" << mAccount->objectPath() << "- has connection:" << (connection != 0);
140
141     mContacts.clear();
142     mHasRoster = false;
143
144     if (connection) {
145         connect(connection->contactManager().data(),
146                 SIGNAL(stateChanged(Tp::ContactListState)),
147                 SLOT(onContactListStateChanged(Tp::ContactListState)));
148         setContactManager(connection->contactManager());
149     }
150 }
151
152 void CDTpAccount::onContactListStateChanged(Tp::ContactListState state)
153 {
154     Q_UNUSED(state);
155
156     /* NB#240743 - It can happen that tpqt4 still emits signals on the
157      * ContactManager after connection has been removed from the account.
158      * In that case Tp::Account::connectionChanged() should be emitted soon */
159     if (!mAccount->connection()) {
160         return;
161     }
162
163     bool oldHasRoster = mHasRoster;
164     setContactManager(mAccount->connection()->contactManager());
165     if (oldHasRoster != mHasRoster) {
166         Q_EMIT rosterChanged(CDTpAccountPtr(this));
167         mNewAccount = false;
168     }
169 }
170
171 void CDTpAccount::setContactManager(const Tp::ContactManagerPtr &contactManager)
172 {
173     if (contactManager->state() != Tp::ContactListStateSuccess) {
174         return;
175     }
176
177     if (mHasRoster) {
178         warning() << "Account" << mAccount->objectPath() << "- already received the roster";
179         return;
180     }
181
182     debug() << "Account" << mAccount->objectPath() << "- received the roster";
183
184     mHasRoster = true;
185     connect(contactManager.data(),
186             SIGNAL(allKnownContactsChanged(const Tp::Contacts &, const Tp::Contacts &, const Tp::Channel::GroupMemberChangeDetails &)),
187             SLOT(onAllKnownContactsChanged(const Tp::Contacts &, const Tp::Contacts &)));
188
189     Q_FOREACH (const Tp::ContactPtr &contact, contactManager->allKnownContacts()) {
190         if (mContactsToAvoid.contains(contact->id())) {
191             continue;
192         }
193         insertContact(contact);
194         if (mNewAccount) {
195             maybeRequestExtraInfo(contact);
196         }
197     }
198 }
199
200 void CDTpAccount::onAllKnownContactsChanged(const Tp::Contacts &contactsAdded,
201         const Tp::Contacts &contactsRemoved)
202 {
203     debug() << "Account" << mAccount->objectPath() << "roster contacts changed:";
204     debug() << " " << contactsAdded.size() << "contacts added";
205     debug() << " " << contactsRemoved.size() << "contacts removed";
206
207     QList<CDTpContactPtr> added;
208     Q_FOREACH (const Tp::ContactPtr &contact, contactsAdded) {
209         if (mContacts.contains(contact->id())) {
210             warning() << "Internal error, contact was already in roster";
211             continue;
212         }
213         if (mContactsToAvoid.contains(contact->id())) {
214             continue;
215         }
216         maybeRequestExtraInfo(contact);
217         CDTpContactPtr contactWrapper = insertContact(contact);
218         if (contactWrapper->isVisible()) {
219             added << contactWrapper;
220         }
221     }
222
223     QList<CDTpContactPtr> removed;
224     Q_FOREACH (const Tp::ContactPtr &contact, contactsRemoved) {
225         const QString id(contact->id());
226         if (!mContacts.contains(id)) {
227             warning() << "Internal error, contact is not in the internal list"
228                 "but was removed from roster";
229             continue;
230         }
231         CDTpContactPtr contactWrapper = mContacts.take(id);
232         if (contactWrapper->isVisible()) {
233             removed << contactWrapper;
234         }
235         contactWrapper->setRemoved(true);
236     }
237
238     if (!added.isEmpty() || !removed.isEmpty()) {
239         Q_EMIT rosterUpdated(CDTpAccountPtr(this), added, removed);
240     }
241 }
242
243 void CDTpAccount::onAccountContactChanged(CDTpContactPtr contactWrapper,
244         CDTpContact::Changes changes)
245 {
246     if ((changes & CDTpContact::Visibility) != 0) {
247         // Visibility of this contact changed. Transform this update operation
248         // to an add/remove operation
249         debug() << "Visibility changed for contact" << contactWrapper->contact()->id();
250
251         QList<CDTpContactPtr> added;
252         QList<CDTpContactPtr> removed;
253         if (contactWrapper->isVisible()) {
254             added << contactWrapper;
255         } else {
256             removed << contactWrapper;
257         }
258
259         Q_EMIT rosterUpdated(CDTpAccountPtr(this), added, removed);
260
261         return;
262     }
263
264     // Forward changes only if contact is visible
265     if (contactWrapper->isVisible()) {
266         Q_EMIT rosterContactChanged(contactWrapper, changes);
267     }
268 }
269
270 CDTpContactPtr CDTpAccount::insertContact(const Tp::ContactPtr &contact)
271 {
272     debug() << "  creating wrapper for contact" << contact->id();
273
274     CDTpContactPtr contactWrapper = CDTpContactPtr(new CDTpContact(contact, this));
275     connect(contactWrapper.data(),
276             SIGNAL(changed(CDTpContactPtr, CDTpContact::Changes)),
277             SLOT(onAccountContactChanged(CDTpContactPtr, CDTpContact::Changes)));
278     mContacts.insert(contact->id(), contactWrapper);
279     return contactWrapper;
280 }
281
282 void CDTpAccount::maybeRequestExtraInfo(Tp::ContactPtr contact)
283 {
284     if (!contact->isAvatarTokenKnown()) {
285         debug() << contact->id() << "first seen: request avatar";
286         contact->requestAvatarData();
287     }
288     if (!contact->isContactInfoKnown()) {
289         debug() << contact->id() << "first seen: refresh ContactInfo";
290         contact->refreshInfo();
291     }
292 }
293
294 CDTpContactPtr CDTpAccount::contact(const QString &id) const
295 {
296     return mContacts.value(id);
297 }
298