1 /**
2  * This file is part of TelepathyQt
3  *
4  * @copyright Copyright (C) 2008-2010 Collabora Ltd. <http://www.collabora.co.uk/>
5  * @copyright Copyright (C) 2008-2010 Nokia Corporation
6  * @license LGPL 2.1
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
21  */
22 
23 #include <TelepathyQt/Contact>
24 
25 #include "TelepathyQt/_gen/contact.moc.hpp"
26 
27 #include "TelepathyQt/debug-internal.h"
28 #include "TelepathyQt/future-internal.h"
29 
30 #include <TelepathyQt/AvatarData>
31 #include <TelepathyQt/Connection>
32 #include <TelepathyQt/ConnectionCapabilities>
33 #include <TelepathyQt/Constants>
34 #include <TelepathyQt/ContactCapabilities>
35 #include <TelepathyQt/ContactManager>
36 #include <TelepathyQt/LocationInfo>
37 #include <TelepathyQt/PendingContactInfo>
38 #include <TelepathyQt/PendingStringList>
39 #include <TelepathyQt/PendingVoid>
40 #include <TelepathyQt/Presence>
41 #include <TelepathyQt/ReferencedHandles>
42 
43 namespace Tp
44 {
45 
46 struct TP_QT_NO_EXPORT Contact::Private
47 {
PrivateTp::Contact::Private48     Private(Contact *parent, ContactManager *manager,
49         const ReferencedHandles &handle)
50         : parent(parent),
51           manager(ContactManagerPtr(manager)),
52           handle(handle),
53           caps(manager->supportedFeatures().contains(Contact::FeatureCapabilities) ?
54                    ContactCapabilities(true) :  ContactCapabilities(
55                            manager->connection()->capabilities().allClassSpecs(), false)),
56           isContactInfoKnown(false), isAvatarTokenKnown(false),
57           subscriptionState(SubscriptionStateUnknown),
58           publishState(SubscriptionStateUnknown),
59           blocked(false)
60     {
61     }
62 
63     void updateAvatarData();
64 
65     Contact *parent;
66 
67     WeakPtr<ContactManager> manager;
68     ReferencedHandles handle;
69     QString id;
70 
71     Features requestedFeatures;
72     Features actualFeatures;
73 
74     QString alias;
75     QMap<QString, QString> vcardAddresses;
76     QStringList uris;
77     Presence presence;
78     ContactCapabilities caps;
79     LocationInfo location;
80 
81     bool isContactInfoKnown;
82     InfoFields info;
83 
84     bool isAvatarTokenKnown;
85     QString avatarToken;
86     AvatarData avatarData;
87 
88     SubscriptionState subscriptionState;
89     SubscriptionState publishState;
90     QString publishStateMessage;
91     bool blocked;
92 
93     QSet<QString> groups;
94 
95     QStringList clientTypes;
96 };
97 
updateAvatarData()98 void Contact::Private::updateAvatarData()
99 {
100     /* If token is NULL, it means that CM doesn't know the token. In that case we
101      * have to request the avatar data to get the token. This happens with XMPP
102      * for offline contacts. We don't want to bypass the avatar cache, so we won't
103      * update avatar. */
104     if (avatarToken.isNull()) {
105         return;
106     }
107 
108     /* If token is empty (""), it means the contact has no avatar. */
109     if (avatarToken.isEmpty()) {
110         debug() << "Contact" << parent->id() << "has no avatar";
111         avatarData = AvatarData();
112         emit parent->avatarDataChanged(avatarData);
113         return;
114     }
115 
116     parent->manager()->requestContactAvatars(QList<ContactPtr>() << ContactPtr(parent));
117 }
118 
119 struct TP_QT_NO_EXPORT Contact::InfoFields::Private : public QSharedData
120 {
PrivateTp::Contact::InfoFields::Private121     Private(const ContactInfoFieldList &allFields)
122         : allFields(allFields) {}
123 
124     ContactInfoFieldList allFields;
125 };
126 
127 /**
128  * \class Contact::InfoFields
129  * \ingroup clientconn
130  * \headerfile TelepathyQt/contact.h <TelepathyQt/Contact>
131  *
132  * \brief The Contact::InfoFields class represents the information of a
133  * Telepathy contact.
134  */
135 
136 /**
137  * Construct a info fields instance with the given fields. The instance will indicate that
138  * it is valid.
139  */
InfoFields(const ContactInfoFieldList & allFields)140 Contact::InfoFields::InfoFields(const ContactInfoFieldList &allFields)
141     : mPriv(new Private(allFields))
142 {
143 }
144 
145 /**
146  * Constructs a new invalid InfoFields instance.
147  */
InfoFields()148 Contact::InfoFields::InfoFields()
149 {
150 }
151 
152 /**
153  * Copy constructor.
154  */
InfoFields(const Contact::InfoFields & other)155 Contact::InfoFields::InfoFields(const Contact::InfoFields &other)
156     : mPriv(other.mPriv)
157 {
158 }
159 
160 /**
161  * Class destructor.
162  */
~InfoFields()163 Contact::InfoFields::~InfoFields()
164 {
165 }
166 
167 /**
168  * Assignment operator.
169  */
operator =(const Contact::InfoFields & other)170 Contact::InfoFields &Contact::InfoFields::operator=(const Contact::InfoFields &other)
171 {
172     this->mPriv = other.mPriv;
173     return *this;
174 }
175 
176 /**
177  * Return a list containing all fields whose name are \a name.
178  *
179  * \param name The name used to match the fields.
180  * \return A list of ContactInfoField objects.
181  */
fields(const QString & name) const182 ContactInfoFieldList Contact::InfoFields::fields(const QString &name) const
183 {
184     if (!isValid()) {
185         return ContactInfoFieldList();
186     }
187 
188     ContactInfoFieldList ret;
189     foreach (const ContactInfoField &field, mPriv->allFields) {
190         if (field.fieldName == name) {
191             ret.append(field);
192         }
193     }
194     return ret;
195 }
196 
197 /**
198  * Return a list containing all fields describing the contact information.
199  *
200  * \return The contact information as a list of ContactInfoField objects.
201  */
allFields() const202 ContactInfoFieldList Contact::InfoFields::allFields() const
203 {
204     return isValid() ? mPriv->allFields : ContactInfoFieldList();
205 }
206 
207 /**
208  * \class Contact
209  * \ingroup clientconn
210  * \headerfile TelepathyQt/contact.h <TelepathyQt/Contact>
211  *
212  * \brief The Contact class represents a Telepathy contact.
213  *
214  * The accessor functions on this object (id(), alias(), and so on) don't make any D-Bus calls;
215  * instead, they return/use values cached from a previous introspection run.
216  * The introspection process populates their values in the most efficient way possible based on what
217  * the service implements.
218  *
219  * To avoid unnecessary D-Bus traffic, some accessors only return valid
220  * information after specific features have been enabled.
221  * For instance, to retrieve the contact avatar token, it is necessary to
222  * enable the feature Contact::FeatureAvatarToken.
223  * See the individual methods descriptions for more details.
224  *
225  * Contact features can be enabled by constructing a ContactFactory and enabling
226  * the desired features, and passing it to AccountManager, Account or ClientRegistrar
227  * when creating them as appropriate. However, if a particular
228  * feature is only ever used in a specific circumstance, such as an user opening
229  * some settings dialog separate from the general view of the application,
230  * features can be later enabled as needed by calling ContactManager::upgradeContacts() with the
231  * additional features, and waiting for the resulting PendingOperation to finish.
232  *
233  * As an addition to accessors, signals are emitted to indicate that properties have
234  * changed, for example aliasChanged(), avatarTokenChanged(), etc.
235  *
236  * See \ref async_model, \ref shared_ptr
237  */
238 
239 /**
240  * Feature used in order to access contact alias info.
241  *
242  * \sa alias(), aliasChanged()
243  */
244 const Feature Contact::FeatureAlias = Feature(QLatin1String(Contact::staticMetaObject.className()), 0, false);
245 
246 /**
247  * Feature used in order to access contact avatar data info.
248  *
249  * Enabling this feature will also enable FeatureAvatarToken.
250  *
251  * \sa avatarData(), avatarDataChanged()
252  */
253 const Feature Contact::FeatureAvatarData = Feature(QLatin1String(Contact::staticMetaObject.className()), 1, false);
254 
255 /**
256  * Feature used in order to access contact avatar token info.
257  *
258  * \sa isAvatarTokenKnown(), avatarToken(), avatarTokenChanged()
259  */
260 const Feature Contact::FeatureAvatarToken = Feature(QLatin1String(Contact::staticMetaObject.className()), 2, false);
261 
262 /**
263  * Feature used in order to access contact capabilities info.
264  *
265  * \sa capabilities(), capabilitiesChanged()
266  */
267 const Feature Contact::FeatureCapabilities = Feature(QLatin1String(Contact::staticMetaObject.className()), 3, false);
268 
269 /**
270  * Feature used in order to access contact info fields.
271  *
272  * \sa infoFields(), infoFieldsChanged()
273  */
274 const Feature Contact::FeatureInfo = Feature(QLatin1String(Contact::staticMetaObject.className()), 4, false);
275 
276 /**
277  * Feature used in order to access contact location info.
278  *
279  * \sa location(), locationUpdated()
280  */
281 const Feature Contact::FeatureLocation = Feature(QLatin1String(Contact::staticMetaObject.className()), 5, false);
282 
283 /**
284  * Feature used in order to access contact presence info.
285  *
286  * \sa presence(), presenceChanged()
287  */
288 const Feature Contact::FeatureSimplePresence = Feature(QLatin1String(Contact::staticMetaObject.className()), 6, false);
289 
290 /**
291  * Feature used in order to access contact roster groups.
292  *
293  * \sa groups(), addedToGroup(), removedFromGroup()
294  */
295 const Feature Contact::FeatureRosterGroups = Feature(QLatin1String(Contact::staticMetaObject.className()), 7, false);
296 
297 /**
298  * Feature used in order to access contact addressable addresses info.
299  *
300  * \sa vcardAddresses(), uris()
301  */
302 const Feature Contact::FeatureAddresses = Feature(QLatin1String(Contact::staticMetaObject.className()), 8, false);
303 
304 /**
305  * Feature used in order to access contact client types info.
306  *
307  * \sa clientTypes(), requestClientTypes(), clientTypesChanged()
308  */
309 const Feature Contact::FeatureClientTypes = Feature(QLatin1String(Contact::staticMetaObject.className()), 9, false);
310 
311 /**
312  * Construct a new Contact object.
313  *
314  * \param manager ContactManager owning this contact.
315  * \param handle The contact handle.
316  * \param requestedFeatures The contact requested features.
317  * \param attributes The contact attributes.
318  */
Contact(ContactManager * manager,const ReferencedHandles & handle,const Features & requestedFeatures,const QVariantMap & attributes)319 Contact::Contact(ContactManager *manager, const ReferencedHandles &handle,
320         const Features &requestedFeatures, const QVariantMap &attributes)
321     : Object(),
322       mPriv(new Private(this, manager, handle))
323 {
324     mPriv->requestedFeatures.unite(requestedFeatures);
325     mPriv->id = qdbus_cast<QString>(attributes[
326             TP_QT_IFACE_CONNECTION + QLatin1String("/contact-id")]);
327 }
328 
329 /**
330  * Class destructor.
331  */
~Contact()332 Contact::~Contact()
333 {
334     debug() << "Contact" << id() << "destroyed";
335     delete mPriv;
336 }
337 
338 /**
339  * Return the contact nanager owning this contact.
340  *
341  * \return A pointer to the ContactManager object.
342  */
manager() const343 ContactManagerPtr Contact::manager() const
344 {
345     return ContactManagerPtr(mPriv->manager);
346 }
347 
348 /**
349  * Return the handle of this contact.
350  *
351  * \return The handle as a ReferencedHandles object.
352  */
handle() const353 ReferencedHandles Contact::handle() const
354 {
355     return mPriv->handle;
356 }
357 
358 /**
359  * Return the identifier of this contact.
360  *
361  * \return The identifier.
362  */
id() const363 QString Contact::id() const
364 {
365     return mPriv->id;
366 }
367 
368 /**
369  * Return the features requested on this contact.
370  *
371  * \return The requested features as a set of Feature objects.
372  */
requestedFeatures() const373 Features Contact::requestedFeatures() const
374 {
375     return mPriv->requestedFeatures;
376 }
377 
378 /**
379  * Return the features that are actually enabled on this contact.
380  *
381  * \return The actual features as a set of Feature objects.
382  */
actualFeatures() const383 Features Contact::actualFeatures() const
384 {
385     return mPriv->actualFeatures;
386 }
387 
388 /**
389  * Return the alias of this contact.
390  *
391  * Change notification is via the aliasChanged() signal.
392  *
393  * This method requires Contact::FeatureAlias to be ready.
394  *
395  * \return The alias.
396  */
alias() const397 QString Contact::alias() const
398 {
399     if (!mPriv->requestedFeatures.contains(FeatureAlias)) {
400         warning() << "Contact::alias() used on" << this
401             << "for which FeatureAlias hasn't been requested - returning id";
402         return id();
403     }
404 
405     return mPriv->alias;
406 }
407 
408 /**
409  * Return the various vcard addresses that identify this contact.
410  *
411  * This method requires Contact::FeatureAddresses to be ready.
412  *
413  * \return The vcard addresses identifying this contact.
414  * \sa ContactManager::contactsForVCardAddresses(), uris()
415  */
vcardAddresses() const416 QMap<QString, QString> Contact::vcardAddresses() const
417 {
418     return mPriv->vcardAddresses;
419 }
420 
421 /**
422  * Return the various URI addresses that identify this contact.
423  *
424  * This method requires Contact::FeatureAddresses to be ready.
425  *
426  * \return The URI addresses identifying this contact.
427  * \sa ContactManager::contactsForUris(), vcardAddresses()
428  */
uris() const429 QStringList Contact::uris() const
430 {
431     return mPriv->uris;
432 }
433 
434 /**
435  * Return whether the avatar token of this contact is known.
436  *
437  * This method requires Contact::FeatureAvatarToken to be ready.
438  *
439  * \return \c true if the avatar token is known, \c false otherwise.
440  * \sa avatarToken()
441  */
isAvatarTokenKnown() const442 bool Contact::isAvatarTokenKnown() const
443 {
444     if (!mPriv->requestedFeatures.contains(FeatureAvatarToken)) {
445         warning() << "Contact::isAvatarTokenKnown() used on" << this
446             << "for which FeatureAvatarToken hasn't been requested - returning false";
447         return false;
448     }
449 
450     return mPriv->isAvatarTokenKnown;
451 }
452 
453 /**
454  * Return the avatar token for this contact.
455  *
456  * Change notification is via the avatarTokenChanged() signal.
457  *
458  * This method requires Contact::FeatureAvatarToken to be ready.
459  *
460  * \return The avatar token.
461  * \sa isAvatarTokenKnown(), avatarTokenChanged(), avatarData()
462  */
avatarToken() const463 QString Contact::avatarToken() const
464 {
465     if (!mPriv->requestedFeatures.contains(FeatureAvatarToken)) {
466         warning() << "Contact::avatarToken() used on" << this
467             << "for which FeatureAvatarToken hasn't been requested - returning \"\"";
468         return QString();
469     } else if (!isAvatarTokenKnown()) {
470         warning() << "Contact::avatarToken() used on" << this
471             << "for which the avatar token is not (yet) known - returning \"\"";
472         return QString();
473     }
474 
475     return mPriv->avatarToken;
476 }
477 
478 /**
479  * Return the actual avatar for this contact.
480  *
481  * Change notification is via the avatarDataChanged() signal.
482  *
483  * This method requires Contact::FeatureAvatarData to be ready.
484  *
485  * \return The avatar as an AvatarData object.
486  * \sa avatarDataChanged(), avatarToken()
487  */
avatarData() const488 AvatarData Contact::avatarData() const
489 {
490     if (!mPriv->requestedFeatures.contains(FeatureAvatarData)) {
491         warning() << "Contact::avatarData() used on" << this
492             << "for which FeatureAvatarData hasn't been requested - returning \"\"";
493         return AvatarData();
494     }
495 
496     return mPriv->avatarData;
497 }
498 
499 /**
500  * Start a request to retrieve the avatar for this contact.
501  *
502  * Force the request of the avatar data. This method returns directly, emitting
503  * avatarTokenChanged() and avatarDataChanged() signals once the token and data are
504  * fetched from the server.
505  *
506  * This is only useful if the avatar token is unknown; see isAvatarTokenKnown().
507  * It happens in the case of offline XMPP contacts, because the server does not
508  * send the token for them and an explicit request of the avatar data is needed.
509  *
510  * This method requires Contact::FeatureAvatarData to be ready.
511  *
512  * \sa avatarData(), avatarDataChanged(), avatarToken(), avatarTokenChanged()
513  */
requestAvatarData()514 void Contact::requestAvatarData()
515 {
516     if (!mPriv->requestedFeatures.contains(FeatureAvatarData)) {
517         warning() << "Contact::requestAvatarData() used on" << this
518             << "for which FeatureAvatarData hasn't been requested - returning \"\"";
519         return;
520     }
521 
522     return manager()->requestContactAvatars(QList<ContactPtr>() << ContactPtr(this));
523 }
524 
525 /**
526  * Return the actual presence of this contact.
527  *
528  * Change notification is via the presenceChanged() signal.
529  *
530  * This method requires Contact::FeatureSimplePresence to be ready.
531  *
532  * \return The presence as a Presence object.
533  */
presence() const534 Presence Contact::presence() const
535 {
536     if (!mPriv->requestedFeatures.contains(FeatureSimplePresence)) {
537         warning() << "Contact::presence() used on" << this
538             << "for which FeatureSimplePresence hasn't been requested - returning Unknown";
539         return Presence();
540     }
541 
542     return mPriv->presence;
543 }
544 
545 /**
546  * Return the capabilities for this contact.
547  *
548  * User interfaces can use this information to show or hide UI components.
549  *
550  * If ContactManager::supportedFeatures() contains Contact::FeatureCapabilities,
551  * the returned object will be a ContactCapabilities object, where
552  * CapabilitiesBase::isSpecificToContact() will be \c true; if that feature
553  * isn't present, this returned object is the subset of
554  * Contact::manager()::connection()::capabilities()
555  * and CapabilitiesBase::isSpecificToContact() will be \c false.
556  *
557  * Change notification is via the capabilitiesChanged() signal.
558  *
559  * This method requires Contact::FeatureCapabilities to be ready.
560  *
561  * @return An object representing the contact capabilities.
562  */
capabilities() const563 ContactCapabilities Contact::capabilities() const
564 {
565     if (!mPriv->requestedFeatures.contains(FeatureCapabilities)) {
566         warning() << "Contact::capabilities() used on" << this
567             << "for which FeatureCapabilities hasn't been requested - returning 0";
568         return ContactCapabilities(false);
569     }
570 
571     return mPriv->caps;
572 }
573 
574 /**
575  * Return the location for this contact.
576  *
577  * Change notification is via the locationUpdated() signal.
578  *
579  * This method requires Contact::FeatureLocation to be ready.
580  *
581  * \return The contact location as a LocationInfo object.
582  */
location() const583 LocationInfo Contact::location() const
584 {
585     if (!mPriv->requestedFeatures.contains(FeatureLocation)) {
586         warning() << "Contact::location() used on" << this
587             << "for which FeatureLocation hasn't been requested - returning 0";
588         return LocationInfo();
589     }
590 
591     return mPriv->location;
592 }
593 
594 /**
595  * Return whether the info card for this contact has been received.
596  *
597  * With some protocols (notably XMPP) information is not pushed from the server
598  * and must be requested explicitely using refreshInfo() or requestInfo(). This
599  * method can be used to know if the information is received from the server
600  * or if an explicit request is needed.
601  *
602  * This method requires Contacat::FeatureInfo to be ready.
603  *
604  * \return \c true if the information is known; \c false otherwise.
605  */
isContactInfoKnown() const606 bool Contact::isContactInfoKnown() const
607 {
608     if (!mPriv->requestedFeatures.contains(FeatureInfo)) {
609         warning() << "Contact::isContactInfoKnown() used on" << this
610             << "for which FeatureInfo hasn't been requested - returning false";
611         return false;
612     }
613 
614     return mPriv->isContactInfoKnown;
615 }
616 
617 /**
618  * Return the information for this contact.
619  *
620  * Note that this method only return cached information. In order to refresh the
621  * information use refreshInfo().
622  *
623  * Change notification is via the infoFieldsChanged() signal.
624  *
625  * This method requires Contact::FeatureInfo to be ready.
626  *
627  * \return The contact info as a Contact::InfoFields object.
628  */
infoFields() const629 Contact::InfoFields Contact::infoFields() const
630 {
631     if (!mPriv->requestedFeatures.contains(FeatureInfo)) {
632         warning() << "Contact::infoFields() used on" << this
633             << "for which FeatureInfo hasn't been requested - returning empty "
634                "InfoFields";
635         return InfoFields();
636     }
637 
638     return mPriv->info;
639 }
640 
641 /**
642  * Refresh information for the given contact.
643  *
644  * Once the information is retrieved infoFieldsChanged() will be emitted.
645  *
646  * This method requires Contact::FeatureInfo to be ready.
647  *
648  * \return A PendingOperation, which will emit PendingOperation::finished
649  *         when the call has finished.
650  * \sa infoFieldsChanged()
651  */
refreshInfo()652 PendingOperation *Contact::refreshInfo()
653 {
654     ConnectionPtr conn = manager()->connection();
655     if (!mPriv->requestedFeatures.contains(FeatureInfo)) {
656         warning() << "Contact::refreshInfo() used on" << this
657             << "for which FeatureInfo hasn't been requested - failing";
658         return new PendingFailure(TP_QT_ERROR_NOT_AVAILABLE,
659                 QLatin1String("FeatureInfo needs to be ready in order to "
660                     "use this method"),
661                 ContactPtr(this));
662     }
663 
664     return manager()->refreshContactInfo(QList<ContactPtr>() << ContactPtr(this));
665 }
666 
667 /**
668  * Start a request to retrieve the information for this contact.
669  *
670  * This method is useful for UIs that don't care about notification of changes
671  * in the contact information but want to show the contact information
672  * (e.g. right-click on a contact and show the contact info).
673  *
674  * \return A PendingContactInfo, which will emit PendingContactInfo::finished
675  *         when the information has been retrieved or an error occurred.
676  */
requestInfo()677 PendingContactInfo *Contact::requestInfo()
678 {
679     return new PendingContactInfo(ContactPtr(this));
680 }
681 
682 /**
683  * Return whether the presence subscription state of this contact is known.
684  *
685  * \return \c true if the presence subscription state is known, \c false otherwise.
686  * \sa subscriptionState(), isSubscriptionRejected()
687  */
isSubscriptionStateKnown() const688 bool Contact::isSubscriptionStateKnown() const
689 {
690     return mPriv->subscriptionState != SubscriptionStateUnknown;
691 }
692 
693 /**
694  * Return whether a request to see this contact's presence was denied.
695  *
696  * \return \c if the a request to see the presence subscription was denied, \c false otherwise.
697  * \sa isSubscriptionStateKnown(), subscriptionState()
698  */
isSubscriptionRejected() const699 bool Contact::isSubscriptionRejected() const
700 {
701     return mPriv->subscriptionState == SubscriptionStateRemovedRemotely;
702 }
703 
704 /**
705  * Return the presence subscription state of this contact (i.e. whether the local user can retrieve
706  * information about this contact's presence).
707  *
708  * \return The presence subscription state as Contact::PresenceState.
709  * \sa isSubscriptionStateKnown(), isSubscriptionRejected()
710  */
subscriptionState() const711 Contact::PresenceState Contact::subscriptionState() const
712 {
713     return subscriptionStateToPresenceState(mPriv->subscriptionState);
714 }
715 
716 /**
717  * Return whether the presence publish state of this contact is known.
718  *
719  * \return \c true if the presence publish state is known, \c false otherwise.
720  * \sa publishState(), isPublishCancelled()
721  */
isPublishStateKnown() const722 bool Contact::isPublishStateKnown() const
723 {
724     return mPriv->publishState != SubscriptionStateUnknown;
725 }
726 
727 /**
728  * Return whether a request to publish presence information to this contact was cancelled.
729  *
730  * \return \c true if a request to publish presence information was cancelled,
731  *         \c false otherwise.
732  * \sa isPublishStateKnown(), publishState()
733  */
isPublishCancelled() const734 bool Contact::isPublishCancelled() const
735 {
736     return mPriv->publishState == SubscriptionStateRemovedRemotely;
737 }
738 
739 /**
740  * Return the presence publish state of this contact (i.e. whether this contact can retrieve
741  * information about the local user's presence).
742  *
743  * \return The presence publish state as Contact::PresenceState.
744  * \sa isSubscriptionStateKnown(), isSubscriptionRejected()
745  */
publishState() const746 Contact::PresenceState Contact::publishState() const
747 {
748     return subscriptionStateToPresenceState(mPriv->publishState);
749 }
750 
751 /**
752  * If the publishState() is Contact::PresenceStateAsk, return an optional message that was sent
753  * by the contact asking to receive the local user's presence; omitted if none was given.
754  *
755  * \return The message that was sent by the contact asking to receive the local user's presence.
756  * \sa publishState()
757  */
publishStateMessage() const758 QString Contact::publishStateMessage() const
759 {
760     return mPriv->publishStateMessage;
761 }
762 
763 /**
764  * Start a request that this contact allow the local user to subscribe to their presence (i.e. that
765  * this contact's subscribe attribute becomes Contact::PresenceStateYes)
766  *
767  * This method requires Connection::FeatureRoster to be ready.
768  *
769  * \return A PendingOperation which will emit PendingOperation::finished
770  *         when the request has been made.
771  * \sa subscriptionState(), removePresenceSubscription()
772  */
requestPresenceSubscription(const QString & message)773 PendingOperation *Contact::requestPresenceSubscription(const QString &message)
774 {
775     return manager()->requestPresenceSubscription(QList<ContactPtr>() << ContactPtr(this), message);
776 }
777 
778 /**
779  * Start a request for the local user to stop receiving presence from this contact.
780  *
781  * This method requires Connection::FeatureRoster to be ready.
782  *
783  * \return A PendingOperation which will emit PendingOperation::finished
784  *         when the request has been made.
785  * \sa subscriptionState(), requestPresenceSubscription()
786  */
removePresenceSubscription(const QString & message)787 PendingOperation *Contact::removePresenceSubscription(const QString &message)
788 {
789     return manager()->removePresenceSubscription(QList<ContactPtr>() << ContactPtr(this), message);
790 }
791 
792 /**
793  * Start a request to authorize this contact's request to see the local user presence
794  * (i.e. that this contact publish attribute becomes Contact::PresenceStateYes).
795  *
796  * This method requires Connection::FeatureRoster to be ready.
797  *
798  * \return A PendingOperation which will emit PendingOperation::finished
799  *         when the request has been made.
800  * \sa publishState(), removePresencePublication()
801  */
authorizePresencePublication(const QString & message)802 PendingOperation *Contact::authorizePresencePublication(const QString &message)
803 {
804     return manager()->authorizePresencePublication(QList<ContactPtr>() << ContactPtr(this), message);
805 }
806 
807 /**
808  * Start a request for the local user to stop sending presence to this contact.
809  *
810  * This method requires Connection::FeatureRoster to be ready.
811  *
812  * \return A PendingOperation which will emit PendingOperation::finished
813  *         when the request has been made.
814  * \sa publishState(), authorizePresencePublication()
815  */
removePresencePublication(const QString & message)816 PendingOperation *Contact::removePresencePublication(const QString &message)
817 {
818     return manager()->removePresencePublication(QList<ContactPtr>() << ContactPtr(this), message);
819 }
820 
821 /**
822  * Return whether this contact is blocked.
823  *
824  * Change notification is via the blockStatusChanged() signal.
825  *
826  * \return \c true if blocked, \c false otherwise.
827  * \sa block()
828  */
isBlocked() const829 bool Contact::isBlocked() const
830 {
831     return mPriv->blocked;
832 }
833 
834 /**
835  * Block this contact. Blocked contacts cannot send messages to the user;
836  * depending on the protocol, blocking a contact may have other effects.
837  *
838  * This method requires Connection::FeatureRoster to be ready.
839  *
840  * \return A PendingOperation which will emit PendingOperation::finished
841  *         when an attempt has been made to take the requested action.
842  * \sa blockAndReportAbuse(), unblock()
843  */
block()844 PendingOperation *Contact::block()
845 {
846     return manager()->blockContacts(QList<ContactPtr>() << ContactPtr(this));
847 }
848 
849 /**
850  * Block this contact and additionally report abusive behaviour
851  * to the server.
852  *
853  * If reporting abusive behaviour is not supported by the protocol,
854  * this method has the same effect as block().
855  *
856  * This method requires Connection::FeatureRoster to be ready.
857  *
858  * \return A PendingOperation which will emit PendingOperation::finished
859  *         when an attempt has been made to take the requested action.
860  * \sa ContactManager::canReportAbuse(), block(), unblock()
861  */
blockAndReportAbuse()862 PendingOperation *Contact::blockAndReportAbuse()
863 {
864     return manager()->blockContactsAndReportAbuse(QList<ContactPtr>() << ContactPtr(this));
865 }
866 
867 /**
868  * Unblock this contact.
869  *
870  * This method requires Connection::FeatureRoster to be ready.
871  *
872  * \return A PendingOperation which will emit PendingOperation::finished
873  *         when an attempt has been made to take the requested action.
874  * \sa block(), blockAndReportAbuse()
875  */
unblock()876 PendingOperation *Contact::unblock()
877 {
878     return manager()->unblockContacts(QList<ContactPtr>() << ContactPtr(this));
879 }
880 
881 /**
882  * Return the names of the user-defined roster groups to which the contact
883  * belongs.
884  *
885  * Change notification is via the addedToGroup() and removedFromGroup() signals.
886  *
887  * This method requires Connection::FeatureRosterGroups to be ready.
888  *
889  * \return A list of user-defined contact list groups names.
890  * \sa addToGroup(), removedFromGroup()
891  */
groups() const892 QStringList Contact::groups() const
893 {
894     return mPriv->groups.toList();
895 }
896 
897 /**
898  * Attempt to add the contact to the user-defined contact list
899  * group named \a group.
900  *
901  * This method requires Connection::FeatureRosterGroups to be ready.
902  *
903  * \param group The group name.
904  * \return A PendingOperation which will emit PendingOperation::finished
905  *         when an attempt has been made to to add the contact to the user-defined contact
906  *         list group.
907  * \sa groups(), removeFromGroup()
908  */
addToGroup(const QString & group)909 PendingOperation *Contact::addToGroup(const QString &group)
910 {
911     return manager()->addContactsToGroup(group, QList<ContactPtr>() << ContactPtr(this));
912 }
913 
914 /**
915  * Attempt to remove the contact from the user-defined contact list
916  * group named \a group.
917  *
918  * This method requires Connection::FeatureRosterGroups to be ready.
919  *
920  * \param group The group name.
921  * \return A PendingOperation which will emit PendingOperation::finished
922  *         when an attempt has been made to to remote the contact to the user-defined contact
923  *         list group.
924  * \sa groups(), addToGroup()
925  */
removeFromGroup(const QString & group)926 PendingOperation *Contact::removeFromGroup(const QString &group)
927 {
928     return manager()->removeContactsFromGroup(group, QList<ContactPtr>() << ContactPtr(this));
929 }
930 
931 /**
932  * Return the client types of this contact, if known.
933  *
934  * Client types are represented using the values documented by the XMPP registrar,
935  * with some additional types. A contact can set one or more client types, or can simply
936  * advertise itself as unknown - in this case, an empty list is returned.
937  *
938  * This method returns cached information and is more appropriate for "lazy"
939  * client type finding, for instance displaying the client types (if available)
940  * of everyone in your contact list. For getting latest up-to-date information from
941  * the server you should use requestClientTypes() instead.
942  *
943  * This method requires FeatureClientTypes to be ready.
944  *
945  * \return A list of the client types advertised by this contact.
946  * \sa requestClientTypes(), clientTypesChanged()
947  */
clientTypes() const948 QStringList Contact::clientTypes() const
949 {
950     if (!mPriv->requestedFeatures.contains(FeatureClientTypes)) {
951         warning() << "Contact::clientTypes() used on" << this
952             << "for which FeatureClientTypes hasn't been requested - returning an empty list";
953         return QStringList();
954     }
955 
956     return mPriv->clientTypes;
957 }
958 
959 /**
960  * Return the current client types of the given contact.
961  *
962  * If necessary, this method will make a request to the server for up-to-date
963  * information and wait for a reply. Therefore, this method is more appropriate
964  * for use in a "Contact Information..." dialog; it can be used to show progress
965  * information (while waiting for the method to return), and can distinguish
966  * between various error conditions.
967  *
968  * This method requires FeatureClientTypes to be ready.
969  *
970  * \return A list of the client types advertised by this contact.
971  * \sa clientTypes(), clientTypesChanged()
972  */
requestClientTypes()973 PendingStringList *Contact::requestClientTypes()
974 {
975     if (!mPriv->requestedFeatures.contains(FeatureClientTypes)) {
976         warning() << "Contact::requestClientTypes() used on" << this
977             << "for which FeatureClientTypes hasn't been requested - the operation will fail";
978     }
979 
980     Client::ConnectionInterfaceClientTypesInterface *clientTypesInterface =
981         manager()->connection()->interface<Client::ConnectionInterfaceClientTypesInterface>();
982 
983     return new PendingStringList(
984             clientTypesInterface->RequestClientTypes(mPriv->handle.at(0)),
985             ContactPtr(this));
986 }
987 
augment(const Features & requestedFeatures,const QVariantMap & attributes)988 void Contact::augment(const Features &requestedFeatures, const QVariantMap &attributes)
989 {
990     mPriv->requestedFeatures.unite(requestedFeatures);
991 
992     mPriv->id = qdbus_cast<QString>(attributes[
993             TP_QT_IFACE_CONNECTION + QLatin1String("/contact-id")]);
994 
995     if (attributes.contains(TP_QT_IFACE_CONNECTION_INTERFACE_CONTACT_LIST +
996                 QLatin1String("/subscribe"))) {
997         uint subscriptionState = qdbus_cast<uint>(attributes.value(
998                      TP_QT_IFACE_CONNECTION_INTERFACE_CONTACT_LIST + QLatin1String("/subscribe")));
999         setSubscriptionState((SubscriptionState) subscriptionState);
1000     }
1001 
1002     if (attributes.contains(TP_QT_IFACE_CONNECTION_INTERFACE_CONTACT_LIST +
1003                 QLatin1String("/publish"))) {
1004         uint publishState = qdbus_cast<uint>(attributes.value(
1005                     TP_QT_IFACE_CONNECTION_INTERFACE_CONTACT_LIST + QLatin1String("/publish")));
1006         QString publishRequest = qdbus_cast<QString>(attributes.value(
1007                     TP_QT_IFACE_CONNECTION_INTERFACE_CONTACT_LIST + QLatin1String("/publish-request")));
1008         setPublishState((SubscriptionState) publishState, publishRequest);
1009     }
1010 
1011     foreach (const Feature &feature, requestedFeatures) {
1012         QString maybeAlias;
1013         SimplePresence maybePresence;
1014         RequestableChannelClassList maybeCaps;
1015         QVariantMap maybeLocation;
1016         ContactInfoFieldList maybeInfo;
1017 
1018         if (feature == FeatureAlias) {
1019             maybeAlias = qdbus_cast<QString>(attributes.value(
1020                         TP_QT_IFACE_CONNECTION_INTERFACE_ALIASING + QLatin1String("/alias")));
1021 
1022             if (!maybeAlias.isEmpty()) {
1023                 receiveAlias(maybeAlias);
1024             } else if (mPriv->alias.isEmpty()) {
1025                 mPriv->alias = mPriv->id;
1026             }
1027         } else if (feature == FeatureAvatarData) {
1028             if (manager()->supportedFeatures().contains(FeatureAvatarData)) {
1029                 mPriv->actualFeatures.insert(FeatureAvatarData);
1030                 mPriv->updateAvatarData();
1031             }
1032         } else if (feature == FeatureAvatarToken) {
1033             if (attributes.contains(
1034                         TP_QT_IFACE_CONNECTION_INTERFACE_AVATARS + QLatin1String("/token"))) {
1035                 receiveAvatarToken(qdbus_cast<QString>(attributes.value(
1036                                 TP_QT_IFACE_CONNECTION_INTERFACE_AVATARS + QLatin1String("/token"))));
1037             } else {
1038                 if (manager()->supportedFeatures().contains(FeatureAvatarToken)) {
1039                     // AvatarToken being supported but not included in the mapping indicates
1040                     // that the avatar token is not known - however, the feature is working fine
1041                     mPriv->actualFeatures.insert(FeatureAvatarToken);
1042                 }
1043                 // In either case, the avatar token can't be known
1044                 mPriv->isAvatarTokenKnown = false;
1045                 mPriv->avatarToken = QLatin1String("");
1046             }
1047         } else if (feature == FeatureCapabilities) {
1048             maybeCaps = qdbus_cast<RequestableChannelClassList>(attributes.value(
1049                         TP_QT_IFACE_CONNECTION_INTERFACE_CONTACT_CAPABILITIES + QLatin1String("/capabilities")));
1050 
1051             if (!maybeCaps.isEmpty()) {
1052                 receiveCapabilities(maybeCaps);
1053             } else {
1054                 if (manager()->supportedFeatures().contains(FeatureCapabilities) &&
1055                     mPriv->requestedFeatures.contains(FeatureCapabilities)) {
1056                     // Capabilities being supported but not updated in the
1057                     // mapping indicates that the capabilities is not known -
1058                     // however, the feature is working fine.
1059                     mPriv->actualFeatures.insert(FeatureCapabilities);
1060                 }
1061             }
1062         } else if (feature == FeatureInfo) {
1063             maybeInfo = qdbus_cast<ContactInfoFieldList>(attributes.value(
1064                         TP_QT_IFACE_CONNECTION_INTERFACE_CONTACT_INFO + QLatin1String("/info")));
1065 
1066             if (!maybeInfo.isEmpty()) {
1067                 receiveInfo(maybeInfo);
1068             } else {
1069                 if (manager()->supportedFeatures().contains(FeatureInfo) &&
1070                     mPriv->requestedFeatures.contains(FeatureInfo)) {
1071                     // Info being supported but not updated in the
1072                     // mapping indicates that the info is not known -
1073                     // however, the feature is working fine
1074                     mPriv->actualFeatures.insert(FeatureInfo);
1075                 }
1076             }
1077         } else if (feature == FeatureLocation) {
1078             maybeLocation = qdbus_cast<QVariantMap>(attributes.value(
1079                         TP_QT_IFACE_CONNECTION_INTERFACE_LOCATION + QLatin1String("/location")));
1080 
1081             if (!maybeLocation.isEmpty()) {
1082                 receiveLocation(maybeLocation);
1083             } else {
1084                 if (manager()->supportedFeatures().contains(FeatureLocation) &&
1085                     mPriv->requestedFeatures.contains(FeatureLocation)) {
1086                     // Location being supported but not updated in the
1087                     // mapping indicates that the location is not known -
1088                     // however, the feature is working fine
1089                     mPriv->actualFeatures.insert(FeatureLocation);
1090                 }
1091             }
1092         } else if (feature == FeatureSimplePresence) {
1093             maybePresence = qdbus_cast<SimplePresence>(attributes.value(
1094                         TP_QT_IFACE_CONNECTION_INTERFACE_SIMPLE_PRESENCE + QLatin1String("/presence")));
1095 
1096             if (!maybePresence.status.isEmpty()) {
1097                 receiveSimplePresence(maybePresence);
1098             } else {
1099                 mPriv->presence.setStatus(ConnectionPresenceTypeUnknown,
1100                         QLatin1String("unknown"), QLatin1String(""));
1101             }
1102         } else if (feature == FeatureRosterGroups) {
1103             QStringList groups = qdbus_cast<QStringList>(attributes.value(
1104                         TP_QT_IFACE_CONNECTION_INTERFACE_CONTACT_GROUPS + QLatin1String("/groups")));
1105             mPriv->groups = groups.toSet();
1106         } else if (feature == FeatureAddresses) {
1107             VCardFieldAddressMap addresses = qdbus_cast<VCardFieldAddressMap>(attributes.value(
1108                         TP_QT_IFACE_CONNECTION_INTERFACE_ADDRESSING + QLatin1String("/addresses")));
1109             QStringList uris = qdbus_cast<QStringList>(attributes.value(
1110                         TP_QT_IFACE_CONNECTION_INTERFACE_ADDRESSING + QLatin1String("/uris")));
1111             receiveAddresses(addresses, uris);
1112         } else if (feature == FeatureClientTypes) {
1113             QStringList maybeClientTypes = qdbus_cast<QStringList>(attributes.value(
1114                         TP_QT_IFACE_CONNECTION_INTERFACE_CLIENT_TYPES + QLatin1String("/client-types")));
1115 
1116             if (!maybeClientTypes.isEmpty()) {
1117                 receiveClientTypes(maybeClientTypes);
1118             } else {
1119                 if (manager()->supportedFeatures().contains(FeatureClientTypes) &&
1120                     mPriv->requestedFeatures.contains(FeatureClientTypes)) {
1121                     // ClientTypes being supported but not updated in the
1122                     // mapping indicates that the info is not known -
1123                     // however, the feature is working fine
1124                     mPriv->actualFeatures.insert(FeatureClientTypes);
1125                 }
1126             }
1127         } else {
1128             warning() << "Unknown feature" << feature << "encountered when augmenting Contact";
1129         }
1130     }
1131 }
1132 
receiveAlias(const QString & alias)1133 void Contact::receiveAlias(const QString &alias)
1134 {
1135     if (!mPriv->requestedFeatures.contains(FeatureAlias)) {
1136         return;
1137     }
1138 
1139     mPriv->actualFeatures.insert(FeatureAlias);
1140 
1141     if (mPriv->alias != alias) {
1142         mPriv->alias = alias;
1143         emit aliasChanged(alias);
1144     }
1145 }
1146 
receiveAvatarToken(const QString & token)1147 void Contact::receiveAvatarToken(const QString &token)
1148 {
1149     setAvatarToken(token);
1150 
1151     if (mPriv->actualFeatures.contains(FeatureAvatarData)) {
1152         mPriv->updateAvatarData();
1153     }
1154 }
1155 
setAvatarToken(const QString & token)1156 void Contact::setAvatarToken(const QString &token)
1157 {
1158     if (!mPriv->requestedFeatures.contains(FeatureAvatarToken)) {
1159         return;
1160     }
1161 
1162     mPriv->actualFeatures.insert(FeatureAvatarToken);
1163 
1164     if (!mPriv->isAvatarTokenKnown || mPriv->avatarToken != token) {
1165         mPriv->isAvatarTokenKnown = true;
1166         mPriv->avatarToken = token;
1167         emit avatarTokenChanged(mPriv->avatarToken);
1168     }
1169 }
1170 
receiveAvatarData(const AvatarData & avatar)1171 void Contact::receiveAvatarData(const AvatarData &avatar)
1172 {
1173     if (mPriv->avatarData.fileName != avatar.fileName) {
1174         mPriv->avatarData = avatar;
1175         emit avatarDataChanged(mPriv->avatarData);
1176     }
1177 }
1178 
receiveSimplePresence(const SimplePresence & presence)1179 void Contact::receiveSimplePresence(const SimplePresence &presence)
1180 {
1181     if (!mPriv->requestedFeatures.contains(FeatureSimplePresence)) {
1182         return;
1183     }
1184 
1185     mPriv->actualFeatures.insert(FeatureSimplePresence);
1186 
1187     if (mPriv->presence.status() != presence.status ||
1188         mPriv->presence.statusMessage() != presence.statusMessage) {
1189         mPriv->presence.setStatus(presence);
1190         emit presenceChanged(mPriv->presence);
1191     }
1192 }
1193 
receiveCapabilities(const RequestableChannelClassList & caps)1194 void Contact::receiveCapabilities(const RequestableChannelClassList &caps)
1195 {
1196     if (!mPriv->requestedFeatures.contains(FeatureCapabilities)) {
1197         return;
1198     }
1199 
1200     mPriv->actualFeatures.insert(FeatureCapabilities);
1201 
1202     if (mPriv->caps.allClassSpecs().bareClasses() != caps) {
1203         mPriv->caps.updateRequestableChannelClasses(caps);
1204         emit capabilitiesChanged(mPriv->caps);
1205     }
1206 }
1207 
receiveLocation(const QVariantMap & location)1208 void Contact::receiveLocation(const QVariantMap &location)
1209 {
1210     if (!mPriv->requestedFeatures.contains(FeatureLocation)) {
1211         return;
1212     }
1213 
1214     mPriv->actualFeatures.insert(FeatureLocation);
1215 
1216     if (mPriv->location.allDetails() != location) {
1217         mPriv->location.updateData(location);
1218         emit locationUpdated(mPriv->location);
1219     }
1220 }
1221 
receiveInfo(const ContactInfoFieldList & info)1222 void Contact::receiveInfo(const ContactInfoFieldList &info)
1223 {
1224     if (!mPriv->requestedFeatures.contains(FeatureInfo)) {
1225         return;
1226     }
1227 
1228     mPriv->actualFeatures.insert(FeatureInfo);
1229     mPriv->isContactInfoKnown = true;
1230 
1231     if (mPriv->info.allFields() != info) {
1232         mPriv->info = InfoFields(info);
1233         emit infoFieldsChanged(mPriv->info);
1234     }
1235 }
1236 
receiveAddresses(const QMap<QString,QString> & addresses,const QStringList & uris)1237 void Contact::receiveAddresses(const QMap<QString, QString> &addresses,
1238         const QStringList &uris)
1239 {
1240     if (!mPriv->requestedFeatures.contains(FeatureAddresses)) {
1241         return;
1242     }
1243 
1244     mPriv->actualFeatures.insert(FeatureAddresses);
1245     mPriv->vcardAddresses = addresses;
1246     mPriv->uris = uris;
1247 }
1248 
receiveClientTypes(const QStringList & clientTypes)1249 void Contact::receiveClientTypes(const QStringList &clientTypes)
1250 {
1251     if (!mPriv->requestedFeatures.contains(FeatureClientTypes)) {
1252         return;
1253     }
1254 
1255     mPriv->actualFeatures.insert(FeatureClientTypes);
1256 
1257     if (mPriv->clientTypes != clientTypes) {
1258         mPriv->clientTypes = clientTypes;
1259         emit clientTypesChanged(mPriv->clientTypes);
1260     }
1261 }
1262 
subscriptionStateToPresenceState(uint subscriptionState)1263 Contact::PresenceState Contact::subscriptionStateToPresenceState(uint subscriptionState)
1264 {
1265     switch (subscriptionState) {
1266         case SubscriptionStateAsk:
1267             return PresenceStateAsk;
1268         case SubscriptionStateYes:
1269             return PresenceStateYes;
1270         default:
1271             return PresenceStateNo;
1272     }
1273 }
1274 
setSubscriptionState(SubscriptionState state)1275 void Contact::setSubscriptionState(SubscriptionState state)
1276 {
1277     if (mPriv->subscriptionState == state) {
1278         return;
1279     }
1280 
1281     mPriv->subscriptionState = state;
1282 
1283     emit subscriptionStateChanged(subscriptionStateToPresenceState(state));
1284 }
1285 
setPublishState(SubscriptionState state,const QString & message)1286 void Contact::setPublishState(SubscriptionState state, const QString &message)
1287 {
1288     if (mPriv->publishState == state && mPriv->publishStateMessage == message) {
1289         return;
1290     }
1291 
1292     mPriv->publishState = state;
1293     mPriv->publishStateMessage = message;
1294 
1295     emit publishStateChanged(subscriptionStateToPresenceState(state), message);
1296 }
1297 
setBlocked(bool value)1298 void Contact::setBlocked(bool value)
1299 {
1300     if (mPriv->blocked == value) {
1301         return;
1302     }
1303 
1304     mPriv->blocked = value;
1305 
1306     emit blockStatusChanged(value);
1307 }
1308 
setAddedToGroup(const QString & group)1309 void Contact::setAddedToGroup(const QString &group)
1310 {
1311     if (!mPriv->groups.contains(group)) {
1312         mPriv->groups.insert(group);
1313         emit addedToGroup(group);
1314     }
1315 }
1316 
setRemovedFromGroup(const QString & group)1317 void Contact::setRemovedFromGroup(const QString &group)
1318 {
1319     if (mPriv->groups.remove(group)) {
1320         emit removedFromGroup(group);
1321     }
1322 }
1323 
1324 /**
1325  * \fn void Contact::aliasChanged(const QString &alias)
1326  *
1327  * Emitted when the value of alias() changes.
1328  *
1329  * \param alias The new alias of this contact.
1330  * \sa alias()
1331  */
1332 
1333 /**
1334  * \fn void Contact::avatarTokenChanged(const QString &avatarToken)
1335  *
1336  * Emitted when the value of avatarToken() changes.
1337  *
1338  * \param avatarToken The new avatar token of this contact.
1339  * \sa avatarToken()
1340  */
1341 
1342 /**
1343  * \fn void Contact::avatarDataChanged(const QString &avatarToken)
1344  *
1345  * Emitted when the value of avatarData() changes.
1346  *
1347  * \param avatarData The new avatar of this contact.
1348  * \sa avatarData()
1349  */
1350 
1351 /**
1352  * \fn void Contact::presenceChanged(const Tp::Presence &presence)
1353  *
1354  * Emitted when the value of presence() changes.
1355  *
1356  * \param presence The new presence of this contact.
1357  * \sa presence()
1358  */
1359 
1360 /**
1361  * \fn void Contact::capabilitiesChanged(const Tp::ContactCapabilities &caps)
1362  *
1363  * Emitted when the value of capabilities() changes.
1364  *
1365  * \param caps The new capabilities of this contact.
1366  * \sa capabilities()
1367  */
1368 
1369 /**
1370  * \fn void Contact::locationUpdated(const Tp::LocationInfo &location)
1371  *
1372  * Emitted when the value of location() changes.
1373  *
1374  * \param location The new location of this contact.
1375  * \sa location()
1376  */
1377 
1378 /**
1379  * \fn void Contact::infoFieldsChanged(const Tp::Contact::InfoFields &infoFields)
1380  *
1381  * Emitted when the value of infoFields() changes.
1382  *
1383  * \param infoFields The new info of this contact.
1384  * \sa infoFields()
1385  */
1386 
1387 /**
1388  * \fn void Contact::subscriptionStateChanged(Tp::Contact::PresenceState state)
1389  *
1390  * Emitted when the value of subscriptionState() changes.
1391  *
1392  * \param state The new subscription state of this contact.
1393  * \sa subscriptionState()
1394  */
1395 
1396 /**
1397  * \fn void Contact::publishStateChanged(Tp::Contact::PresenceState state, const QString &message)
1398  *
1399  * Emitted when the value of publishState() changes.
1400  *
1401  * \param state The new publish state of this contact.
1402  * \param message The new user-defined status message of this contact.
1403  * \sa publishState()
1404  */
1405 
1406 /**
1407  * \fn void Contact::blockStatusChanged(bool blocked)
1408  *
1409  * Emitted when the value of isBlocked() changes.
1410  *
1411  * \param blocked The new block status of this contact.
1412  * \sa isBlocked()
1413  */
1414 
1415 /**
1416  * \fn void Contact::addedToGroup(const QString &group)
1417  *
1418  * Emitted when this contact is added to \a group of the contact list.
1419  *
1420  * \param group The group name.
1421  * \sa groups(), removedFromGroup()
1422  */
1423 
1424 /**
1425  * \fn void Contact::removedFromGroup(const QString &group)
1426  *
1427  * Emitted when this contact is removed from \a group of the contact list.
1428  *
1429  * \param group The group name.
1430  * \sa groups(), addedToGroup()
1431  */
1432 
1433 /**
1434  * \fn void Contact::clientTypesChanged(const QStringList &clientTypes)
1435  *
1436  * Emitted when the client types of this contact change or become known.
1437  *
1438  * \param clientTypes The contact's client types
1439  * \sa clientTypes(), requestClientTypes()
1440  */
1441 
1442 } // Tp
1443