~tiagosh/telepathy-qt/group-chat2

« back to all changes in this revision

Viewing changes to TelepathyQt/channel.cpp

  • Committer: Package Import Robot
  • Author(s): Ricardo Salveti de Araujo
  • Date: 2013-06-06 04:56:14 UTC
  • Revision ID: package-import@ubuntu.com-20130606045614-inpxexo6765rnmp1
Tags: upstream-0.9.3
ImportĀ upstreamĀ versionĀ 0.9.3

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/**
 
2
 * This file is part of TelepathyQt
 
3
 *
 
4
 * @copyright Copyright (C) 2008 Collabora Ltd. <http://www.collabora.co.uk/>
 
5
 * @copyright Copyright (C) 2008 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/Channel>
 
24
#include "TelepathyQt/channel-internal.h"
 
25
 
 
26
#include "TelepathyQt/_gen/cli-channel-body.hpp"
 
27
#include "TelepathyQt/_gen/cli-channel.moc.hpp"
 
28
#include "TelepathyQt/_gen/channel.moc.hpp"
 
29
#include "TelepathyQt/_gen/channel-internal.moc.hpp"
 
30
 
 
31
#include "TelepathyQt/debug-internal.h"
 
32
 
 
33
#include "TelepathyQt/future-internal.h"
 
34
 
 
35
#include <TelepathyQt/ChannelFactory>
 
36
#include <TelepathyQt/Connection>
 
37
#include <TelepathyQt/ConnectionCapabilities>
 
38
#include <TelepathyQt/ConnectionLowlevel>
 
39
#include <TelepathyQt/ContactManager>
 
40
#include <TelepathyQt/PendingContacts>
 
41
#include <TelepathyQt/PendingFailure>
 
42
#include <TelepathyQt/PendingOperation>
 
43
#include <TelepathyQt/PendingReady>
 
44
#include <TelepathyQt/PendingSuccess>
 
45
#include <TelepathyQt/StreamTubeChannel>
 
46
#include <TelepathyQt/ReferencedHandles>
 
47
#include <TelepathyQt/Constants>
 
48
 
 
49
#include <QHash>
 
50
#include <QQueue>
 
51
#include <QSharedData>
 
52
#include <QTimer>
 
53
 
 
54
namespace Tp
 
55
{
 
56
 
 
57
using TpFuture::Client::ChannelInterfaceMergeableConferenceInterface;
 
58
using TpFuture::Client::ChannelInterfaceSplittableInterface;
 
59
 
 
60
struct TP_QT_NO_EXPORT Channel::Private
 
61
{
 
62
    Private(Channel *parent, const ConnectionPtr &connection,
 
63
            const QVariantMap &immutableProperties);
 
64
    ~Private();
 
65
 
 
66
    static void introspectMain(Private *self);
 
67
    void introspectMainProperties();
 
68
    void introspectMainFallbackChannelType();
 
69
    void introspectMainFallbackHandle();
 
70
    void introspectMainFallbackInterfaces();
 
71
    void introspectGroup();
 
72
    void introspectGroupFallbackFlags();
 
73
    void introspectGroupFallbackMembers();
 
74
    void introspectGroupFallbackLocalPendingWithInfo();
 
75
    void introspectGroupFallbackSelfHandle();
 
76
    void introspectConference();
 
77
 
 
78
    static void introspectConferenceInitialInviteeContacts(Private *self);
 
79
 
 
80
    void continueIntrospection();
 
81
 
 
82
    void extractMainProps(const QVariantMap &props);
 
83
    void extract0176GroupProps(const QVariantMap &props);
 
84
 
 
85
    void nowHaveInterfaces();
 
86
    void nowHaveInitialMembers();
 
87
 
 
88
    bool setGroupFlags(uint groupFlags);
 
89
 
 
90
    void buildContacts();
 
91
    void doMembersChangedDetailed(const UIntList &, const UIntList &, const UIntList &,
 
92
            const UIntList &, const QVariantMap &);
 
93
    void processMembersChanged();
 
94
    void updateContacts(const QList<ContactPtr> &contacts =
 
95
            QList<ContactPtr>());
 
96
    bool fakeGroupInterfaceIfNeeded();
 
97
    void setReady();
 
98
 
 
99
    QString groupMemberChangeDetailsTelepathyError(
 
100
            const GroupMemberChangeDetails &details);
 
101
 
 
102
    inline ChannelInterfaceMergeableConferenceInterface *mergeableConferenceInterface(
 
103
            InterfaceSupportedChecking check = CheckInterfaceSupported) const
 
104
    {
 
105
        return parent->optionalInterface<ChannelInterfaceMergeableConferenceInterface>(check);
 
106
    }
 
107
 
 
108
    inline ChannelInterfaceSplittableInterface *splittableInterface(
 
109
            InterfaceSupportedChecking check = CheckInterfaceSupported) const
 
110
    {
 
111
        return parent->optionalInterface<ChannelInterfaceSplittableInterface>(check);
 
112
    }
 
113
 
 
114
    void processConferenceChannelRemoved();
 
115
 
 
116
    struct GroupMembersChangedInfo;
 
117
    struct ConferenceChannelRemovedInfo;
 
118
 
 
119
    // Public object
 
120
    Channel *parent;
 
121
 
 
122
    // Instance of generated interface class
 
123
    Client::ChannelInterface *baseInterface;
 
124
 
 
125
    // Mandatory properties interface proxy
 
126
    Client::DBus::PropertiesInterface *properties;
 
127
 
 
128
    // Owning connection - it can be a SharedPtr as Connection does not cache
 
129
    // channels
 
130
    ConnectionPtr connection;
 
131
 
 
132
    QVariantMap immutableProperties;
 
133
 
 
134
    // Optional interface proxies
 
135
    Client::ChannelInterfaceGroupInterface *group;
 
136
    Client::ChannelInterfaceConferenceInterface *conference;
 
137
 
 
138
    ReadinessHelper *readinessHelper;
 
139
 
 
140
    // Introspection
 
141
    QQueue<void (Private::*)()> introspectQueue;
 
142
 
 
143
    // Introspected properties
 
144
 
 
145
    // Main interface
 
146
    QString channelType;
 
147
    uint targetHandleType;
 
148
    uint targetHandle;
 
149
    QString targetId;
 
150
    ContactPtr targetContact;
 
151
    bool requested;
 
152
    uint initiatorHandle;
 
153
    ContactPtr initiatorContact;
 
154
 
 
155
    // Group flags
 
156
    uint groupFlags;
 
157
    bool usingMembersChangedDetailed;
 
158
 
 
159
    // Group member introspection
 
160
    bool groupHaveMembers;
 
161
    bool buildingContacts;
 
162
 
 
163
    // Queue of received MCD signals to process
 
164
    QQueue<GroupMembersChangedInfo *> groupMembersChangedQueue;
 
165
    GroupMembersChangedInfo *currentGroupMembersChangedInfo;
 
166
 
 
167
    // Pending from the MCD signal currently processed, but contacts not yet built
 
168
    QSet<uint> pendingGroupMembers;
 
169
    QSet<uint> pendingGroupLocalPendingMembers;
 
170
    QSet<uint> pendingGroupRemotePendingMembers;
 
171
    UIntList groupMembersToRemove;
 
172
    UIntList groupLocalPendingMembersToRemove;
 
173
    UIntList groupRemotePendingMembersToRemove;
 
174
 
 
175
    // Initial members
 
176
    UIntList groupInitialMembers;
 
177
    LocalPendingInfoList groupInitialLP;
 
178
    UIntList groupInitialRP;
 
179
 
 
180
    // Current members
 
181
    QHash<uint, ContactPtr> groupContacts;
 
182
    QHash<uint, ContactPtr> groupLocalPendingContacts;
 
183
    QHash<uint, ContactPtr> groupRemotePendingContacts;
 
184
 
 
185
    // Stored change info
 
186
    QHash<uint, GroupMemberChangeDetails> groupLocalPendingContactsChangeInfo;
 
187
    GroupMemberChangeDetails groupSelfContactRemoveInfo;
 
188
 
 
189
    // Group handle owners
 
190
    bool groupAreHandleOwnersAvailable;
 
191
    HandleOwnerMap groupHandleOwners;
 
192
 
 
193
    // Group self identity
 
194
    bool pendingRetrieveGroupSelfContact;
 
195
    bool groupIsSelfHandleTracked;
 
196
    uint groupSelfHandle;
 
197
    ContactPtr groupSelfContact;
 
198
 
 
199
    // Conference
 
200
    bool introspectingConference;
 
201
    QHash<QString, ChannelPtr> conferenceChannels;
 
202
    QHash<QString, ChannelPtr> conferenceInitialChannels;
 
203
    QString conferenceInvitationMessage;
 
204
    QHash<uint, ChannelPtr> conferenceOriginalChannels;
 
205
    UIntList conferenceInitialInviteeHandles;
 
206
    Contacts conferenceInitialInviteeContacts;
 
207
    QQueue<ConferenceChannelRemovedInfo *> conferenceChannelRemovedQueue;
 
208
    bool buildingConferenceChannelRemovedActorContact;
 
209
 
 
210
    static const QString keyActor;
 
211
};
 
212
 
 
213
struct TP_QT_NO_EXPORT Channel::Private::GroupMembersChangedInfo
 
214
{
 
215
    GroupMembersChangedInfo(const UIntList &added, const UIntList &removed,
 
216
            const UIntList &localPending, const UIntList &remotePending,
 
217
            const QVariantMap &details)
 
218
        : added(added),
 
219
          removed(removed),
 
220
          localPending(localPending),
 
221
          remotePending(remotePending),
 
222
          details(details),
 
223
          // TODO most of these probably should be removed once the rest of the code using them is sanitized
 
224
          actor(qdbus_cast<uint>(details.value(keyActor))),
 
225
          reason(qdbus_cast<uint>(details.value(keyChangeReason))),
 
226
          message(qdbus_cast<QString>(details.value(keyMessage)))
 
227
    {
 
228
    }
 
229
 
 
230
    UIntList added;
 
231
    UIntList removed;
 
232
    UIntList localPending;
 
233
    UIntList remotePending;
 
234
    QVariantMap details;
 
235
    uint actor;
 
236
    uint reason;
 
237
    QString message;
 
238
 
 
239
    static const QString keyChangeReason;
 
240
    static const QString keyMessage;
 
241
    static const QString keyContactIds;
 
242
};
 
243
 
 
244
struct TP_QT_NO_EXPORT Channel::Private::ConferenceChannelRemovedInfo
 
245
{
 
246
    ConferenceChannelRemovedInfo(const QDBusObjectPath &channelPath, const QVariantMap &details)
 
247
        : channelPath(channelPath),
 
248
          details(details)
 
249
    {
 
250
    }
 
251
 
 
252
    QDBusObjectPath channelPath;
 
253
    QVariantMap details;
 
254
};
 
255
 
 
256
const QString Channel::Private::keyActor(QLatin1String("actor"));
 
257
const QString Channel::Private::GroupMembersChangedInfo::keyChangeReason(
 
258
        QLatin1String("change-reason"));
 
259
const QString Channel::Private::GroupMembersChangedInfo::keyMessage(QLatin1String("message"));
 
260
const QString Channel::Private::GroupMembersChangedInfo::keyContactIds(QLatin1String("contact-ids"));
 
261
 
 
262
Channel::Private::Private(Channel *parent, const ConnectionPtr &connection,
 
263
        const QVariantMap &immutableProperties)
 
264
    : parent(parent),
 
265
      baseInterface(new Client::ChannelInterface(parent)),
 
266
      properties(parent->interface<Client::DBus::PropertiesInterface>()),
 
267
      connection(connection),
 
268
      immutableProperties(immutableProperties),
 
269
      group(0),
 
270
      conference(0),
 
271
      readinessHelper(parent->readinessHelper()),
 
272
      targetHandleType(0),
 
273
      targetHandle(0),
 
274
      requested(false),
 
275
      initiatorHandle(0),
 
276
      groupFlags(0),
 
277
      usingMembersChangedDetailed(false),
 
278
      groupHaveMembers(false),
 
279
      buildingContacts(false),
 
280
      currentGroupMembersChangedInfo(0),
 
281
      groupAreHandleOwnersAvailable(false),
 
282
      pendingRetrieveGroupSelfContact(false),
 
283
      groupIsSelfHandleTracked(false),
 
284
      groupSelfHandle(0),
 
285
      introspectingConference(false),
 
286
      buildingConferenceChannelRemovedActorContact(false)
 
287
{
 
288
    debug() << "Creating new Channel:" << parent->objectPath();
 
289
 
 
290
    if (connection->isValid()) {
 
291
        debug() << " Connecting to Channel::Closed() signal";
 
292
        parent->connect(baseInterface,
 
293
                        SIGNAL(Closed()),
 
294
                        SLOT(onClosed()));
 
295
 
 
296
        debug() << " Connection to owning connection's lifetime signals";
 
297
        parent->connect(connection.data(),
 
298
                        SIGNAL(invalidated(Tp::DBusProxy*,QString,QString)),
 
299
                        SLOT(onConnectionInvalidated()));
 
300
    }
 
301
    else {
 
302
        warning() << "Connection given as the owner for a Channel was "
 
303
            "invalid! Channel will be stillborn.";
 
304
        parent->invalidate(TP_QT_ERROR_INVALID_ARGUMENT,
 
305
                QLatin1String("Connection given as the owner of this channel was invalid"));
 
306
    }
 
307
 
 
308
    ReadinessHelper::Introspectables introspectables;
 
309
 
 
310
    // As Channel does not have predefined statuses let's simulate one (0)
 
311
    ReadinessHelper::Introspectable introspectableCore(
 
312
        QSet<uint>() << 0,                                           // makesSenseForStatuses
 
313
        Features(),                                                  // dependsOnFeatures
 
314
        QStringList(),                                               // dependsOnInterfaces
 
315
        (ReadinessHelper::IntrospectFunc) &Private::introspectMain,
 
316
        this);
 
317
    introspectables[FeatureCore] = introspectableCore;
 
318
 
 
319
    // As Channel does not have predefined statuses let's simulate one (0)
 
320
    ReadinessHelper::Introspectable introspectableConferenceInitialInviteeContacts(
 
321
        QSet<uint>() << 0,                                           // makesSenseForStatuses
 
322
        Features() << FeatureCore,                                   // dependsOnFeatures
 
323
        QStringList() << TP_QT_IFACE_CHANNEL_INTERFACE_CONFERENCE,  // dependsOnInterfaces
 
324
        (ReadinessHelper::IntrospectFunc) &Private::introspectConferenceInitialInviteeContacts,
 
325
        this);
 
326
    introspectables[FeatureConferenceInitialInviteeContacts] =
 
327
        introspectableConferenceInitialInviteeContacts;
 
328
 
 
329
    readinessHelper->addIntrospectables(introspectables);
 
330
}
 
331
 
 
332
Channel::Private::~Private()
 
333
{
 
334
    delete currentGroupMembersChangedInfo;
 
335
    foreach (GroupMembersChangedInfo *info, groupMembersChangedQueue) {
 
336
        delete info;
 
337
    }
 
338
    foreach (ConferenceChannelRemovedInfo *info, conferenceChannelRemovedQueue) {
 
339
        delete info;
 
340
    }
 
341
}
 
342
 
 
343
void Channel::Private::introspectMain(Channel::Private *self)
 
344
{
 
345
    // Make sure connection object is ready, as we need to use some methods that
 
346
    // are only available after connection object gets ready.
 
347
    debug() << "Calling Connection::becomeReady()";
 
348
    self->parent->connect(self->connection->becomeReady(),
 
349
            SIGNAL(finished(Tp::PendingOperation*)),
 
350
            SLOT(onConnectionReady(Tp::PendingOperation*)));
 
351
}
 
352
 
 
353
void Channel::Private::introspectMainProperties()
 
354
{
 
355
    QVariantMap props;
 
356
    QString key;
 
357
    bool needIntrospectMainProps = false;
 
358
    const unsigned numNames = 8;
 
359
    const static QString names[numNames] = {
 
360
        QLatin1String("ChannelType"),
 
361
        QLatin1String("Interfaces"),
 
362
        QLatin1String("TargetHandleType"),
 
363
        QLatin1String("TargetHandle"),
 
364
        QLatin1String("TargetID"),
 
365
        QLatin1String("Requested"),
 
366
        QLatin1String("InitiatorHandle"),
 
367
        QLatin1String("InitiatorID")
 
368
    };
 
369
    const static QString qualifiedNames[numNames] = {
 
370
        TP_QT_IFACE_CHANNEL + QLatin1String(".ChannelType"),
 
371
        TP_QT_IFACE_CHANNEL + QLatin1String(".Interfaces"),
 
372
        TP_QT_IFACE_CHANNEL + QLatin1String(".TargetHandleType"),
 
373
        TP_QT_IFACE_CHANNEL + QLatin1String(".TargetHandle"),
 
374
        TP_QT_IFACE_CHANNEL + QLatin1String(".TargetID"),
 
375
        TP_QT_IFACE_CHANNEL + QLatin1String(".Requested"),
 
376
        TP_QT_IFACE_CHANNEL + QLatin1String(".InitiatorHandle"),
 
377
        TP_QT_IFACE_CHANNEL + QLatin1String(".InitiatorID")
 
378
    };
 
379
    for (unsigned i = 0; i < numNames; ++i) {
 
380
        const QString &qualified = qualifiedNames[i];
 
381
        if (!immutableProperties.contains(qualified)) {
 
382
            needIntrospectMainProps = true;
 
383
            break;
 
384
        }
 
385
        props.insert(names[i], immutableProperties.value(qualified));
 
386
    }
 
387
 
 
388
    // Save Requested and InitiatorHandle here, so even if the GetAll return doesn't have them but
 
389
    // the given immutable props do (eg. due to the PendingChannel fallback guesses) we use them
 
390
    requested = qdbus_cast<bool>(props[QLatin1String("Requested")]);
 
391
    initiatorHandle = qdbus_cast<uint>(props[QLatin1String("InitiatorHandle")]);
 
392
 
 
393
    if (props.contains(QLatin1String("InitiatorID"))) {
 
394
        QString initiatorId = qdbus_cast<QString>(props[QLatin1String("InitiatorID")]);
 
395
        connection->lowlevel()->injectContactId(initiatorHandle, initiatorId);
 
396
    }
 
397
 
 
398
    if (needIntrospectMainProps) {
 
399
        debug() << "Calling Properties::GetAll(Channel)";
 
400
        QDBusPendingCallWatcher *watcher =
 
401
            new QDBusPendingCallWatcher(
 
402
                    properties->GetAll(TP_QT_IFACE_CHANNEL),
 
403
                    parent);
 
404
        parent->connect(watcher,
 
405
                SIGNAL(finished(QDBusPendingCallWatcher*)),
 
406
                SLOT(gotMainProperties(QDBusPendingCallWatcher*)));
 
407
    } else {
 
408
        extractMainProps(props);
 
409
        continueIntrospection();
 
410
    }
 
411
}
 
412
 
 
413
void Channel::Private::introspectMainFallbackChannelType()
 
414
{
 
415
    debug() << "Calling Channel::GetChannelType()";
 
416
    QDBusPendingCallWatcher *watcher =
 
417
        new QDBusPendingCallWatcher(baseInterface->GetChannelType(), parent);
 
418
    parent->connect(watcher,
 
419
                    SIGNAL(finished(QDBusPendingCallWatcher*)),
 
420
                    SLOT(gotChannelType(QDBusPendingCallWatcher*)));
 
421
}
 
422
 
 
423
void Channel::Private::introspectMainFallbackHandle()
 
424
{
 
425
    debug() << "Calling Channel::GetHandle()";
 
426
    QDBusPendingCallWatcher *watcher =
 
427
        new QDBusPendingCallWatcher(baseInterface->GetHandle(), parent);
 
428
    parent->connect(watcher,
 
429
                    SIGNAL(finished(QDBusPendingCallWatcher*)),
 
430
                    SLOT(gotHandle(QDBusPendingCallWatcher*)));
 
431
}
 
432
 
 
433
void Channel::Private::introspectMainFallbackInterfaces()
 
434
{
 
435
    debug() << "Calling Channel::GetInterfaces()";
 
436
    QDBusPendingCallWatcher *watcher =
 
437
        new QDBusPendingCallWatcher(baseInterface->GetInterfaces(), parent);
 
438
    parent->connect(watcher,
 
439
                    SIGNAL(finished(QDBusPendingCallWatcher*)),
 
440
                    SLOT(gotInterfaces(QDBusPendingCallWatcher*)));
 
441
}
 
442
 
 
443
void Channel::Private::introspectGroup()
 
444
{
 
445
    Q_ASSERT(properties != 0);
 
446
 
 
447
    if (!group) {
 
448
        group = parent->interface<Client::ChannelInterfaceGroupInterface>();
 
449
        Q_ASSERT(group != 0);
 
450
    }
 
451
 
 
452
    debug() << "Introspecting Channel.Interface.Group for" << parent->objectPath();
 
453
 
 
454
    parent->connect(group,
 
455
                    SIGNAL(GroupFlagsChanged(uint,uint)),
 
456
                    SLOT(onGroupFlagsChanged(uint,uint)));
 
457
 
 
458
    parent->connect(group,
 
459
                    SIGNAL(MembersChanged(QString,Tp::UIntList,
 
460
                            Tp::UIntList,Tp::UIntList,
 
461
                            Tp::UIntList,uint,uint)),
 
462
                    SLOT(onMembersChanged(QString,Tp::UIntList,
 
463
                            Tp::UIntList,Tp::UIntList,
 
464
                            Tp::UIntList,uint,uint)));
 
465
    parent->connect(group,
 
466
                    SIGNAL(MembersChangedDetailed(Tp::UIntList,
 
467
                            Tp::UIntList,Tp::UIntList,
 
468
                            Tp::UIntList,QVariantMap)),
 
469
                    SLOT(onMembersChangedDetailed(Tp::UIntList,
 
470
                            Tp::UIntList,Tp::UIntList,
 
471
                            Tp::UIntList,QVariantMap)));
 
472
 
 
473
    parent->connect(group,
 
474
                    SIGNAL(HandleOwnersChanged(Tp::HandleOwnerMap,
 
475
                            Tp::UIntList)),
 
476
                    SLOT(onHandleOwnersChanged(Tp::HandleOwnerMap,
 
477
                            Tp::UIntList)));
 
478
 
 
479
    parent->connect(group,
 
480
                    SIGNAL(SelfHandleChanged(uint)),
 
481
                    SLOT(onSelfHandleChanged(uint)));
 
482
 
 
483
    debug() << "Calling Properties::GetAll(Channel.Interface.Group)";
 
484
    QDBusPendingCallWatcher *watcher =
 
485
        new QDBusPendingCallWatcher(
 
486
                properties->GetAll(TP_QT_IFACE_CHANNEL_INTERFACE_GROUP),
 
487
                parent);
 
488
    parent->connect(watcher,
 
489
                    SIGNAL(finished(QDBusPendingCallWatcher*)),
 
490
                    SLOT(gotGroupProperties(QDBusPendingCallWatcher*)));
 
491
}
 
492
 
 
493
void Channel::Private::introspectGroupFallbackFlags()
 
494
{
 
495
    Q_ASSERT(group != 0);
 
496
 
 
497
    debug() << "Calling Channel.Interface.Group::GetGroupFlags()";
 
498
    QDBusPendingCallWatcher *watcher =
 
499
        new QDBusPendingCallWatcher(group->GetGroupFlags(), parent);
 
500
    parent->connect(watcher,
 
501
                    SIGNAL(finished(QDBusPendingCallWatcher*)),
 
502
                    SLOT(gotGroupFlags(QDBusPendingCallWatcher*)));
 
503
}
 
504
 
 
505
void Channel::Private::introspectGroupFallbackMembers()
 
506
{
 
507
    Q_ASSERT(group != 0);
 
508
 
 
509
    debug() << "Calling Channel.Interface.Group::GetAllMembers()";
 
510
    QDBusPendingCallWatcher *watcher =
 
511
        new QDBusPendingCallWatcher(group->GetAllMembers(), parent);
 
512
    parent->connect(watcher,
 
513
                    SIGNAL(finished(QDBusPendingCallWatcher*)),
 
514
                    SLOT(gotAllMembers(QDBusPendingCallWatcher*)));
 
515
}
 
516
 
 
517
void Channel::Private::introspectGroupFallbackLocalPendingWithInfo()
 
518
{
 
519
    Q_ASSERT(group != 0);
 
520
 
 
521
    debug() << "Calling Channel.Interface.Group::GetLocalPendingMembersWithInfo()";
 
522
    QDBusPendingCallWatcher *watcher =
 
523
        new QDBusPendingCallWatcher(group->GetLocalPendingMembersWithInfo(),
 
524
                parent);
 
525
    parent->connect(watcher,
 
526
                    SIGNAL(finished(QDBusPendingCallWatcher*)),
 
527
                    SLOT(gotLocalPendingMembersWithInfo(QDBusPendingCallWatcher*)));
 
528
}
 
529
 
 
530
void Channel::Private::introspectGroupFallbackSelfHandle()
 
531
{
 
532
    Q_ASSERT(group != 0);
 
533
 
 
534
    debug() << "Calling Channel.Interface.Group::GetSelfHandle()";
 
535
    QDBusPendingCallWatcher *watcher =
 
536
        new QDBusPendingCallWatcher(group->GetSelfHandle(), parent);
 
537
    parent->connect(watcher,
 
538
                    SIGNAL(finished(QDBusPendingCallWatcher*)),
 
539
                    SLOT(gotSelfHandle(QDBusPendingCallWatcher*)));
 
540
}
 
541
 
 
542
void Channel::Private::introspectConference()
 
543
{
 
544
    Q_ASSERT(properties != 0);
 
545
    Q_ASSERT(conference == 0);
 
546
 
 
547
    debug() << "Introspecting Conference interface";
 
548
    conference = parent->interface<Client::ChannelInterfaceConferenceInterface>();
 
549
    Q_ASSERT(conference != 0);
 
550
 
 
551
    introspectingConference = true;
 
552
 
 
553
    debug() << "Connecting to Channel.Interface.Conference.ChannelMerged/Removed";
 
554
    parent->connect(conference,
 
555
            SIGNAL(ChannelMerged(QDBusObjectPath,uint,QVariantMap)),
 
556
            SLOT(onConferenceChannelMerged(QDBusObjectPath,uint,QVariantMap)));
 
557
    parent->connect(conference,
 
558
            SIGNAL(ChannelRemoved(QDBusObjectPath,QVariantMap)),
 
559
            SLOT(onConferenceChannelRemoved(QDBusObjectPath,QVariantMap)));
 
560
 
 
561
    debug() << "Calling Properties::GetAll(Channel.Interface.Conference)";
 
562
    QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(
 
563
            properties->GetAll(TP_QT_IFACE_CHANNEL_INTERFACE_CONFERENCE),
 
564
            parent);
 
565
    parent->connect(watcher,
 
566
            SIGNAL(finished(QDBusPendingCallWatcher*)),
 
567
            SLOT(gotConferenceProperties(QDBusPendingCallWatcher*)));
 
568
}
 
569
 
 
570
void Channel::Private::introspectConferenceInitialInviteeContacts(Private *self)
 
571
{
 
572
    if (!self->conferenceInitialInviteeHandles.isEmpty()) {
 
573
        ContactManagerPtr manager = self->connection->contactManager();
 
574
        PendingContacts *pendingContacts = manager->contactsForHandles(
 
575
                self->conferenceInitialInviteeHandles);
 
576
        self->parent->connect(pendingContacts,
 
577
                SIGNAL(finished(Tp::PendingOperation *)),
 
578
                SLOT(gotConferenceInitialInviteeContacts(Tp::PendingOperation *)));
 
579
    } else {
 
580
        self->readinessHelper->setIntrospectCompleted(
 
581
                FeatureConferenceInitialInviteeContacts, true);
 
582
    }
 
583
}
 
584
 
 
585
void Channel::Private::continueIntrospection()
 
586
{
 
587
    if (introspectQueue.isEmpty()) {
 
588
        // this should always be true, but let's make sure
 
589
        if (!parent->isReady(Channel::FeatureCore)) {
 
590
            if (groupMembersChangedQueue.isEmpty() && !buildingContacts &&
 
591
                !introspectingConference) {
 
592
                debug() << "Both the IS and the MCD queue empty for the first time. Ready.";
 
593
                setReady();
 
594
            } else {
 
595
                debug() << "Introspection done before contacts done - contacts sets ready";
 
596
            }
 
597
        }
 
598
    } else {
 
599
        (this->*(introspectQueue.dequeue()))();
 
600
    }
 
601
}
 
602
 
 
603
void Channel::Private::extractMainProps(const QVariantMap &props)
 
604
{
 
605
    const static QString keyChannelType(QLatin1String("ChannelType"));
 
606
    const static QString keyInterfaces(QLatin1String("Interfaces"));
 
607
    const static QString keyTargetHandle(QLatin1String("TargetHandle"));
 
608
    const static QString keyTargetHandleType(QLatin1String("TargetHandleType"));
 
609
 
 
610
    bool haveProps = props.size() >= 4
 
611
                  && props.contains(keyChannelType)
 
612
                  && !qdbus_cast<QString>(props[keyChannelType]).isEmpty()
 
613
                  && props.contains(keyInterfaces)
 
614
                  && props.contains(keyTargetHandle)
 
615
                  && props.contains(keyTargetHandleType);
 
616
 
 
617
    if (!haveProps) {
 
618
        warning() << "Channel properties specified in 0.17.7 not found";
 
619
 
 
620
        introspectQueue.enqueue(&Private::introspectMainFallbackChannelType);
 
621
        introspectQueue.enqueue(&Private::introspectMainFallbackHandle);
 
622
        introspectQueue.enqueue(&Private::introspectMainFallbackInterfaces);
 
623
    } else {
 
624
        parent->setInterfaces(qdbus_cast<QStringList>(props[keyInterfaces]));
 
625
        readinessHelper->setInterfaces(parent->interfaces());
 
626
        channelType = qdbus_cast<QString>(props[keyChannelType]);
 
627
        targetHandle = qdbus_cast<uint>(props[keyTargetHandle]);
 
628
        targetHandleType = qdbus_cast<uint>(props[keyTargetHandleType]);
 
629
 
 
630
        const static QString keyTargetId(QLatin1String("TargetID"));
 
631
        const static QString keyRequested(QLatin1String("Requested"));
 
632
        const static QString keyInitiatorHandle(QLatin1String("InitiatorHandle"));
 
633
        const static QString keyInitiatorId(QLatin1String("InitiatorID"));
 
634
 
 
635
        if (props.contains(keyTargetId)) {
 
636
            targetId = qdbus_cast<QString>(props[keyTargetId]);
 
637
 
 
638
            if (targetHandleType == HandleTypeContact) {
 
639
                connection->lowlevel()->injectContactId(targetHandle, targetId);
 
640
            }
 
641
        }
 
642
 
 
643
        if (props.contains(keyRequested)) {
 
644
            requested = qdbus_cast<uint>(props[keyRequested]);
 
645
        }
 
646
 
 
647
        if (props.contains(keyInitiatorHandle)) {
 
648
            initiatorHandle = qdbus_cast<uint>(props[keyInitiatorHandle]);
 
649
        }
 
650
 
 
651
        if (props.contains(keyInitiatorId)) {
 
652
            QString initiatorId = qdbus_cast<QString>(props[keyInitiatorId]);
 
653
            connection->lowlevel()->injectContactId(initiatorHandle, initiatorId);
 
654
        }
 
655
 
 
656
        if (!fakeGroupInterfaceIfNeeded() &&
 
657
            !parent->interfaces().contains(TP_QT_IFACE_CHANNEL_INTERFACE_GROUP) &&
 
658
            initiatorHandle) {
 
659
            // No group interface, so nobody will build the poor fellow for us. Will do it ourselves
 
660
            // out of pity for him.
 
661
            // TODO: needs testing. I would imagine some of the elaborate updateContacts logic
 
662
            // tripping over with just this.
 
663
            buildContacts();
 
664
        }
 
665
 
 
666
        nowHaveInterfaces();
 
667
    }
 
668
 
 
669
    debug() << "Have initiator handle:" << (initiatorHandle ? "yes" : "no");
 
670
}
 
671
 
 
672
void Channel::Private::extract0176GroupProps(const QVariantMap &props)
 
673
{
 
674
    const static QString keyGroupFlags(QLatin1String("GroupFlags"));
 
675
    const static QString keyHandleOwners(QLatin1String("HandleOwners"));
 
676
    const static QString keyLPMembers(QLatin1String("LocalPendingMembers"));
 
677
    const static QString keyMembers(QLatin1String("Members"));
 
678
    const static QString keyRPMembers(QLatin1String("RemotePendingMembers"));
 
679
    const static QString keySelfHandle(QLatin1String("SelfHandle"));
 
680
 
 
681
    bool haveProps = props.size() >= 6
 
682
                  && (props.contains(keyGroupFlags)
 
683
                  && (qdbus_cast<uint>(props[keyGroupFlags]) &
 
684
                      ChannelGroupFlagProperties))
 
685
                  && props.contains(keyHandleOwners)
 
686
                  && props.contains(keyLPMembers)
 
687
                  && props.contains(keyMembers)
 
688
                  && props.contains(keyRPMembers)
 
689
                  && props.contains(keySelfHandle);
 
690
 
 
691
    if (!haveProps) {
 
692
        warning() << " Properties specified in 0.17.6 not found";
 
693
        warning() << "  Handle owners and self handle tracking disabled";
 
694
 
 
695
        introspectQueue.enqueue(&Private::introspectGroupFallbackFlags);
 
696
        introspectQueue.enqueue(&Private::introspectGroupFallbackMembers);
 
697
        introspectQueue.enqueue(&Private::introspectGroupFallbackLocalPendingWithInfo);
 
698
        introspectQueue.enqueue(&Private::introspectGroupFallbackSelfHandle);
 
699
    } else {
 
700
        debug() << " Found properties specified in 0.17.6";
 
701
 
 
702
        groupAreHandleOwnersAvailable = true;
 
703
        groupIsSelfHandleTracked = true;
 
704
 
 
705
        setGroupFlags(qdbus_cast<uint>(props[keyGroupFlags]));
 
706
        groupHandleOwners = qdbus_cast<HandleOwnerMap>(props[keyHandleOwners]);
 
707
 
 
708
        groupInitialMembers = qdbus_cast<UIntList>(props[keyMembers]);
 
709
        groupInitialLP = qdbus_cast<LocalPendingInfoList>(props[keyLPMembers]);
 
710
        groupInitialRP = qdbus_cast<UIntList>(props[keyRPMembers]);
 
711
 
 
712
        uint propSelfHandle = qdbus_cast<uint>(props[keySelfHandle]);
 
713
        // Don't overwrite the self handle we got from the Connection with 0
 
714
        if (propSelfHandle) {
 
715
            groupSelfHandle = propSelfHandle;
 
716
        }
 
717
 
 
718
        nowHaveInitialMembers();
 
719
    }
 
720
}
 
721
 
 
722
void Channel::Private::nowHaveInterfaces()
 
723
{
 
724
    debug() << "Channel has" << parent->interfaces().size() <<
 
725
        "optional interfaces:" << parent->interfaces();
 
726
 
 
727
    QStringList interfaces = parent->interfaces();
 
728
 
 
729
    if (interfaces.contains(TP_QT_IFACE_CHANNEL_INTERFACE_GROUP)) {
 
730
        introspectQueue.enqueue(&Private::introspectGroup);
 
731
    }
 
732
 
 
733
    if (interfaces.contains(TP_QT_IFACE_CHANNEL_INTERFACE_CONFERENCE)) {
 
734
        introspectQueue.enqueue(&Private::introspectConference);
 
735
    }
 
736
}
 
737
 
 
738
void Channel::Private::nowHaveInitialMembers()
 
739
{
 
740
    // Must be called with no contacts anywhere in the first place
 
741
    Q_ASSERT(!parent->isReady(Channel::FeatureCore));
 
742
    Q_ASSERT(!buildingContacts);
 
743
 
 
744
    Q_ASSERT(pendingGroupMembers.isEmpty());
 
745
    Q_ASSERT(pendingGroupLocalPendingMembers.isEmpty());
 
746
    Q_ASSERT(pendingGroupRemotePendingMembers.isEmpty());
 
747
 
 
748
    Q_ASSERT(groupContacts.isEmpty());
 
749
    Q_ASSERT(groupLocalPendingContacts.isEmpty());
 
750
    Q_ASSERT(groupRemotePendingContacts.isEmpty());
 
751
 
 
752
    // Set groupHaveMembers so we start queueing fresh MCD signals
 
753
    Q_ASSERT(!groupHaveMembers);
 
754
    groupHaveMembers = true;
 
755
 
 
756
    // Synthesize MCD for current + RP
 
757
    groupMembersChangedQueue.enqueue(new GroupMembersChangedInfo(
 
758
                groupInitialMembers, // Members
 
759
                UIntList(), // Removed - obviously, none
 
760
                UIntList(), // LP - will be handled separately, see below
 
761
                groupInitialRP, // Remote pending
 
762
                QVariantMap())); // No details for members + RP
 
763
 
 
764
    // Synthesize one MCD for each initial LP member - they might have different details
 
765
    foreach (const LocalPendingInfo &info, groupInitialLP) {
 
766
        QVariantMap details;
 
767
 
 
768
        if (info.actor != 0) {
 
769
            details.insert(QLatin1String("actor"), info.actor);
 
770
        }
 
771
 
 
772
        if (info.reason != ChannelGroupChangeReasonNone) {
 
773
            details.insert(QLatin1String("change-reason"), info.reason);
 
774
        }
 
775
 
 
776
        if (!info.message.isEmpty()) {
 
777
            details.insert(QLatin1String("message"), info.message);
 
778
        }
 
779
 
 
780
        groupMembersChangedQueue.enqueue(new GroupMembersChangedInfo(UIntList(), UIntList(),
 
781
                    UIntList() << info.toBeAdded, UIntList(), details));
 
782
    }
 
783
 
 
784
    // At least our added MCD event to process
 
785
    processMembersChanged();
 
786
}
 
787
 
 
788
bool Channel::Private::setGroupFlags(uint newGroupFlags)
 
789
{
 
790
    if (groupFlags == newGroupFlags) {
 
791
        return false;
 
792
    }
 
793
 
 
794
    groupFlags = newGroupFlags;
 
795
 
 
796
    // this shouldn't happen but let's make sure
 
797
    if (!parent->interfaces().contains(TP_QT_IFACE_CHANNEL_INTERFACE_GROUP)) {
 
798
        return false;
 
799
    }
 
800
 
 
801
    if ((groupFlags & ChannelGroupFlagMembersChangedDetailed) &&
 
802
        !usingMembersChangedDetailed) {
 
803
        usingMembersChangedDetailed = true;
 
804
        debug() << "Starting to exclusively listen to MembersChangedDetailed for" <<
 
805
            parent->objectPath();
 
806
        parent->disconnect(group,
 
807
                           SIGNAL(MembersChanged(QString,Tp::UIntList,
 
808
                                   Tp::UIntList,Tp::UIntList,
 
809
                                   Tp::UIntList,uint,uint)),
 
810
                           parent,
 
811
                           SLOT(onMembersChanged(QString,Tp::UIntList,
 
812
                                   Tp::UIntList,Tp::UIntList,
 
813
                                   Tp::UIntList,uint,uint)));
 
814
    } else if (!(groupFlags & ChannelGroupFlagMembersChangedDetailed) &&
 
815
               usingMembersChangedDetailed) {
 
816
        warning() << " Channel service did spec-incompliant removal of MCD from GroupFlags";
 
817
        usingMembersChangedDetailed = false;
 
818
        parent->connect(group,
 
819
                        SIGNAL(MembersChanged(QString,Tp::UIntList,
 
820
                                Tp::UIntList,Tp::UIntList,
 
821
                                Tp::UIntList,uint,uint)),
 
822
                        parent,
 
823
                        SLOT(onMembersChanged(QString,Tp::UIntList,
 
824
                                Tp::UIntList,Tp::UIntList,
 
825
                                Tp::UIntList,uint,uint)));
 
826
    }
 
827
 
 
828
    return true;
 
829
}
 
830
 
 
831
void Channel::Private::buildContacts()
 
832
{
 
833
    buildingContacts = true;
 
834
 
 
835
    ContactManagerPtr manager = connection->contactManager();
 
836
    UIntList toBuild = QSet<uint>(pendingGroupMembers +
 
837
            pendingGroupLocalPendingMembers +
 
838
            pendingGroupRemotePendingMembers).toList();
 
839
 
 
840
    if (currentGroupMembersChangedInfo &&
 
841
            currentGroupMembersChangedInfo->actor != 0) {
 
842
        toBuild.append(currentGroupMembersChangedInfo->actor);
 
843
    }
 
844
 
 
845
    if (!initiatorContact && initiatorHandle) {
 
846
        // No initiator contact, but Yes initiator handle - might do something about it with just
 
847
        // that information
 
848
        toBuild.append(initiatorHandle);
 
849
    }
 
850
 
 
851
    if (!targetContact && targetHandleType == HandleTypeContact && targetHandle != 0) {
 
852
        toBuild.append(targetHandle);
 
853
    }
 
854
 
 
855
    // always try to retrieve selfContact and check if it changed on
 
856
    // updateContacts or on gotContacts, in case we were not able to retrieve it
 
857
    if (groupSelfHandle) {
 
858
        toBuild.append(groupSelfHandle);
 
859
    }
 
860
 
 
861
    // group self handle changed to 0 <- strange but it may happen, and contacts
 
862
    // were being built at the time, so check now
 
863
    if (toBuild.isEmpty()) {
 
864
        if (!groupSelfHandle && groupSelfContact) {
 
865
            groupSelfContact.reset();
 
866
            if (parent->isReady(Channel::FeatureCore)) {
 
867
                emit parent->groupSelfContactChanged();
 
868
            }
 
869
        }
 
870
 
 
871
        buildingContacts = false;
 
872
        return;
 
873
    }
 
874
 
 
875
    PendingContacts *pendingContacts = manager->contactsForHandles(
 
876
            toBuild);
 
877
    parent->connect(pendingContacts,
 
878
            SIGNAL(finished(Tp::PendingOperation*)),
 
879
            SLOT(gotContacts(Tp::PendingOperation*)));
 
880
}
 
881
 
 
882
void Channel::Private::processMembersChanged()
 
883
{
 
884
    Q_ASSERT(!buildingContacts);
 
885
 
 
886
    if (groupMembersChangedQueue.isEmpty()) {
 
887
        if (pendingRetrieveGroupSelfContact) {
 
888
            pendingRetrieveGroupSelfContact = false;
 
889
            // nothing queued but selfContact changed
 
890
            buildContacts();
 
891
            return;
 
892
        }
 
893
 
 
894
        if (!parent->isReady(Channel::FeatureCore)) {
 
895
            if (introspectQueue.isEmpty()) {
 
896
                debug() << "Both the MCD and the introspect queue empty for the first time. Ready!";
 
897
 
 
898
                if (initiatorHandle && !initiatorContact) {
 
899
                    warning() << " Unable to create contact object for initiator with handle" <<
 
900
                        initiatorHandle;
 
901
                }
 
902
 
 
903
                if (targetHandleType == HandleTypeContact && targetHandle != 0 && !targetContact) {
 
904
                    warning() << " Unable to create contact object for target with handle" <<
 
905
                        targetHandle;
 
906
                }
 
907
 
 
908
                if (groupSelfHandle && !groupSelfContact) {
 
909
                    warning() << " Unable to create contact object for self handle" <<
 
910
                        groupSelfHandle;
 
911
                }
 
912
 
 
913
                continueIntrospection();
 
914
            } else {
 
915
                debug() << "Contact queue empty but introspect queue isn't. IS will set ready.";
 
916
            }
 
917
        }
 
918
 
 
919
        return;
 
920
    }
 
921
 
 
922
    Q_ASSERT(pendingGroupMembers.isEmpty());
 
923
    Q_ASSERT(pendingGroupLocalPendingMembers.isEmpty());
 
924
    Q_ASSERT(pendingGroupRemotePendingMembers.isEmpty());
 
925
 
 
926
    // always set this to false here, as buildContacts will always try to
 
927
    // retrieve the selfContact and updateContacts will check if the built
 
928
    // contact is the same as the current contact.
 
929
    pendingRetrieveGroupSelfContact = false;
 
930
 
 
931
    currentGroupMembersChangedInfo = groupMembersChangedQueue.dequeue();
 
932
 
 
933
    foreach (uint handle, currentGroupMembersChangedInfo->added) {
 
934
        if (!groupContacts.contains(handle)) {
 
935
            pendingGroupMembers.insert(handle);
 
936
        }
 
937
 
 
938
        // the member was added to current members, check if it was in the
 
939
        // local/pending lists and if true, schedule for removal from that list
 
940
        if (groupLocalPendingContacts.contains(handle)) {
 
941
            groupLocalPendingMembersToRemove.append(handle);
 
942
        } else if(groupRemotePendingContacts.contains(handle)) {
 
943
            groupRemotePendingMembersToRemove.append(handle);
 
944
        }
 
945
    }
 
946
 
 
947
    foreach (uint handle, currentGroupMembersChangedInfo->localPending) {
 
948
        if (!groupLocalPendingContacts.contains(handle)) {
 
949
            pendingGroupLocalPendingMembers.insert(handle);
 
950
        }
 
951
    }
 
952
 
 
953
    foreach (uint handle, currentGroupMembersChangedInfo->remotePending) {
 
954
        if (!groupRemotePendingContacts.contains(handle)) {
 
955
            pendingGroupRemotePendingMembers.insert(handle);
 
956
        }
 
957
    }
 
958
 
 
959
    foreach (uint handle, currentGroupMembersChangedInfo->removed) {
 
960
        groupMembersToRemove.append(handle);
 
961
    }
 
962
 
 
963
    // Always go through buildContacts - we might have a self/initiator/whatever handle to build
 
964
    buildContacts();
 
965
}
 
966
 
 
967
void Channel::Private::updateContacts(const QList<ContactPtr> &contacts)
 
968
{
 
969
    Contacts groupContactsAdded;
 
970
    Contacts groupLocalPendingContactsAdded;
 
971
    Contacts groupRemotePendingContactsAdded;
 
972
    ContactPtr actorContact;
 
973
    bool selfContactUpdated = false;
 
974
 
 
975
    debug() << "Entering Chan::Priv::updateContacts() with" << contacts.size() << "contacts";
 
976
 
 
977
    // FIXME: simplify. Some duplication of logic present.
 
978
    foreach (ContactPtr contact, contacts) {
 
979
        uint handle = contact->handle()[0];
 
980
        if (pendingGroupMembers.contains(handle)) {
 
981
            groupContactsAdded.insert(contact);
 
982
            groupContacts[handle] = contact;
 
983
        } else if (pendingGroupLocalPendingMembers.contains(handle)) {
 
984
            groupLocalPendingContactsAdded.insert(contact);
 
985
            groupLocalPendingContacts[handle] = contact;
 
986
            // FIXME: should set the details and actor here too
 
987
            groupLocalPendingContactsChangeInfo[handle] = GroupMemberChangeDetails();
 
988
        } else if (pendingGroupRemotePendingMembers.contains(handle)) {
 
989
            groupRemotePendingContactsAdded.insert(contact);
 
990
            groupRemotePendingContacts[handle] = contact;
 
991
        }
 
992
 
 
993
        if (groupSelfHandle == handle && groupSelfContact != contact) {
 
994
            groupSelfContact = contact;
 
995
            selfContactUpdated = true;
 
996
        }
 
997
 
 
998
        if (!initiatorContact && initiatorHandle == handle) {
 
999
            // No initiator contact stored, but there's a contact for the initiator handle
 
1000
            // We can use that!
 
1001
            initiatorContact = contact;
 
1002
        }
 
1003
 
 
1004
        if (!targetContact && targetHandleType == HandleTypeContact && targetHandle == handle) {
 
1005
            targetContact = contact;
 
1006
 
 
1007
            if (targetId.isEmpty()) {
 
1008
                // For some reason, TargetID was missing from the property map. We can initialize it
 
1009
                // here in that case.
 
1010
                targetId = targetContact->id();
 
1011
            }
 
1012
        }
 
1013
 
 
1014
        if (currentGroupMembersChangedInfo &&
 
1015
            currentGroupMembersChangedInfo->actor == contact->handle()[0]) {
 
1016
            actorContact = contact;
 
1017
        }
 
1018
    }
 
1019
 
 
1020
    if (!groupSelfHandle && groupSelfContact) {
 
1021
        groupSelfContact.reset();
 
1022
        selfContactUpdated = true;
 
1023
    }
 
1024
 
 
1025
    pendingGroupMembers.clear();
 
1026
    pendingGroupLocalPendingMembers.clear();
 
1027
    pendingGroupRemotePendingMembers.clear();
 
1028
 
 
1029
    // FIXME: This shouldn't be needed. Clearer would be to first scan for the actor being present
 
1030
    // in the contacts supplied.
 
1031
    foreach (ContactPtr contact, contacts) {
 
1032
        uint handle = contact->handle()[0];
 
1033
        if (groupLocalPendingContactsChangeInfo.contains(handle)) {
 
1034
            groupLocalPendingContactsChangeInfo[handle] =
 
1035
                GroupMemberChangeDetails(actorContact,
 
1036
                        currentGroupMembersChangedInfo ? currentGroupMembersChangedInfo->details : QVariantMap());
 
1037
        }
 
1038
    }
 
1039
 
 
1040
    Contacts groupContactsRemoved;
 
1041
    ContactPtr contactToRemove;
 
1042
    foreach (uint handle, groupMembersToRemove) {
 
1043
        if (groupContacts.contains(handle)) {
 
1044
            contactToRemove = groupContacts[handle];
 
1045
            groupContacts.remove(handle);
 
1046
        } else if (groupLocalPendingContacts.contains(handle)) {
 
1047
            contactToRemove = groupLocalPendingContacts[handle];
 
1048
            groupLocalPendingContacts.remove(handle);
 
1049
        } else if (groupRemotePendingContacts.contains(handle)) {
 
1050
            contactToRemove = groupRemotePendingContacts[handle];
 
1051
            groupRemotePendingContacts.remove(handle);
 
1052
        }
 
1053
 
 
1054
        if (groupLocalPendingContactsChangeInfo.contains(handle)) {
 
1055
            groupLocalPendingContactsChangeInfo.remove(handle);
 
1056
        }
 
1057
 
 
1058
        if (contactToRemove) {
 
1059
            groupContactsRemoved.insert(contactToRemove);
 
1060
        }
 
1061
    }
 
1062
    groupMembersToRemove.clear();
 
1063
 
 
1064
    // FIXME: drop the LPToRemove and RPToRemove sets - they're redundant
 
1065
    foreach (uint handle, groupLocalPendingMembersToRemove) {
 
1066
        groupLocalPendingContacts.remove(handle);
 
1067
    }
 
1068
    groupLocalPendingMembersToRemove.clear();
 
1069
 
 
1070
    foreach (uint handle, groupRemotePendingMembersToRemove) {
 
1071
        groupRemotePendingContacts.remove(handle);
 
1072
    }
 
1073
    groupRemotePendingMembersToRemove.clear();
 
1074
 
 
1075
    if (!groupContactsAdded.isEmpty() ||
 
1076
        !groupLocalPendingContactsAdded.isEmpty() ||
 
1077
        !groupRemotePendingContactsAdded.isEmpty() ||
 
1078
        !groupContactsRemoved.isEmpty()) {
 
1079
        GroupMemberChangeDetails details(
 
1080
                actorContact,
 
1081
                currentGroupMembersChangedInfo ? currentGroupMembersChangedInfo->details : QVariantMap());
 
1082
 
 
1083
        if (currentGroupMembersChangedInfo
 
1084
                && currentGroupMembersChangedInfo->removed.contains(groupSelfHandle)) {
 
1085
            // Update groupSelfContactRemoveInfo with the proper actor in case
 
1086
            // the actor was not available by the time onMembersChangedDetailed
 
1087
            // was called.
 
1088
            groupSelfContactRemoveInfo = details;
 
1089
        }
 
1090
 
 
1091
        if (parent->isReady(Channel::FeatureCore)) {
 
1092
            // Channel is ready, we can signal membership changes to the outside world without
 
1093
            // confusing anyone's fragile logic.
 
1094
            emit parent->groupMembersChanged(
 
1095
                    groupContactsAdded,
 
1096
                    groupLocalPendingContactsAdded,
 
1097
                    groupRemotePendingContactsAdded,
 
1098
                    groupContactsRemoved,
 
1099
                    details);
 
1100
        }
 
1101
    }
 
1102
    delete currentGroupMembersChangedInfo;
 
1103
    currentGroupMembersChangedInfo = 0;
 
1104
 
 
1105
    if (selfContactUpdated && parent->isReady(Channel::FeatureCore)) {
 
1106
        emit parent->groupSelfContactChanged();
 
1107
    }
 
1108
 
 
1109
    processMembersChanged();
 
1110
}
 
1111
 
 
1112
bool Channel::Private::fakeGroupInterfaceIfNeeded()
 
1113
{
 
1114
    if (parent->interfaces().contains(TP_QT_IFACE_CHANNEL_INTERFACE_GROUP)) {
 
1115
        return false;
 
1116
    } else if (targetHandleType != HandleTypeContact) {
 
1117
        return false;
 
1118
    }
 
1119
 
 
1120
    // fake group interface
 
1121
    if (connection->selfHandle() && targetHandle) {
 
1122
        // Fake groupSelfHandle and initial members, let the MCD handling take care of the rest
 
1123
        // TODO connect to Connection::selfHandleChanged
 
1124
        groupSelfHandle = connection->selfHandle();
 
1125
        groupInitialMembers = UIntList() << groupSelfHandle << targetHandle;
 
1126
 
 
1127
        debug().nospace() << "Faking a group on channel with self handle=" <<
 
1128
            groupSelfHandle << " and other handle=" << targetHandle;
 
1129
 
 
1130
        nowHaveInitialMembers();
 
1131
    } else {
 
1132
        warning() << "Connection::selfHandle is 0 or targetHandle is 0, "
 
1133
            "not faking a group on channel";
 
1134
    }
 
1135
 
 
1136
    return true;
 
1137
}
 
1138
 
 
1139
void Channel::Private::setReady()
 
1140
{
 
1141
    Q_ASSERT(!parent->isReady(Channel::FeatureCore));
 
1142
 
 
1143
    debug() << "Channel fully ready";
 
1144
    debug() << " Channel type" << channelType;
 
1145
    debug() << " Target handle" << targetHandle;
 
1146
    debug() << " Target handle type" << targetHandleType;
 
1147
 
 
1148
    if (parent->interfaces().contains(TP_QT_IFACE_CHANNEL_INTERFACE_GROUP)) {
 
1149
        debug() << " Group: flags" << groupFlags;
 
1150
        if (groupAreHandleOwnersAvailable) {
 
1151
            debug() << " Group: Number of handle owner mappings" <<
 
1152
                groupHandleOwners.size();
 
1153
        }
 
1154
        else {
 
1155
            debug() << " Group: No handle owners property present";
 
1156
        }
 
1157
        debug() << " Group: Number of current members" <<
 
1158
            groupContacts.size();
 
1159
        debug() << " Group: Number of local pending members" <<
 
1160
            groupLocalPendingContacts.size();
 
1161
        debug() << " Group: Number of remote pending members" <<
 
1162
            groupRemotePendingContacts.size();
 
1163
        debug() << " Group: Self handle" << groupSelfHandle <<
 
1164
            "tracked:" << (groupIsSelfHandleTracked ? "yes" : "no");
 
1165
    }
 
1166
 
 
1167
    readinessHelper->setIntrospectCompleted(FeatureCore, true);
 
1168
}
 
1169
 
 
1170
QString Channel::Private::groupMemberChangeDetailsTelepathyError(
 
1171
        const GroupMemberChangeDetails &details)
 
1172
{
 
1173
    QString error;
 
1174
    uint reason = details.reason();
 
1175
    switch (reason) {
 
1176
        case ChannelGroupChangeReasonOffline:
 
1177
            error = TP_QT_ERROR_OFFLINE;
 
1178
            break;
 
1179
        case ChannelGroupChangeReasonKicked:
 
1180
            error = TP_QT_ERROR_CHANNEL_KICKED;
 
1181
            break;
 
1182
        case ChannelGroupChangeReasonBanned:
 
1183
            error = TP_QT_ERROR_CHANNEL_BANNED;
 
1184
            break;
 
1185
        case ChannelGroupChangeReasonBusy:
 
1186
            error = TP_QT_ERROR_BUSY;
 
1187
            break;
 
1188
        case ChannelGroupChangeReasonNoAnswer:
 
1189
            error = TP_QT_ERROR_NO_ANSWER;
 
1190
            break;
 
1191
        case ChannelGroupChangeReasonPermissionDenied:
 
1192
            error = TP_QT_ERROR_PERMISSION_DENIED;
 
1193
            break;
 
1194
        case ChannelGroupChangeReasonInvalidContact:
 
1195
            error = TP_QT_ERROR_DOES_NOT_EXIST;
 
1196
            break;
 
1197
        // The following change reason are being mapped to default
 
1198
        // case ChannelGroupChangeReasonNone:
 
1199
        // case ChannelGroupChangeReasonInvited:   // shouldn't happen
 
1200
        // case ChannelGroupChangeReasonError:
 
1201
        // case ChannelGroupChangeReasonRenamed:
 
1202
        // case ChannelGroupChangeReasonSeparated: // shouldn't happen
 
1203
        default:
 
1204
            // let's use the actor handle and selfHandle here instead of the
 
1205
            // contacts, as the contacts may not be ready.
 
1206
            error = ((qdbus_cast<uint>(details.allDetails().value(QLatin1String("actor"))) == groupSelfHandle) ?
 
1207
                     TP_QT_ERROR_CANCELLED :
 
1208
                     TP_QT_ERROR_TERMINATED);
 
1209
            break;
 
1210
    }
 
1211
 
 
1212
    return error;
 
1213
}
 
1214
 
 
1215
void Channel::Private::processConferenceChannelRemoved()
 
1216
{
 
1217
    if (buildingConferenceChannelRemovedActorContact ||
 
1218
        conferenceChannelRemovedQueue.isEmpty()) {
 
1219
        return;
 
1220
    }
 
1221
 
 
1222
    ConferenceChannelRemovedInfo *info = conferenceChannelRemovedQueue.first();
 
1223
    if (!conferenceChannels.contains(info->channelPath.path())) {
 
1224
        info = conferenceChannelRemovedQueue.dequeue();
 
1225
        delete info;
 
1226
        processConferenceChannelRemoved();
 
1227
        return;
 
1228
    }
 
1229
 
 
1230
    buildingConferenceChannelRemovedActorContact = true;
 
1231
 
 
1232
    if (info->details.contains(keyActor)) {
 
1233
        ContactManagerPtr manager = connection->contactManager();
 
1234
        PendingContacts *pendingContacts = manager->contactsForHandles(
 
1235
                UIntList() << qdbus_cast<uint>(info->details.value(keyActor)));
 
1236
        parent->connect(pendingContacts,
 
1237
                SIGNAL(finished(Tp::PendingOperation*)),
 
1238
                SLOT(gotConferenceChannelRemovedActorContact(Tp::PendingOperation*)));
 
1239
    } else {
 
1240
        parent->gotConferenceChannelRemovedActorContact(0);
 
1241
    }
 
1242
}
 
1243
 
 
1244
struct TP_QT_NO_EXPORT Channel::GroupMemberChangeDetails::Private : public QSharedData
 
1245
{
 
1246
    Private(const ContactPtr &actor, const QVariantMap &details)
 
1247
        : actor(actor), details(details) {}
 
1248
 
 
1249
    ContactPtr actor;
 
1250
    QVariantMap details;
 
1251
};
 
1252
 
 
1253
/**
 
1254
 * \class Channel::GroupMemberChangeDetails
 
1255
 * \ingroup clientchannel
 
1256
 * \headerfile TelepathyQt/channel.h <TelepathyQt/Channel>
 
1257
 *
 
1258
 * \brief The Channel::GroupMemberChangeDetails class represents the details of a group membership
 
1259
 *        change.
 
1260
 *
 
1261
 * Extended information is not always available; this will be reflected by
 
1262
 * the return value of isValid().
 
1263
 */
 
1264
 
 
1265
/**
 
1266
 * Constructs a new invalid GroupMemberChangeDetails instance.
 
1267
 */
 
1268
Channel::GroupMemberChangeDetails::GroupMemberChangeDetails()
 
1269
{
 
1270
}
 
1271
 
 
1272
/**
 
1273
 * Copy constructor.
 
1274
 */
 
1275
Channel::GroupMemberChangeDetails::GroupMemberChangeDetails(const GroupMemberChangeDetails &other)
 
1276
    : mPriv(other.mPriv)
 
1277
{
 
1278
}
 
1279
 
 
1280
/**
 
1281
 * Class destructor.
 
1282
 */
 
1283
Channel::GroupMemberChangeDetails::~GroupMemberChangeDetails()
 
1284
{
 
1285
}
 
1286
 
 
1287
/**
 
1288
 * Assigns all information (validity, details) from other to this.
 
1289
 */
 
1290
Channel::GroupMemberChangeDetails &Channel::GroupMemberChangeDetails::operator=(
 
1291
        const GroupMemberChangeDetails &other)
 
1292
{
 
1293
    this->mPriv = other.mPriv;
 
1294
    return *this;
 
1295
}
 
1296
 
 
1297
/**
 
1298
 * \fn bool Channel::GroupMemberChangeDetails::isValid() const
 
1299
 *
 
1300
 * Return whether the details are valid (have actually been received from the service).
 
1301
 *
 
1302
 * \return \c true if valid, \c false otherwise.
 
1303
 */
 
1304
 
 
1305
/**
 
1306
 * Return whether the details specify an actor.
 
1307
 *
 
1308
 * If present, actor() will return the contact object representing the person who made the change.
 
1309
 *
 
1310
 * \return \c true if the actor is known, \c false otherwise.
 
1311
 * \sa actor()
 
1312
 */
 
1313
bool Channel::GroupMemberChangeDetails::hasActor() const
 
1314
{
 
1315
    return isValid() && !mPriv->actor.isNull();
 
1316
}
 
1317
 
 
1318
/**
 
1319
 * Return the contact object representing the person who made the change (actor), if known.
 
1320
 *
 
1321
 * \return A pointer to the Contact object, or a null ContactPtr if the actor is unknown.
 
1322
 * \sa hasActor()
 
1323
 */
 
1324
ContactPtr Channel::GroupMemberChangeDetails::actor() const
 
1325
{
 
1326
    return isValid() ? mPriv->actor : ContactPtr();
 
1327
}
 
1328
 
 
1329
/**
 
1330
 * \fn bool Channel::GroupMemberChangeDetails::hasReason() const
 
1331
 *
 
1332
 * Return whether the details specify the reason for the change.
 
1333
 *
 
1334
 * \return \c true if the reason is known, \c false otherwise.
 
1335
 * \sa reason()
 
1336
 */
 
1337
 
 
1338
/**
 
1339
 * \fn ChannelGroupChangeReason Channel::GroupMemberChangeDetails::reason() const
 
1340
 *
 
1341
 * Return the reason for the change, if known.
 
1342
 *
 
1343
 * \return The change reason as #ChannelGroupChangeReason, or #ChannelGroupChangeReasonNone
 
1344
 *         if the reason is unknown.
 
1345
 * \sa hasReason()
 
1346
 */
 
1347
 
 
1348
/**
 
1349
 * \fn bool Channel::GroupMemberChangeDetails::hasMessage() const
 
1350
 *
 
1351
 * Return whether the details specify a human-readable message from the contact represented by
 
1352
 * actor() pertaining to the change.
 
1353
 *
 
1354
 * \return \c true if the message is known, \c false otherwise.
 
1355
 * \sa message()
 
1356
 */
 
1357
 
 
1358
/**
 
1359
 * \fn QString Channel::GroupMemberChangeDetails::message() const
 
1360
 *
 
1361
 * Return a human-readable message from the contact represented by actor() pertaining to the change,
 
1362
 * if known.
 
1363
 *
 
1364
 * \return The message, or an empty string if the message is unknown.
 
1365
 * \sa hasMessage()
 
1366
 */
 
1367
 
 
1368
/**
 
1369
 * \fn bool Channel::GroupMemberChangeDetails::hasError() const
 
1370
 *
 
1371
 * Return whether the details specify a D-Bus error describing the change.
 
1372
 *
 
1373
 * \return \c true if the error is known, \c false otherwise.
 
1374
 * \sa error()
 
1375
 */
 
1376
 
 
1377
/**
 
1378
 * \fn QString Channel::GroupMemberChangeDetails::error() const
 
1379
 *
 
1380
 * Return the D-Bus error describing the change, if known.
 
1381
 *
 
1382
 * The D-Bus error provides more specific information than the reason() and should be used if
 
1383
 * applicable.
 
1384
 *
 
1385
 * \return A D-Bus error describing the change, or an empty string if the error is unknown.
 
1386
 * \sa hasError()
 
1387
 */
 
1388
 
 
1389
/**
 
1390
 * \fn bool Channel::GroupMemberChangeDetails::hasDebugMessage() const
 
1391
 *
 
1392
 * Return whether the details specify a debug message.
 
1393
 *
 
1394
 * \return \c true if debug message is present, \c false otherwise.
 
1395
 * \sa debugMessage()
 
1396
 */
 
1397
 
 
1398
/**
 
1399
 * \fn QString Channel::GroupMemberChangeDetails::debugMessage() const
 
1400
 *
 
1401
 * Return the debug message specified by the details, if any.
 
1402
 *
 
1403
 * The debug message is purely informational, offered for display for bug reporting purposes, and
 
1404
 * should not be attempted to be parsed.
 
1405
 *
 
1406
 * \return The debug message, or an empty string if there is none.
 
1407
 * \sa hasDebugMessage()
 
1408
 */
 
1409
 
 
1410
/**
 
1411
 * Return a map containing all details of the group members change.
 
1412
 *
 
1413
 * This is useful for accessing domain-specific additional details.
 
1414
 *
 
1415
 * \return The details of the group members change as QVariantMap.
 
1416
 */
 
1417
QVariantMap Channel::GroupMemberChangeDetails::allDetails() const
 
1418
{
 
1419
    return isValid() ? mPriv->details : QVariantMap();
 
1420
}
 
1421
 
 
1422
Channel::GroupMemberChangeDetails::GroupMemberChangeDetails(const ContactPtr &actor,
 
1423
        const QVariantMap &details)
 
1424
    : mPriv(new Private(actor, details))
 
1425
{
 
1426
}
 
1427
 
 
1428
/**
 
1429
 * \class Channel
 
1430
 * \ingroup clientchannel
 
1431
 * \headerfile TelepathyQt/channel.h <TelepathyQt/Channel>
 
1432
 *
 
1433
 * \brief The Channel class represents a Telepathy channel.
 
1434
 *
 
1435
 * All communication in the Telepathy framework is carried out via channel
 
1436
 * objects. Specialized classes for some specific channel types such as
 
1437
 * StreamedMediaChannel, TextChannel, FileTransferChannel are provided.
 
1438
 *
 
1439
 * The remote object accessor functions on this object (channelType(), targetHandleType(),
 
1440
 * and so on) don't make any D-Bus calls; instead, they return/use
 
1441
 * values cached from a previous introspection run. The introspection process
 
1442
 * populates their values in the most efficient way possible based on what the
 
1443
 * service implements.
 
1444
 *
 
1445
 * To avoid unnecessary D-Bus traffic, some accessors only return valid
 
1446
 * information after specific features have been enabled.
 
1447
 * For instance, to retrieve the initial invitee contacts in a conference channel,
 
1448
 * it is necessary to enable the feature Channel::FeatureConferenceInitialInviteeContacts.
 
1449
 * See the individual methods descriptions for more details.
 
1450
 *
 
1451
 * Channel features can be enabled by constructing a ChannelFactory and enabling
 
1452
 * the desired features, and passing it to AccountManager, Account or ClientRegistrar
 
1453
 * when creating them as appropriate. However, if a particular
 
1454
 * feature is only ever used in a specific circumstance, such as an user opening
 
1455
 * some settings dialog separate from the general view of the application,
 
1456
 * features can be later enabled as needed by calling becomeReady() with the additional
 
1457
 * features, and waiting for the resulting PendingOperation to finish.
 
1458
 *
 
1459
 * Each channel is owned by a connection. If the Connection object becomes invalidated
 
1460
 * the Channel object will also get invalidated.
 
1461
 *
 
1462
 * \section chan_usage_sec Usage
 
1463
 *
 
1464
 * \subsection chan_create_sec Creating a channel object
 
1465
 *
 
1466
 * Channel objects can be created in various ways, but the preferred way is
 
1467
 * trough Account channel creation methods such as Account::ensureTextChat(),
 
1468
 * Account::createFileTransfer(), which uses the channel dispatcher.
 
1469
 *
 
1470
 * If you already know the object path, you can just call create().
 
1471
 * For example:
 
1472
 *
 
1473
 * \code
 
1474
 *
 
1475
 * ChannelPtr chan = Channel::create(connection, objectPath,
 
1476
 *         immutableProperties);
 
1477
 *
 
1478
 * \endcode
 
1479
 *
 
1480
 * \subsection chan_ready_sec Making channel ready to use
 
1481
 *
 
1482
 * A Channel object needs to become ready before usage, meaning that the
 
1483
 * introspection process finished and the object accessors can be used.
 
1484
 *
 
1485
 * To make the object ready, use becomeReady() and wait for the
 
1486
 * PendingOperation::finished() signal to be emitted.
 
1487
 *
 
1488
 * \code
 
1489
 *
 
1490
 * class MyClass : public QObject
 
1491
 * {
 
1492
 *     QOBJECT
 
1493
 *
 
1494
 * public:
 
1495
 *     MyClass(QObject *parent = 0);
 
1496
 *     ~MyClass() { }
 
1497
 *
 
1498
 * private Q_SLOTS:
 
1499
 *     void onChannelReady(Tp::PendingOperation*);
 
1500
 *
 
1501
 * private:
 
1502
 *     ChannelPtr chan;
 
1503
 * };
 
1504
 *
 
1505
 * MyClass::MyClass(const ConnectionPtr &connection,
 
1506
 *         const QString &objectPath, const QVariantMap &immutableProperties)
 
1507
 *     : QObject(parent)
 
1508
 *       chan(Channel::create(connection, objectPath, immutableProperties))
 
1509
 * {
 
1510
 *     connect(chan->becomeReady(),
 
1511
 *             SIGNAL(finished(Tp::PendingOperation*)),
 
1512
 *             SLOT(onChannelReady(Tp::PendingOperation*)));
 
1513
 * }
 
1514
 *
 
1515
 * void MyClass::onChannelReady(Tp::PendingOperation *op)
 
1516
 * {
 
1517
 *     if (op->isError()) {
 
1518
 *         qWarning() << "Channel cannot become ready:" <<
 
1519
 *             op->errorName() << "-" << op->errorMessage();
 
1520
 *         return;
 
1521
 *     }
 
1522
 *
 
1523
 *     // Channel is now ready
 
1524
 * }
 
1525
 *
 
1526
 * \endcode
 
1527
 *
 
1528
 * See \ref async_model, \ref shared_ptr
 
1529
 */
 
1530
 
 
1531
/**
 
1532
 * Feature representing the core that needs to become ready to make the Channel
 
1533
 * object usable.
 
1534
 *
 
1535
 * Note that this feature must be enabled in order to use most Channel methods.
 
1536
 * See specific methods documentation for more details.
 
1537
 *
 
1538
 * When calling isReady(), becomeReady(), this feature is implicitly added
 
1539
 * to the requested features.
 
1540
 */
 
1541
const Feature Channel::FeatureCore = Feature(QLatin1String(Channel::staticMetaObject.className()), 0, true);
 
1542
 
 
1543
/**
 
1544
 * Feature used in order to access the conference initial invitee contacts info.
 
1545
 *
 
1546
 * \sa conferenceInitialInviteeContacts()
 
1547
 */
 
1548
const Feature Channel::FeatureConferenceInitialInviteeContacts = Feature(QLatin1String(Channel::staticMetaObject.className()), 1, true);
 
1549
 
 
1550
/**
 
1551
 * Create a new Channel object.
 
1552
 *
 
1553
 * \param connection Connection owning this channel, and specifying the
 
1554
 *                   service.
 
1555
 * \param objectPath The channel object path.
 
1556
 * \param immutableProperties The channel immutable properties.
 
1557
 * \return A ChannelPtr object pointing to the newly created Channel object.
 
1558
 *
 
1559
 * \todo \a immutableProperties should be used to populate the corresponding accessors (such as
 
1560
 * channelType()) already on construction, not only when making FeatureCore ready (fd.o #41654)
 
1561
 */
 
1562
ChannelPtr Channel::create(const ConnectionPtr &connection,
 
1563
        const QString &objectPath, const QVariantMap &immutableProperties)
 
1564
{
 
1565
    return ChannelPtr(new Channel(connection, objectPath, immutableProperties,
 
1566
                Channel::FeatureCore));
 
1567
}
 
1568
 
 
1569
/**
 
1570
 * Construct a new Channel object.
 
1571
 *
 
1572
 * \param connection Connection owning this channel, and specifying the
 
1573
 *                   service.
 
1574
 * \param objectPath The channel object path.
 
1575
 * \param immutableProperties The channel immutable properties.
 
1576
 * \param coreFeature The core feature of the channel type. The corresponding introspectable should
 
1577
 *                    depend on Channel::FeatureCore.
 
1578
 */
 
1579
Channel::Channel(const ConnectionPtr &connection,
 
1580
                 const QString &objectPath,
 
1581
                 const QVariantMap &immutableProperties,
 
1582
                 const Feature &coreFeature)
 
1583
    : StatefulDBusProxy(connection->dbusConnection(), connection->busName(),
 
1584
            objectPath, coreFeature),
 
1585
      OptionalInterfaceFactory<Channel>(this),
 
1586
      mPriv(new Private(this, connection, immutableProperties))
 
1587
{
 
1588
}
 
1589
 
 
1590
/**
 
1591
 * Class destructor.
 
1592
 */
 
1593
Channel::~Channel()
 
1594
{
 
1595
    delete mPriv;
 
1596
}
 
1597
 
 
1598
/**
 
1599
 * Return the connection owning this channel.
 
1600
 *
 
1601
 * \return A pointer to the Connection object.
 
1602
 */
 
1603
ConnectionPtr Channel::connection() const
 
1604
{
 
1605
    return mPriv->connection;
 
1606
}
 
1607
 
 
1608
/**
 
1609
 * Return the immutable properties of the channel.
 
1610
 *
 
1611
 * If the channel is ready (isReady(Channel::FeatureCore) returns true), the following keys are
 
1612
 * guaranteed to be present:
 
1613
 * org.freedesktop.Telepathy.Channel.ChannelType,
 
1614
 * org.freedesktop.Telepathy.Channel.TargetHandleType,
 
1615
 * org.freedesktop.Telepathy.Channel.TargetHandle and
 
1616
 * org.freedesktop.Telepathy.Channel.Requested.
 
1617
 *
 
1618
 * The keys and values in this map are defined by the \telepathy_spec,
 
1619
 * or by third-party extensions to that specification.
 
1620
 * These are the properties that cannot change over the lifetime of the
 
1621
 * channel; they're announced in the result of the request, for efficiency.
 
1622
 *
 
1623
 * \return The immutable properties as QVariantMap.
 
1624
 */
 
1625
QVariantMap Channel::immutableProperties() const
 
1626
{
 
1627
    if (isReady(Channel::FeatureCore)) {
 
1628
        QString key;
 
1629
 
 
1630
        key = TP_QT_IFACE_CHANNEL + QLatin1String(".ChannelType");
 
1631
        if (!mPriv->immutableProperties.contains(key)) {
 
1632
            mPriv->immutableProperties.insert(key, mPriv->channelType);
 
1633
        }
 
1634
 
 
1635
        key = TP_QT_IFACE_CHANNEL + QLatin1String(".Interfaces");
 
1636
        if (!mPriv->immutableProperties.contains(key)) {
 
1637
            mPriv->immutableProperties.insert(key, interfaces());
 
1638
        }
 
1639
 
 
1640
        key = TP_QT_IFACE_CHANNEL + QLatin1String(".TargetHandleType");
 
1641
        if (!mPriv->immutableProperties.contains(key)) {
 
1642
            mPriv->immutableProperties.insert(key, mPriv->targetHandleType);
 
1643
        }
 
1644
 
 
1645
        key = TP_QT_IFACE_CHANNEL + QLatin1String(".TargetHandle");
 
1646
        if (!mPriv->immutableProperties.contains(key)) {
 
1647
            mPriv->immutableProperties.insert(key, mPriv->targetHandle);
 
1648
        }
 
1649
 
 
1650
        key = TP_QT_IFACE_CHANNEL + QLatin1String(".TargetID");
 
1651
        if (!mPriv->immutableProperties.contains(key)) {
 
1652
            mPriv->immutableProperties.insert(key, mPriv->targetId);
 
1653
        }
 
1654
 
 
1655
        key = TP_QT_IFACE_CHANNEL + QLatin1String(".Requested");
 
1656
        if (!mPriv->immutableProperties.contains(key)) {
 
1657
            mPriv->immutableProperties.insert(key, mPriv->requested);
 
1658
        }
 
1659
 
 
1660
        key = TP_QT_IFACE_CHANNEL + QLatin1String(".InitiatorHandle");
 
1661
        if (!mPriv->immutableProperties.contains(key)) {
 
1662
            mPriv->immutableProperties.insert(key, mPriv->initiatorHandle);
 
1663
        }
 
1664
 
 
1665
        key = TP_QT_IFACE_CHANNEL + QLatin1String(".InitiatorID");
 
1666
        if (!mPriv->immutableProperties.contains(key) && !mPriv->initiatorContact.isNull()) {
 
1667
            mPriv->immutableProperties.insert(key, mPriv->initiatorContact->id());
 
1668
        }
 
1669
    }
 
1670
 
 
1671
    return mPriv->immutableProperties;
 
1672
}
 
1673
 
 
1674
/**
 
1675
 * Return the D-Bus interface name for the type of this channel.
 
1676
 *
 
1677
 * This method requires Channel::FeatureCore to be ready.
 
1678
 *
 
1679
 * \return The D-Bus interface name for the type of the channel.
 
1680
 */
 
1681
QString Channel::channelType() const
 
1682
{
 
1683
    // Similarly, we don't want warnings triggered when using the type interface
 
1684
    // proxies internally.
 
1685
    if (!isReady(Channel::FeatureCore) && mPriv->channelType.isEmpty()) {
 
1686
        warning() << "Channel::channelType() before the channel type has "
 
1687
            "been received";
 
1688
    }
 
1689
    else if (!isValid()) {
 
1690
        warning() << "Channel::channelType() used with channel closed";
 
1691
    }
 
1692
 
 
1693
    return mPriv->channelType;
 
1694
}
 
1695
 
 
1696
/**
 
1697
 * Return the type of the handle returned by targetHandle() as specified in
 
1698
 * #HandleType.
 
1699
 *
 
1700
 * This method requires Channel::FeatureCore to be ready.
 
1701
 *
 
1702
 * \return The target handle type as #HandleType.
 
1703
 * \sa targetHandle(), targetId()
 
1704
 */
 
1705
HandleType Channel::targetHandleType() const
 
1706
{
 
1707
    if (!isReady(Channel::FeatureCore)) {
 
1708
        warning() << "Channel::targetHandleType() used channel not ready";
 
1709
    }
 
1710
 
 
1711
    return (HandleType) mPriv->targetHandleType;
 
1712
}
 
1713
 
 
1714
/**
 
1715
 * Return the handle of the remote party with which this channel
 
1716
 * communicates.
 
1717
 *
 
1718
 * This method requires Channel::FeatureCore to be ready.
 
1719
 *
 
1720
 * \return An integer representing the target handle, which is of the type
 
1721
 *         targetHandleType() indicates.
 
1722
 * \sa targetHandleType(), targetId()
 
1723
 */
 
1724
uint Channel::targetHandle() const
 
1725
{
 
1726
    if (!isReady(Channel::FeatureCore)) {
 
1727
        warning() << "Channel::targetHandle() used channel not ready";
 
1728
    }
 
1729
 
 
1730
    return mPriv->targetHandle;
 
1731
}
 
1732
 
 
1733
/**
 
1734
 * Return the persistent unique ID of the remote party with which this channel communicates.
 
1735
 *
 
1736
 * If targetHandleType() is #HandleTypeContact, this will be the ID of the remote contact, and
 
1737
 * similarly the unique ID of the room when targetHandleType() is #HandleTypeRoom.
 
1738
 *
 
1739
 * This is not necessarily the best identifier to display to the user, though. In particular, for
 
1740
 * contacts, their alias should be displayed instead. It can be used for matching channels and UI
 
1741
 * elements for them across reconnects, though, at which point the old channels and contacts are
 
1742
 * invalidated.
 
1743
 *
 
1744
 * This method requires Channel::FeatureCore to be ready.
 
1745
 *
 
1746
 * \return The target identifier.
 
1747
 * \sa targetHandle(), targetContact()
 
1748
 */
 
1749
QString Channel::targetId() const
 
1750
{
 
1751
    if (!isReady(Channel::FeatureCore)) {
 
1752
        warning() << "Channel::targetId() used, but the channel is not ready";
 
1753
    }
 
1754
 
 
1755
    return mPriv->targetId;
 
1756
}
 
1757
 
 
1758
/**
 
1759
 * Return the contact with which this channel communicates for its lifetime, if applicable.
 
1760
 *
 
1761
 * This method requires Channel::FeatureCore to be ready.
 
1762
 *
 
1763
 * \return A pointer to the Contact object, or a null ContactPtr if targetHandleType() is not
 
1764
 *         #HandleTypeContact.
 
1765
 * \sa targetHandle(), targetId()
 
1766
 */
 
1767
ContactPtr Channel::targetContact() const
 
1768
{
 
1769
    if (!isReady(Channel::FeatureCore)) {
 
1770
        warning() << "Channel::targetContact() used, but the channel is not ready";
 
1771
    } else if (targetHandleType() != HandleTypeContact) {
 
1772
        warning() << "Channel::targetContact() used with targetHandleType() != Contact";
 
1773
    }
 
1774
 
 
1775
    return mPriv->targetContact;
 
1776
}
 
1777
 
 
1778
/**
 
1779
 * Return whether this channel was created in response to a
 
1780
 * local request.
 
1781
 *
 
1782
 * This method requires Channel::FeatureCore to be ready.
 
1783
 *
 
1784
 * \return \c true if the channel was created in response to a local request,
 
1785
 *         \c false otherwise.
 
1786
 */
 
1787
bool Channel::isRequested() const
 
1788
{
 
1789
    if (!isReady(Channel::FeatureCore)) {
 
1790
        warning() << "Channel::isRequested() used channel not ready";
 
1791
    }
 
1792
 
 
1793
    return mPriv->requested;
 
1794
}
 
1795
 
 
1796
/**
 
1797
 * Return the contact who initiated this channel.
 
1798
 *
 
1799
 * This method requires Channel::FeatureCore to be ready.
 
1800
 *
 
1801
 * \return A pointer to the Contact object representing the contact who initiated the channel,
 
1802
 *         or a null ContactPtr if it can't be retrieved.
 
1803
 */
 
1804
ContactPtr Channel::initiatorContact() const
 
1805
{
 
1806
    if (!isReady(Channel::FeatureCore)) {
 
1807
        warning() << "Channel::initiatorContact() used channel not ready";
 
1808
    }
 
1809
 
 
1810
    return mPriv->initiatorContact;
 
1811
}
 
1812
 
 
1813
/**
 
1814
 * Start an asynchronous request that this channel be closed.
 
1815
 *
 
1816
 * The returned PendingOperation object will signal the success or failure
 
1817
 * of this request; under normal circumstances, it can be expected to
 
1818
 * succeed.
 
1819
 *
 
1820
 * \return A PendingOperation which will emit PendingOperation::finished
 
1821
 *         when the call has finished.
 
1822
 * \sa requestLeave()
 
1823
 */
 
1824
PendingOperation *Channel::requestClose()
 
1825
{
 
1826
    // Closing a channel does not make sense if it is already closed,
 
1827
    // just silently Return.
 
1828
    if (!isValid()) {
 
1829
        return new PendingSuccess(ChannelPtr(this));
 
1830
    }
 
1831
 
 
1832
    return new PendingVoid(mPriv->baseInterface->Close(), ChannelPtr(this));
 
1833
}
 
1834
 
 
1835
Channel::PendingLeave::PendingLeave(const ChannelPtr &chan, const QString &message,
 
1836
        ChannelGroupChangeReason reason)
 
1837
    : PendingOperation(chan)
 
1838
{
 
1839
    Q_ASSERT(chan->mPriv->group != NULL);
 
1840
 
 
1841
    QDBusPendingCall call =
 
1842
        chan->mPriv->group->RemoveMembersWithReason(
 
1843
                UIntList() << chan->mPriv->groupSelfHandle,
 
1844
                message,
 
1845
                reason);
 
1846
 
 
1847
    connect(chan.data(),
 
1848
            SIGNAL(invalidated(Tp::DBusProxy*,QString,QString)),
 
1849
            this,
 
1850
            SLOT(onChanInvalidated(Tp::DBusProxy*)));
 
1851
 
 
1852
    connect(new PendingVoid(call, chan),
 
1853
            SIGNAL(finished(Tp::PendingOperation*)),
 
1854
            this,
 
1855
            SLOT(onRemoveFinished(Tp::PendingOperation*)));
 
1856
}
 
1857
 
 
1858
void Channel::PendingLeave::onChanInvalidated(Tp::DBusProxy *proxy)
 
1859
{
 
1860
    if (isFinished()) {
 
1861
        return;
 
1862
    }
 
1863
 
 
1864
    debug() << "Finishing PendingLeave successfully as the channel was invalidated";
 
1865
 
 
1866
    setFinished();
 
1867
}
 
1868
 
 
1869
void Channel::PendingLeave::onRemoveFinished(Tp::PendingOperation *op)
 
1870
{
 
1871
    if (isFinished()) {
 
1872
        return;
 
1873
    }
 
1874
 
 
1875
    ChannelPtr chan = ChannelPtr::staticCast(object());
 
1876
 
 
1877
    if (op->isValid()) {
 
1878
        debug() << "We left the channel" << chan->objectPath();
 
1879
 
 
1880
        ContactPtr c = chan->groupSelfContact();
 
1881
 
 
1882
        if (chan->groupContacts().contains(c)
 
1883
                || chan->groupLocalPendingContacts().contains(c)
 
1884
                || chan->groupRemotePendingContacts().contains(c)) {
 
1885
            debug() << "Waiting for self remove to be picked up";
 
1886
            connect(chan.data(),
 
1887
                    SIGNAL(groupMembersChanged(Tp::Contacts,Tp::Contacts,Tp::Contacts,Tp::Contacts,
 
1888
                            Tp::Channel::GroupMemberChangeDetails)),
 
1889
                    this,
 
1890
                    SLOT(onMembersChanged(Tp::Contacts,Tp::Contacts,Tp::Contacts,Tp::Contacts)));
 
1891
        } else {
 
1892
            setFinished();
 
1893
        }
 
1894
 
 
1895
        return;
 
1896
    }
 
1897
 
 
1898
    debug() << "Leave RemoveMembersWithReason failed with " << op->errorName() << op->errorMessage()
 
1899
        << "- falling back to Close";
 
1900
 
 
1901
    // If the channel has been closed or otherwise invalidated already in this mainloop iteration,
 
1902
    // the requestClose() operation will early-succeed
 
1903
    connect(chan->requestClose(),
 
1904
            SIGNAL(finished(Tp::PendingOperation*)),
 
1905
            this,
 
1906
            SLOT(onCloseFinished(Tp::PendingOperation*)));
 
1907
}
 
1908
 
 
1909
void Channel::PendingLeave::onMembersChanged(const Tp::Contacts &, const Tp::Contacts &,
 
1910
        const Tp::Contacts &, const Tp::Contacts &removed)
 
1911
{
 
1912
    if (isFinished()) {
 
1913
        return;
 
1914
    }
 
1915
 
 
1916
    ChannelPtr chan = ChannelPtr::staticCast(object());
 
1917
    ContactPtr c = chan->groupSelfContact();
 
1918
 
 
1919
    if (removed.contains(c)) {
 
1920
        debug() << "Leave event picked up for" << chan->objectPath();
 
1921
        setFinished();
 
1922
    }
 
1923
}
 
1924
 
 
1925
void Channel::PendingLeave::onCloseFinished(Tp::PendingOperation *op)
 
1926
{
 
1927
    if (isFinished()) {
 
1928
        return;
 
1929
    }
 
1930
 
 
1931
    ChannelPtr chan = ChannelPtr::staticCast(object());
 
1932
 
 
1933
    if (op->isError()) {
 
1934
        warning() << "Closing the channel" << chan->objectPath()
 
1935
            << "as a fallback for leaving it failed with"
 
1936
            << op->errorName() << op->errorMessage() << "- so didn't leave";
 
1937
        setFinishedWithError(op->errorName(), op->errorMessage());
 
1938
    } else {
 
1939
        debug() << "We left (by closing) the channel" << chan->objectPath();
 
1940
        setFinished();
 
1941
    }
 
1942
}
 
1943
 
 
1944
/**
 
1945
 * Start an asynchronous request to leave this channel as gracefully as possible.
 
1946
 *
 
1947
 * If leaving any more gracefully is not possible, this will revert to the same as requestClose().
 
1948
 * In particular, this will be the case for channels with no group interface
 
1949
 * (#TP_QT_IFACE_CHANNEL_INTERFACE_GROUP not in the list returned by interfaces()).
 
1950
 *
 
1951
 * The returned PendingOperation object will signal the success or failure
 
1952
 * of this request; under normal circumstances, it can be expected to
 
1953
 * succeed.
 
1954
 *
 
1955
 * A message and a reason may be provided along with the request, which will be sent to the server
 
1956
 * if supported, which is indicated by #ChannelGroupFlagMessageDepart and/or
 
1957
 * #ChannelGroupFlagMessageReject.
 
1958
 *
 
1959
 * Attempting to leave again when we have already left, either by our request or forcibly, will be a
 
1960
 * no-op, with the returned PendingOperation immediately finishing successfully.
 
1961
 *
 
1962
 * \param message The message, which can be blank if desired.
 
1963
 * \param reason A reason for leaving.
 
1964
 * \return A PendingOperation which will emit PendingOperation::finished
 
1965
 *         when the call has finished.
 
1966
 */
 
1967
PendingOperation *Channel::requestLeave(const QString &message, ChannelGroupChangeReason reason)
 
1968
{
 
1969
    // Leaving a channel does not make sense if it is already closed,
 
1970
    // just silently Return.
 
1971
    if (!isValid()) {
 
1972
        return new PendingSuccess(ChannelPtr(this));
 
1973
    }
 
1974
 
 
1975
    if (!isReady(Channel::FeatureCore)) {
 
1976
        return new PendingFailure(TP_QT_ERROR_NOT_AVAILABLE,
 
1977
                QLatin1String("Channel::FeatureCore must be ready to leave a channel"),
 
1978
                ChannelPtr(this));
 
1979
    }
 
1980
 
 
1981
    if (!interfaces().contains(TP_QT_IFACE_CHANNEL_INTERFACE_GROUP)) {
 
1982
        return requestClose();
 
1983
    }
 
1984
 
 
1985
    ContactPtr self = groupSelfContact();
 
1986
 
 
1987
    if (!groupContacts().contains(self) && !groupLocalPendingContacts().contains(self)
 
1988
            && !groupRemotePendingContacts().contains(self)) {
 
1989
        debug() << "Channel::requestLeave() called for " << objectPath() <<
 
1990
            "which we aren't a member of";
 
1991
        return new PendingSuccess(ChannelPtr(this));
 
1992
    }
 
1993
 
 
1994
    return new PendingLeave(ChannelPtr(this), message, reason);
 
1995
}
 
1996
 
 
1997
/**
 
1998
 * \name Group interface
 
1999
 *
 
2000
 * Cached access to state of the group interface on the associated remote
 
2001
 * object, if the interface is present.
 
2002
 *
 
2003
 * Some methods can be used when targetHandleType() == #HandleTypeContact, such
 
2004
 * as groupFlags(), groupCanAddContacts(), groupCanRemoveContacts(),
 
2005
 * groupSelfContact() and groupContacts().
 
2006
 *
 
2007
 * As the group interface state can change freely during the lifetime of the
 
2008
 * channel due to events like new contacts joining the group, the cached state
 
2009
 * is automatically kept in sync with the remote object's state by hooking
 
2010
 * to the change notification signals present in the D-Bus interface.
 
2011
 *
 
2012
 * As the cached value changes, change notification signals are emitted.
 
2013
 *
 
2014
 * Signals such as groupMembersChanged(), groupSelfContactChanged(), etc., are emitted to
 
2015
 * indicate that properties have changed.
 
2016
 *
 
2017
 * Check the individual signals' descriptions for details.
 
2018
 */
 
2019
 
 
2020
//@{
 
2021
 
 
2022
/**
 
2023
 * Return a set of flags indicating the capabilities and behaviour of the
 
2024
 * group on this channel.
 
2025
 *
 
2026
 * Change notification is via the groupFlagsChanged() signal.
 
2027
 *
 
2028
 * This method requires Channel::FeatureCore to be ready.
 
2029
 *
 
2030
 * \return The bitfield combination of flags as #ChannelGroupFlags.
 
2031
 * \sa groupFlagsChanged()
 
2032
 */
 
2033
ChannelGroupFlags Channel::groupFlags() const
 
2034
{
 
2035
    if (!isReady(Channel::FeatureCore)) {
 
2036
        warning() << "Channel::groupFlags() used channel not ready";
 
2037
    }
 
2038
 
 
2039
    return (ChannelGroupFlags) mPriv->groupFlags;
 
2040
}
 
2041
 
 
2042
/**
 
2043
 * Return whether contacts can be added or invited to this channel.
 
2044
 *
 
2045
 * Change notification is via the groupCanAddContactsChanged() signal.
 
2046
 *
 
2047
 * This method requires Channel::FeatureCore to be ready.
 
2048
 *
 
2049
 * \return \c true if contacts can be added or invited to the channel,
 
2050
 *         \c false otherwise.
 
2051
 * \sa groupFlags(), groupAddContacts()
 
2052
 */
 
2053
bool Channel::groupCanAddContacts() const
 
2054
{
 
2055
    if (!isReady(Channel::FeatureCore)) {
 
2056
        warning() << "Channel::groupCanAddContacts() used channel not ready";
 
2057
    }
 
2058
 
 
2059
    return mPriv->groupFlags & ChannelGroupFlagCanAdd;
 
2060
}
 
2061
 
 
2062
/**
 
2063
 * Return whether a message is expected when adding/inviting contacts, who
 
2064
 * are not already members, to this channel.
 
2065
 *
 
2066
 * This method requires Channel::FeatureCore to be ready.
 
2067
 *
 
2068
 * \return \c true if a message is expected, \c false otherwise.
 
2069
 * \sa groupFlags(), groupAddContacts()
 
2070
 */
 
2071
bool Channel::groupCanAddContactsWithMessage() const
 
2072
{
 
2073
    if (!isReady(Channel::FeatureCore)) {
 
2074
        warning() << "Channel::groupCanAddContactsWithMessage() used when channel not ready";
 
2075
    }
 
2076
 
 
2077
    return mPriv->groupFlags & ChannelGroupFlagMessageAdd;
 
2078
}
 
2079
 
 
2080
/**
 
2081
 * Return whether a message is expected when accepting contacts' requests to
 
2082
 * join this channel.
 
2083
 *
 
2084
 * This method requires Channel::FeatureCore to be ready.
 
2085
 *
 
2086
 * \return \c true if a message is expected, \c false otherwise.
 
2087
 * \sa groupFlags(), groupAddContacts()
 
2088
 */
 
2089
bool Channel::groupCanAcceptContactsWithMessage() const
 
2090
{
 
2091
    if (!isReady(Channel::FeatureCore)) {
 
2092
        warning() << "Channel::groupCanAcceptContactsWithMessage() used when channel not ready";
 
2093
    }
 
2094
 
 
2095
    return mPriv->groupFlags & ChannelGroupFlagMessageAccept;
 
2096
}
 
2097
 
 
2098
/**
 
2099
 * Add contacts to this channel.
 
2100
 *
 
2101
 * Contacts on the local pending list (those waiting for permission to join
 
2102
 * the channel) can always be added. If groupCanAcceptContactsWithMessage()
 
2103
 * returns \c true, an optional message is expected when doing this; if not,
 
2104
 * the message parameter is likely to be ignored (so the user should not be
 
2105
 * asked for a message, and the message parameter should be left empty).
 
2106
 *
 
2107
 * Other contacts can only be added if groupCanAddContacts() returns \c true.
 
2108
 * If groupCanAddContactsWithMessage() returns \c true, an optional message is
 
2109
 * expected when doing this, and if not, the message parameter is likely to be
 
2110
 * ignored.
 
2111
 *
 
2112
 * This method requires Channel::FeatureCore to be ready.
 
2113
 *
 
2114
 * \param contacts Contacts to be added.
 
2115
 * \param message A string message, which can be blank if desired.
 
2116
 * \return A PendingOperation which will emit PendingOperation::finished
 
2117
 *         when the call has finished.
 
2118
 * \sa groupCanAddContacts(), groupCanAddContactsWithMessage(), groupCanAcceptContactsWithMessage()
 
2119
 */
 
2120
PendingOperation *Channel::groupAddContacts(const QList<ContactPtr> &contacts,
 
2121
        const QString &message)
 
2122
{
 
2123
    if (!isReady(Channel::FeatureCore)) {
 
2124
        warning() << "Channel::groupAddContacts() used channel not ready";
 
2125
        return new PendingFailure(TP_QT_ERROR_NOT_AVAILABLE,
 
2126
                QLatin1String("Channel not ready"),
 
2127
                ChannelPtr(this));
 
2128
    } else if (contacts.isEmpty()) {
 
2129
        warning() << "Channel::groupAddContacts() used with empty contacts param";
 
2130
        return new PendingFailure(TP_QT_ERROR_INVALID_ARGUMENT,
 
2131
                QLatin1String("contacts cannot be an empty list"),
 
2132
                ChannelPtr(this));
 
2133
    }
 
2134
 
 
2135
    foreach (const ContactPtr &contact, contacts) {
 
2136
        if (!contact) {
 
2137
            warning() << "Channel::groupAddContacts() used but contacts param contains "
 
2138
                "invalid contact";
 
2139
            return new PendingFailure(TP_QT_ERROR_INVALID_ARGUMENT,
 
2140
                    QLatin1String("Unable to add invalid contacts"),
 
2141
                    ChannelPtr(this));
 
2142
        }
 
2143
    }
 
2144
 
 
2145
    if (!interfaces().contains(TP_QT_IFACE_CHANNEL_INTERFACE_GROUP)) {
 
2146
        warning() << "Channel::groupAddContacts() used with no group interface";
 
2147
        return new PendingFailure(TP_QT_ERROR_NOT_IMPLEMENTED,
 
2148
                QLatin1String("Channel does not support group interface"),
 
2149
                ChannelPtr(this));
 
2150
    }
 
2151
 
 
2152
    UIntList handles;
 
2153
    foreach (const ContactPtr &contact, contacts) {
 
2154
        handles << contact->handle()[0];
 
2155
    }
 
2156
    return new PendingVoid(mPriv->group->AddMembers(handles, message), ChannelPtr(this));
 
2157
}
 
2158
 
 
2159
/**
 
2160
 * Return whether contacts in groupRemotePendingContacts() can be removed from
 
2161
 * this channel (i.e. whether an invitation can be rescinded).
 
2162
 *
 
2163
 * Change notification is via the groupCanRescindContactsChanged() signal.
 
2164
 *
 
2165
 * This method requires Channel::FeatureCore to be ready.
 
2166
 *
 
2167
 * \return \c true if contacts can be removed, \c false otherwise.
 
2168
 * \sa groupFlags(), groupRemoveContacts()
 
2169
 */
 
2170
bool Channel::groupCanRescindContacts() const
 
2171
{
 
2172
    if (!isReady(Channel::FeatureCore)) {
 
2173
        warning() << "Channel::groupCanRescindContacts() used channel not ready";
 
2174
    }
 
2175
 
 
2176
    return mPriv->groupFlags & ChannelGroupFlagCanRescind;
 
2177
}
 
2178
 
 
2179
/**
 
2180
 * Return whether a message is expected when removing contacts who are in
 
2181
 * groupRemotePendingContacts() from this channel (i.e. rescinding an
 
2182
 * invitation).
 
2183
 *
 
2184
 * This method requires Channel::FeatureCore to be ready.
 
2185
 *
 
2186
 * \return \c true if a message is expected, \c false otherwise.
 
2187
 * \sa groupFlags(), groupRemoveContacts()
 
2188
 */
 
2189
bool Channel::groupCanRescindContactsWithMessage() const
 
2190
{
 
2191
    if (!isReady(Channel::FeatureCore)) {
 
2192
        warning() << "Channel::groupCanRescindContactsWithMessage() used when channel not ready";
 
2193
    }
 
2194
 
 
2195
    return mPriv->groupFlags & ChannelGroupFlagMessageRescind;
 
2196
}
 
2197
 
 
2198
/**
 
2199
 * Return if contacts in groupContacts() can be removed from this channel.
 
2200
 *
 
2201
 * Note that contacts in local pending lists, and the groupSelfContact(), can
 
2202
 * always be removed from the channel.
 
2203
 *
 
2204
 * Change notification is via the groupCanRemoveContactsChanged() signal.
 
2205
 *
 
2206
 * This method requires Channel::FeatureCore to be ready.
 
2207
 *
 
2208
 * \return \c true if contacts can be removed, \c false otherwise.
 
2209
 * \sa groupFlags(), groupRemoveContacts()
 
2210
 */
 
2211
bool Channel::groupCanRemoveContacts() const
 
2212
{
 
2213
    if (!isReady(Channel::FeatureCore)) {
 
2214
        warning() << "Channel::groupCanRemoveContacts() used channel not ready";
 
2215
    }
 
2216
 
 
2217
    return mPriv->groupFlags & ChannelGroupFlagCanRemove;
 
2218
}
 
2219
 
 
2220
/**
 
2221
 * Return whether a message is expected when removing contacts who are in
 
2222
 * groupContacts() from this channel.
 
2223
 *
 
2224
 * This method requires Channel::FeatureCore to be ready.
 
2225
 *
 
2226
 * \return \c true if a message is expected, \c false otherwise.
 
2227
 * \sa groupFlags(), groupRemoveContacts()
 
2228
 */
 
2229
bool Channel::groupCanRemoveContactsWithMessage() const
 
2230
{
 
2231
    if (!isReady(Channel::FeatureCore)) {
 
2232
        warning() << "Channel::groupCanRemoveContactsWithMessage() used when channel not ready";
 
2233
    }
 
2234
 
 
2235
    return mPriv->groupFlags & ChannelGroupFlagMessageRemove;
 
2236
}
 
2237
 
 
2238
/**
 
2239
 * Return whether a message is expected when removing contacts who are in
 
2240
 * groupLocalPendingContacts() from this channel (i.e. rejecting a request to
 
2241
 * join).
 
2242
 *
 
2243
 * This method requires Channel::FeatureCore to be ready.
 
2244
 *
 
2245
 * \return \c true if a message is expected, \c false otherwise.
 
2246
 * \sa groupFlags(), groupRemoveContacts()
 
2247
 */
 
2248
bool Channel::groupCanRejectContactsWithMessage() const
 
2249
{
 
2250
    if (!isReady(Channel::FeatureCore)) {
 
2251
        warning() << "Channel::groupCanRejectContactsWithMessage() used when channel not ready";
 
2252
    }
 
2253
 
 
2254
    return mPriv->groupFlags & ChannelGroupFlagMessageReject;
 
2255
}
 
2256
 
 
2257
/**
 
2258
 * Return whether a message is expected when removing the groupSelfContact()
 
2259
 * from this channel (i.e. departing from the channel).
 
2260
 *
 
2261
 * \return \c true if a message is expected, \c false otherwise.
 
2262
 * \sa groupFlags(), groupRemoveContacts()
 
2263
 */
 
2264
bool Channel::groupCanDepartWithMessage() const
 
2265
{
 
2266
    if (!isReady(Channel::FeatureCore)) {
 
2267
        warning() << "Channel::groupCanDepartWithMessage() used when channel not ready";
 
2268
    }
 
2269
 
 
2270
    return mPriv->groupFlags & ChannelGroupFlagMessageDepart;
 
2271
}
 
2272
 
 
2273
/**
 
2274
 * Remove contacts from this channel.
 
2275
 *
 
2276
 * Contacts on the local pending list (those waiting for permission to join
 
2277
 * the channel) can always be removed. If groupCanRejectContactsWithMessage()
 
2278
 * returns \c true, an optional message is expected when doing this; if not,
 
2279
 * the message parameter is likely to be ignored (so the user should not be
 
2280
 * asked for a message, and the message parameter should be left empty).
 
2281
 *
 
2282
 * The groupSelfContact() can also always be removed, as a way to leave the
 
2283
 * group with an optional departure message and/or departure reason indication.
 
2284
 * If groupCanDepartWithMessage() returns \c true, an optional message is
 
2285
 * expected when doing this, and if not, the message parameter is likely to
 
2286
 * be ignored.
 
2287
 *
 
2288
 * Contacts in the group can only be removed (e.g. kicked) if
 
2289
 * groupCanRemoveContacts() returns \c true. If
 
2290
 * groupCanRemoveContactsWithMessage() returns \c true, an optional message is
 
2291
 * expected when doing this, and if not, the message parameter is likely to be
 
2292
 * ignored.
 
2293
 *
 
2294
 * Contacts in the remote pending list (those who have been invited to the
 
2295
 * channel) can only be removed (have their invitations rescinded) if
 
2296
 * groupCanRescindContacts() returns \c true. If
 
2297
 * groupCanRescindContactsWithMessage() returns \c true, an optional message is
 
2298
 * expected when doing this, and if not, the message parameter is likely to be
 
2299
 * ignored.
 
2300
 *
 
2301
 * This method requires Channel::FeatureCore to be ready.
 
2302
 *
 
2303
 * \param contacts Contacts to be removed.
 
2304
 * \param message A string message, which can be blank if desired.
 
2305
 * \param reason Reason of the change, as specified in
 
2306
 *               #ChannelGroupChangeReason
 
2307
 * \return A PendingOperation which will emit PendingOperation::finished
 
2308
 *         when the call has finished.
 
2309
 * \sa groupCanRemoveContacts(), groupCanRemoveContactsWithMessage(),
 
2310
 *     groupCanRejectContactsWithMessage(), groupCanRescindContacts(),
 
2311
 *     groupCanRescindContacts(), groupCanRescindContactsWithMessage(),
 
2312
 *     groupCanDepartWithMessage()
 
2313
 */
 
2314
PendingOperation *Channel::groupRemoveContacts(const QList<ContactPtr> &contacts,
 
2315
        const QString &message, ChannelGroupChangeReason reason)
 
2316
{
 
2317
    if (!isReady(Channel::FeatureCore)) {
 
2318
        warning() << "Channel::groupRemoveContacts() used channel not ready";
 
2319
        return new PendingFailure(TP_QT_ERROR_NOT_AVAILABLE,
 
2320
                QLatin1String("Channel not ready"),
 
2321
                ChannelPtr(this));
 
2322
    }
 
2323
 
 
2324
    if (contacts.isEmpty()) {
 
2325
        warning() << "Channel::groupRemoveContacts() used with empty contacts param";
 
2326
        return new PendingFailure(TP_QT_ERROR_INVALID_ARGUMENT,
 
2327
                QLatin1String("contacts param cannot be an empty list"),
 
2328
                ChannelPtr(this));
 
2329
    }
 
2330
 
 
2331
    foreach (const ContactPtr &contact, contacts) {
 
2332
        if (!contact) {
 
2333
            warning() << "Channel::groupRemoveContacts() used but contacts param contains "
 
2334
                "invalid contact:";
 
2335
            return new PendingFailure(TP_QT_ERROR_INVALID_ARGUMENT,
 
2336
                    QLatin1String("Unable to remove invalid contacts"),
 
2337
                    ChannelPtr(this));
 
2338
        }
 
2339
    }
 
2340
 
 
2341
    if (!interfaces().contains(TP_QT_IFACE_CHANNEL_INTERFACE_GROUP)) {
 
2342
        warning() << "Channel::groupRemoveContacts() used with no group interface";
 
2343
        return new PendingFailure(TP_QT_ERROR_NOT_IMPLEMENTED,
 
2344
                QLatin1String("Channel does not support group interface"),
 
2345
                ChannelPtr(this));
 
2346
    }
 
2347
 
 
2348
    UIntList handles;
 
2349
    foreach (const ContactPtr &contact, contacts) {
 
2350
        handles << contact->handle()[0];
 
2351
    }
 
2352
    return new PendingVoid(
 
2353
            mPriv->group->RemoveMembersWithReason(handles, message, reason),
 
2354
            ChannelPtr(this));
 
2355
}
 
2356
 
 
2357
/**
 
2358
 * Return the current contacts of the group.
 
2359
 *
 
2360
 * It is possible to omit the contact representing the local user, even if
 
2361
 * the contact is in the set, by passing \c false as the parameter \a
 
2362
 * includeSelfContact.
 
2363
 *
 
2364
 * Change notification is via the groupMembersChanged() signal.
 
2365
 *
 
2366
 * This method requires Channel::FeatureCore to be ready.
 
2367
 *
 
2368
 * \param includeSelfContact Whether to include the self contact in the returned set.
 
2369
 * \return A set of pointers to the Contact objects.
 
2370
 * \sa groupLocalPendingContacts(), groupRemotePendingContacts()
 
2371
 */
 
2372
Contacts Channel::groupContacts(bool includeSelfContact) const
 
2373
{
 
2374
    if (!isReady(Channel::FeatureCore)) {
 
2375
        warning() << "Channel::groupMembers() used channel not ready";
 
2376
    }
 
2377
 
 
2378
    Contacts ret = mPriv->groupContacts.values().toSet();
 
2379
    if (!includeSelfContact) {
 
2380
        ret.remove(groupSelfContact());
 
2381
    }
 
2382
    return ret;
 
2383
}
 
2384
 
 
2385
/**
 
2386
 * Return the contacts currently waiting for local approval to join the
 
2387
 * group.
 
2388
 *
 
2389
 * It is possible to omit the contact representing the local user, even if
 
2390
 * the contact is in the set, by passing \c false as the parameter \a
 
2391
 * includeSelfContact.
 
2392
 *
 
2393
 * Change notification is via the groupMembersChanged() signal.
 
2394
 *
 
2395
 * This method requires Channel::FeatureCore to be ready.
 
2396
 *
 
2397
 * \param includeSelfContact Whether to include the self contact in the returned set.
 
2398
 * \return A set of pointers to the Contact objects.
 
2399
 * \sa groupContacts(), groupRemotePendingContacts()
 
2400
 */
 
2401
Contacts Channel::groupLocalPendingContacts(bool includeSelfContact) const
 
2402
{
 
2403
    if (!isReady(Channel::FeatureCore)) {
 
2404
        warning() << "Channel::groupLocalPendingContacts() used channel not ready";
 
2405
    } else if (!interfaces().contains(TP_QT_IFACE_CHANNEL_INTERFACE_GROUP)) {
 
2406
        warning() << "Channel::groupLocalPendingContacts() used with no group interface";
 
2407
    }
 
2408
 
 
2409
    Contacts ret = mPriv->groupLocalPendingContacts.values().toSet();
 
2410
    if (!includeSelfContact) {
 
2411
        ret.remove(groupSelfContact());
 
2412
    }
 
2413
    return ret;
 
2414
}
 
2415
 
 
2416
/**
 
2417
 * Return the contacts currently waiting for remote approval to join the
 
2418
 * group.
 
2419
 *
 
2420
 * It is possible to omit the contact representing the local user, even if
 
2421
 * the contact is in the set, by passing \c false as the parameter \a
 
2422
 * includeSelfContact.
 
2423
 *
 
2424
 * Change notification is via the groupMembersChanged() signal.
 
2425
 *
 
2426
 * This method requires Channel::FeatureCore to be ready.
 
2427
 *
 
2428
 * \param includeSelfContact Whether to include the self contact in the returned set.
 
2429
 * \return A set of pointers to the Contact objects.
 
2430
 * \sa groupContacts(), groupLocalPendingContacts()
 
2431
 */
 
2432
Contacts Channel::groupRemotePendingContacts(bool includeSelfContact) const
 
2433
{
 
2434
    if (!isReady(Channel::FeatureCore)) {
 
2435
        warning() << "Channel::groupRemotePendingContacts() used channel not ready";
 
2436
    } else if (!interfaces().contains(TP_QT_IFACE_CHANNEL_INTERFACE_GROUP)) {
 
2437
        warning() << "Channel::groupRemotePendingContacts() used with no "
 
2438
            "group interface";
 
2439
    }
 
2440
 
 
2441
    Contacts ret = mPriv->groupRemotePendingContacts.values().toSet();
 
2442
    if (!includeSelfContact) {
 
2443
        ret.remove(groupSelfContact());
 
2444
    }
 
2445
    return ret;
 
2446
}
 
2447
 
 
2448
/**
 
2449
 * Return information of a local pending contact change. If
 
2450
 * no information is available, an object for which
 
2451
 * GroupMemberChangeDetails::isValid() returns <code>false</code> is returned.
 
2452
 *
 
2453
 * This method requires Channel::FeatureCore to be ready.
 
2454
 *
 
2455
 * \param contact A Contact object that is on the local pending contacts list.
 
2456
 * \return The change info as a GroupMemberChangeDetails object.
 
2457
 */
 
2458
Channel::GroupMemberChangeDetails Channel::groupLocalPendingContactChangeInfo(
 
2459
        const ContactPtr &contact) const
 
2460
{
 
2461
    if (!isReady(Channel::FeatureCore)) {
 
2462
        warning() << "Channel::groupLocalPendingContactChangeInfo() used channel not ready";
 
2463
    } else if (!interfaces().contains(TP_QT_IFACE_CHANNEL_INTERFACE_GROUP)) {
 
2464
        warning() << "Channel::groupLocalPendingContactChangeInfo() used with no group interface";
 
2465
    } else if (!contact) {
 
2466
        warning() << "Channel::groupLocalPendingContactChangeInfo() used with null contact param";
 
2467
        return GroupMemberChangeDetails();
 
2468
    }
 
2469
 
 
2470
    uint handle = contact->handle()[0];
 
2471
    return mPriv->groupLocalPendingContactsChangeInfo.value(handle);
 
2472
}
 
2473
 
 
2474
/**
 
2475
 * Return information on the removal of the local user from the group. If
 
2476
 * the user hasn't been removed from the group, an object for which
 
2477
 * GroupMemberChangeDetails::isValid() returns <code>false</code> is returned.
 
2478
 *
 
2479
 * This method should be called only after you've left the channel.
 
2480
 * This is useful for getting the remove information after missing the
 
2481
 * corresponding groupMembersChanged() signal, as the local user being
 
2482
 * removed usually causes the channel to be closed.
 
2483
 *
 
2484
 * The returned information is not guaranteed to be correct if
 
2485
 * groupIsSelfHandleTracked() returns false and a self handle change has
 
2486
 * occurred on the remote object.
 
2487
 *
 
2488
 * This method requires Channel::FeatureCore to be ready.
 
2489
 *
 
2490
 * \return The remove info as a GroupMemberChangeDetails object.
 
2491
 */
 
2492
Channel::GroupMemberChangeDetails Channel::groupSelfContactRemoveInfo() const
 
2493
{
 
2494
    // Oftentimes, the channel will be closed as a result from being left - so checking a channel's
 
2495
    // self remove info when it has been closed and hence invalidated is valid
 
2496
    if (isValid() && !isReady(Channel::FeatureCore)) {
 
2497
        warning() << "Channel::groupSelfContactRemoveInfo() used before Channel::FeatureCore is ready";
 
2498
    } else if (!interfaces().contains(TP_QT_IFACE_CHANNEL_INTERFACE_GROUP)) {
 
2499
        warning() << "Channel::groupSelfContactRemoveInfo() used with "
 
2500
            "no group interface";
 
2501
    }
 
2502
 
 
2503
    return mPriv->groupSelfContactRemoveInfo;
 
2504
}
 
2505
 
 
2506
/**
 
2507
 * Return whether globally valid handles can be looked up using the
 
2508
 * channel-specific handle on this channel using this object.
 
2509
 *
 
2510
 * Handle owner lookup is only available if:
 
2511
 * <ul>
 
2512
 *  <li>The object is ready
 
2513
 *  <li>The list returned by interfaces() contains #TP_QT_IFACE_CHANNEL_INTERFACE_GROUP</li>
 
2514
 *  <li>The set of flags returned by groupFlags() contains
 
2515
 *      #GroupFlagProperties and #GroupFlagChannelSpecificHandles</li>
 
2516
 * </ul>
 
2517
 *
 
2518
 * If this function returns \c false, the return value of
 
2519
 * groupHandleOwners() is undefined and groupHandleOwnersChanged() will
 
2520
 * never be emitted.
 
2521
 *
 
2522
 * The value returned by this function will stay fixed for the entire time
 
2523
 * the object is ready, so no change notification is provided.
 
2524
 *
 
2525
 * This method requires Channel::FeatureCore to be ready.
 
2526
 *
 
2527
 * \return \c true if handle owner lookup functionality is available, \c false otherwise.
 
2528
 */
 
2529
bool Channel::groupAreHandleOwnersAvailable() const
 
2530
{
 
2531
    if (!isReady(Channel::FeatureCore)) {
 
2532
        warning() << "Channel::groupAreHandleOwnersAvailable() used channel not ready";
 
2533
    } else if (!interfaces().contains(TP_QT_IFACE_CHANNEL_INTERFACE_GROUP)) {
 
2534
        warning() << "Channel::groupAreHandleOwnersAvailable() used with "
 
2535
            "no group interface";
 
2536
    }
 
2537
 
 
2538
    return mPriv->groupAreHandleOwnersAvailable;
 
2539
}
 
2540
 
 
2541
/**
 
2542
 * Return a mapping of handles specific to this channel to globally valid
 
2543
 * handles.
 
2544
 *
 
2545
 * The mapping includes at least all of the channel-specific handles in this
 
2546
 * channel's members, local-pending and remote-pending sets as keys. Any
 
2547
 * handle not in the keys of this mapping is not channel-specific in this
 
2548
 * channel. Handles which are channel-specific, but for which the owner is
 
2549
 * unknown, appear in this mapping with 0 as owner.
 
2550
 *
 
2551
 * Change notification is via the groupHandleOwnersChanged() signal.
 
2552
 *
 
2553
 * This method requires Channel::FeatureCore to be ready.
 
2554
 *
 
2555
 * \return A mapping from group-specific handles to globally valid handles.
 
2556
 */
 
2557
HandleOwnerMap Channel::groupHandleOwners() const
 
2558
{
 
2559
    if (!isReady(Channel::FeatureCore)) {
 
2560
        warning() << "Channel::groupHandleOwners() used channel not ready";
 
2561
    } else if (!interfaces().contains(TP_QT_IFACE_CHANNEL_INTERFACE_GROUP)) {
 
2562
        warning() << "Channel::groupAreHandleOwnersAvailable() used with no "
 
2563
            "group interface";
 
2564
    }
 
2565
    else if (!groupAreHandleOwnersAvailable()) {
 
2566
        warning() << "Channel::groupAreHandleOwnersAvailable() used, but handle "
 
2567
            "owners not available";
 
2568
    }
 
2569
 
 
2570
    return mPriv->groupHandleOwners;
 
2571
}
 
2572
 
 
2573
/**
 
2574
 * Return whether the value returned by groupSelfContact() is guaranteed to
 
2575
 * accurately represent the local user even after nickname changes, etc.
 
2576
 *
 
2577
 * This should always be \c true for new services implementing the group interface.
 
2578
 *
 
2579
 * Older services not providing group properties don't necessarily
 
2580
 * emit the SelfHandleChanged signal either, so self contact changes can't be
 
2581
 * reliably tracked.
 
2582
 *
 
2583
 * This method requires Channel::FeatureCore to be ready.
 
2584
 *
 
2585
 * \return \c true if changes to the self contact are tracked, \c false otherwise.
 
2586
 */
 
2587
bool Channel::groupIsSelfContactTracked() const
 
2588
{
 
2589
    if (!isReady(Channel::FeatureCore)) {
 
2590
        warning() << "Channel::groupIsSelfHandleTracked() used channel not ready";
 
2591
    } else if (!interfaces().contains(TP_QT_IFACE_CHANNEL_INTERFACE_GROUP)) {
 
2592
        warning() << "Channel::groupIsSelfHandleTracked() used with "
 
2593
            "no group interface";
 
2594
    }
 
2595
 
 
2596
    return mPriv->groupIsSelfHandleTracked;
 
2597
}
 
2598
 
 
2599
/**
 
2600
 * Return a Contact object representing the user in the group if at all possible, otherwise a
 
2601
 * Contact object representing the user globally.
 
2602
 *
 
2603
 * Change notification is via the groupSelfContactChanged() signal.
 
2604
 *
 
2605
 * This method requires Channel::FeatureCore to be ready.
 
2606
 *
 
2607
 * \return A pointer to the Contact object.
 
2608
 */
 
2609
ContactPtr Channel::groupSelfContact() const
 
2610
{
 
2611
    if (!isReady(Channel::FeatureCore)) {
 
2612
        warning() << "Channel::groupSelfContact() used channel not ready";
 
2613
    }
 
2614
 
 
2615
    return mPriv->groupSelfContact;
 
2616
}
 
2617
 
 
2618
/**
 
2619
 * Return whether the local user is in the "local pending" state. This
 
2620
 * indicates that the local user needs to take action to accept an invitation,
 
2621
 * an incoming call, etc.
 
2622
 *
 
2623
 * This method requires Channel::FeatureCore to be ready.
 
2624
 *
 
2625
 * \return \c true if local user is in the channel's local-pending set, \c false otherwise.
 
2626
 */
 
2627
bool Channel::groupSelfHandleIsLocalPending() const
 
2628
{
 
2629
    if (!isReady(Channel::FeatureCore)) {
 
2630
        warning() << "Channel::groupSelfHandleIsLocalPending() used when "
 
2631
            "channel not ready";
 
2632
        return false;
 
2633
    }
 
2634
 
 
2635
    return mPriv->groupLocalPendingContacts.contains(mPriv->groupSelfHandle);
 
2636
}
 
2637
 
 
2638
/**
 
2639
 * Attempt to add the local user to this channel. In some channel types,
 
2640
 * such as Text and StreamedMedia, this is used to accept an invitation or an
 
2641
 * incoming call.
 
2642
 *
 
2643
 * This method requires Channel::FeatureCore to be ready.
 
2644
 *
 
2645
 * \return A PendingOperation which will emit PendingOperation::finished
 
2646
 *         when the call has finished.
 
2647
 */
 
2648
PendingOperation *Channel::groupAddSelfHandle()
 
2649
{
 
2650
    if (!isReady(Channel::FeatureCore)) {
 
2651
        warning() << "Channel::groupAddSelfHandle() used when channel not "
 
2652
            "ready";
 
2653
        return new PendingFailure(TP_QT_ERROR_INVALID_ARGUMENT,
 
2654
                QLatin1String("Channel object not ready"),
 
2655
                ChannelPtr(this));
 
2656
    }
 
2657
 
 
2658
    UIntList handles;
 
2659
 
 
2660
    if (mPriv->groupSelfHandle == 0) {
 
2661
        handles << mPriv->connection->selfHandle();
 
2662
    } else {
 
2663
        handles << mPriv->groupSelfHandle;
 
2664
    }
 
2665
 
 
2666
    return new PendingVoid(
 
2667
            mPriv->group->AddMembers(handles, QLatin1String("")),
 
2668
            ChannelPtr(this));
 
2669
}
 
2670
 
 
2671
//@}
 
2672
 
 
2673
/**
 
2674
 * Return whether this channel implements the conference interface
 
2675
 * (#TP_QT_IFACE_CHANNEL_INTERFACE_CONFERENCE is in the list returned by interfaces()).
 
2676
 *
 
2677
 * This method requires Channel::FeatureCore to be ready.
 
2678
 *
 
2679
 * \return \c true if the conference interface is supported, \c false otherwise.
 
2680
 */
 
2681
bool Channel::isConference() const
 
2682
{
 
2683
    return hasInterface(TP_QT_IFACE_CHANNEL_INTERFACE_CONFERENCE);
 
2684
}
 
2685
 
 
2686
/**
 
2687
 * Return a list of contacts invited to this conference when it was created.
 
2688
 *
 
2689
 * This method requires Channel::FeatureConferenceInitialInviteeContacts to be ready.
 
2690
 *
 
2691
 * \return A set of pointers to the Contact objects.
 
2692
 */
 
2693
Contacts Channel::conferenceInitialInviteeContacts() const
 
2694
{
 
2695
    return mPriv->conferenceInitialInviteeContacts;
 
2696
}
 
2697
 
 
2698
/**
 
2699
 * Return the individual channels that are part of this conference.
 
2700
 *
 
2701
 * Change notification is via the conferenceChannelMerged() and
 
2702
 * conferenceChannelRemoved() signals.
 
2703
 *
 
2704
 * Note that the returned channels are not guaranteed to be ready. Calling
 
2705
 * Channel::becomeReady() may be needed.
 
2706
 *
 
2707
 * This method requires Channel::FeatureCore to be ready.
 
2708
 *
 
2709
 * \return A list of pointers to Channel objects containing all channels in the conference.
 
2710
 * \sa conferenceInitialChannels(), conferenceOriginalChannels()
 
2711
 */
 
2712
QList<ChannelPtr> Channel::conferenceChannels() const
 
2713
{
 
2714
    return mPriv->conferenceChannels.values();
 
2715
}
 
2716
 
 
2717
/**
 
2718
 * Return the initial value of conferenceChannels().
 
2719
 *
 
2720
 * Note that the returned channels are not guaranteed to be ready. Calling
 
2721
 * Channel::becomeReady() may be needed.
 
2722
 *
 
2723
 * This method requires Channel::FeatureCore to be ready.
 
2724
 *
 
2725
 * \return A list of pointers to Channel objects containing all channels that were initially
 
2726
 *         part of the conference.
 
2727
 * \sa conferenceChannels(), conferenceOriginalChannels()
 
2728
 */
 
2729
QList<ChannelPtr> Channel::conferenceInitialChannels() const
 
2730
{
 
2731
    return mPriv->conferenceInitialChannels.values();
 
2732
}
 
2733
 
 
2734
/**
 
2735
 * Return a map between channel specific handles and the corresponding channels of this conference.
 
2736
 *
 
2737
 * This method is only relevant on GSM conference calls where it is possible to have the same phone
 
2738
 * number in a conference twice; for instance, it could be the number of a corporate switchboard.
 
2739
 * This is represented using channel-specific handles; whether or not a channel uses
 
2740
 * channel-specific handles is reported in groupFlags(). The groupHandleOwners() specifies the
 
2741
 * mapping from opaque channel-specific handles to actual numbers; this property specifies the
 
2742
 * original 1-1 channel corresponding to each channel-specific handle in the conference.
 
2743
 *
 
2744
 * In protocols where this situation cannot arise, such as XMPP, this method will return an empty
 
2745
 * hash.
 
2746
 *
 
2747
 * Example, consider this situation:
 
2748
 * 1. Place a call (with path /call/to/simon) to the contact +441234567890 (which is assigned the
 
2749
 *    handle h, say), and ask to be put through to Simon McVittie;
 
2750
 * 2. Put that call on hold;
 
2751
 * 3. Place another call (with path /call/to/jonny) to +441234567890, and ask to be put through to
 
2752
 *    Jonny Lamb;
 
2753
 * 4. Request a new conference channel with initial channels: ['/call/to/simon', '/call/to/jonny'].
 
2754
 *
 
2755
 * The new channel will have the following properties, for some handles s and j:
 
2756
 *
 
2757
 * {
 
2758
 * groupFlags(): ChannelGroupFlagChannelSpecificHandles | (other flags),
 
2759
 * groupMembers(): [self handle, s, j],
 
2760
 * groupHandleOwners(): { s: h, j: h },
 
2761
 * conferenceInitialChannels(): ['/call/to/simon', '/call/to/jonny'],
 
2762
 * conferenceChannels(): ['/call/to/simon', '/call/to/jonny'],
 
2763
 * conferenceOriginalChannels(): { s: '/call/to/simon', j: '/call/to/jonny' },
 
2764
 * # ...
 
2765
 * }
 
2766
 *
 
2767
 * Note that the returned channels are not guaranteed to be ready. Calling
 
2768
 * Channel::becomeReady() may be needed.
 
2769
 *
 
2770
 * This method requires Channel::FeatureCore to be ready.
 
2771
 *
 
2772
 * \return A map of channel specific handles to pointers to Channel objects.
 
2773
 * \sa conferenceChannels(), conferenceInitialChannels()
 
2774
 */
 
2775
QHash<uint, ChannelPtr> Channel::conferenceOriginalChannels() const
 
2776
{
 
2777
    return mPriv->conferenceOriginalChannels;
 
2778
}
 
2779
 
 
2780
/**
 
2781
 * Return whether this channel supports conference merging using conferenceMergeChannel().
 
2782
 *
 
2783
 * This method requires Channel::FeatureCore to be ready.
 
2784
 *
 
2785
 * \return \c true if the interface is supported, \c false otherwise.
 
2786
 * \sa conferenceMergeChannel()
 
2787
 */
 
2788
bool Channel::supportsConferenceMerging() const
 
2789
{
 
2790
    return interfaces().contains(TP_QT_FUTURE_IFACE_CHANNEL_INTERFACE_MERGEABLE_CONFERENCE);
 
2791
}
 
2792
 
 
2793
/**
 
2794
 * Request that the given channel be incorporated into this channel.
 
2795
 *
 
2796
 * This method requires Channel::FeatureCore to be ready.
 
2797
 *
 
2798
 * \return A PendingOperation which will emit PendingOperation::finished
 
2799
 *         when the call has finished.
 
2800
 * \sa supportsConferenceMerging()
 
2801
 */
 
2802
PendingOperation *Channel::conferenceMergeChannel(const ChannelPtr &channel)
 
2803
{
 
2804
    if (!supportsConferenceMerging()) {
 
2805
        return new PendingFailure(TP_QT_ERROR_NOT_IMPLEMENTED,
 
2806
                QLatin1String("Channel does not support MergeableConference interface"),
 
2807
                ChannelPtr(this));
 
2808
    }
 
2809
 
 
2810
    return new PendingVoid(mPriv->mergeableConferenceInterface()->Merge(
 
2811
                QDBusObjectPath(channel->objectPath())),
 
2812
                ChannelPtr(this));
 
2813
}
 
2814
 
 
2815
/**
 
2816
 * Return whether this channel supports splitting using conferenceSplitChannel().
 
2817
 *
 
2818
 * This method requires Channel::FeatureCore to be ready.
 
2819
 *
 
2820
 * \return \c true if the interface is supported, \c false otherwise.
 
2821
 * \sa conferenceSplitChannel()
 
2822
 */
 
2823
bool Channel::supportsConferenceSplitting() const
 
2824
{
 
2825
    return interfaces().contains(TP_QT_FUTURE_IFACE_CHANNEL_INTERFACE_SPLITTABLE);
 
2826
}
 
2827
 
 
2828
/**
 
2829
 * Request that this channel is removed from any conference of which it is
 
2830
 * a part.
 
2831
 *
 
2832
 * This method requires Channel::FeatureCore to be ready.
 
2833
 *
 
2834
 * \return A PendingOperation which will emit PendingOperation::finished
 
2835
 *         when the call has finished.
 
2836
 * \sa supportsConferenceSplitting()
 
2837
 */
 
2838
PendingOperation *Channel::conferenceSplitChannel()
 
2839
{
 
2840
    if (!supportsConferenceSplitting()) {
 
2841
        return new PendingFailure(TP_QT_ERROR_NOT_IMPLEMENTED,
 
2842
                QLatin1String("Channel does not support Splittable interface"),
 
2843
                ChannelPtr(this));
 
2844
    }
 
2845
 
 
2846
    return new PendingVoid(mPriv->splittableInterface()->Split(), ChannelPtr(this));
 
2847
}
 
2848
 
 
2849
/**
 
2850
 * Return the Client::ChannelInterface interface proxy object for this channel.
 
2851
 * This method is protected since the convenience methods provided by this
 
2852
 * class should generally be used instead of calling D-Bus methods
 
2853
 * directly.
 
2854
 *
 
2855
 * \return A pointer to the existing Client::ChannelInterface object for this
 
2856
 *         Channel object.
 
2857
 */
 
2858
Client::ChannelInterface *Channel::baseInterface() const
 
2859
{
 
2860
    return mPriv->baseInterface;
 
2861
}
 
2862
 
 
2863
void Channel::gotMainProperties(QDBusPendingCallWatcher *watcher)
 
2864
{
 
2865
    QDBusPendingReply<QVariantMap> reply = *watcher;
 
2866
    QVariantMap props;
 
2867
 
 
2868
    if (!reply.isError()) {
 
2869
        debug() << "Got reply to Properties::GetAll(Channel)";
 
2870
        props = reply.value();
 
2871
    } else {
 
2872
        warning().nospace() << "Properties::GetAll(Channel) failed with " <<
 
2873
            reply.error().name() << ": " << reply.error().message();
 
2874
    }
 
2875
 
 
2876
    mPriv->extractMainProps(props);
 
2877
 
 
2878
    mPriv->continueIntrospection();
 
2879
}
 
2880
 
 
2881
void Channel::gotChannelType(QDBusPendingCallWatcher *watcher)
 
2882
{
 
2883
    QDBusPendingReply<QString> reply = *watcher;
 
2884
 
 
2885
    if (reply.isError()) {
 
2886
        warning().nospace() << "Channel::GetChannelType() failed with " <<
 
2887
            reply.error().name() << ": " << reply.error().message() <<
 
2888
            ", Channel officially dead";
 
2889
        invalidate(reply.error());
 
2890
        return;
 
2891
    }
 
2892
 
 
2893
    debug() << "Got reply to fallback Channel::GetChannelType()";
 
2894
    mPriv->channelType = reply.value();
 
2895
    mPriv->continueIntrospection();
 
2896
}
 
2897
 
 
2898
void Channel::gotHandle(QDBusPendingCallWatcher *watcher)
 
2899
{
 
2900
    QDBusPendingReply<uint, uint> reply = *watcher;
 
2901
 
 
2902
    if (reply.isError()) {
 
2903
        warning().nospace() << "Channel::GetHandle() failed with " <<
 
2904
            reply.error().name() << ": " << reply.error().message() <<
 
2905
            ", Channel officially dead";
 
2906
        invalidate(reply.error());
 
2907
        return;
 
2908
    }
 
2909
 
 
2910
    debug() << "Got reply to fallback Channel::GetHandle()";
 
2911
    mPriv->targetHandleType = reply.argumentAt<0>();
 
2912
    mPriv->targetHandle = reply.argumentAt<1>();
 
2913
    mPriv->continueIntrospection();
 
2914
}
 
2915
 
 
2916
void Channel::gotInterfaces(QDBusPendingCallWatcher *watcher)
 
2917
{
 
2918
    QDBusPendingReply<QStringList> reply = *watcher;
 
2919
 
 
2920
    if (reply.isError()) {
 
2921
        warning().nospace() << "Channel::GetInterfaces() failed with " <<
 
2922
            reply.error().name() << ": " << reply.error().message() <<
 
2923
            ", Channel officially dead";
 
2924
        invalidate(reply.error());
 
2925
        return;
 
2926
    }
 
2927
 
 
2928
    debug() << "Got reply to fallback Channel::GetInterfaces()";
 
2929
    setInterfaces(reply.value());
 
2930
    mPriv->readinessHelper->setInterfaces(interfaces());
 
2931
    mPriv->nowHaveInterfaces();
 
2932
 
 
2933
    mPriv->fakeGroupInterfaceIfNeeded();
 
2934
 
 
2935
    mPriv->continueIntrospection();
 
2936
}
 
2937
 
 
2938
void Channel::onClosed()
 
2939
{
 
2940
    debug() << "Got Channel::Closed";
 
2941
 
 
2942
    QString error;
 
2943
    QString message;
 
2944
    if (mPriv->groupSelfContactRemoveInfo.isValid() &&
 
2945
        mPriv->groupSelfContactRemoveInfo.hasReason()) {
 
2946
        error = mPriv->groupMemberChangeDetailsTelepathyError(
 
2947
                mPriv->groupSelfContactRemoveInfo);
 
2948
        message = mPriv->groupSelfContactRemoveInfo.message();
 
2949
    } else {
 
2950
        error = TP_QT_ERROR_CANCELLED;
 
2951
        message = QLatin1String("channel closed");
 
2952
    }
 
2953
 
 
2954
    invalidate(error, message);
 
2955
}
 
2956
 
 
2957
void Channel::onConnectionReady(PendingOperation *op)
 
2958
{
 
2959
    if (op->isError()) {
 
2960
        invalidate(op->errorName(), op->errorMessage());
 
2961
        return;
 
2962
    }
 
2963
 
 
2964
    // FIXME: should connect to selfHandleChanged and act accordingly, but that is a PITA for
 
2965
    // keeping the Contacts built and even if we don't do it, the new code is better than the
 
2966
    // old one anyway because earlier on we just wouldn't have had a self contact.
 
2967
    //
 
2968
    // besides, the only thing which breaks without connecting in the world likely is if you're
 
2969
    // using idle and decide to change your nick, which I don't think we necessarily even have API
 
2970
    // to do from tp-qt anyway (or did I make idle change the nick when setting your alias? can't
 
2971
    // remember)
 
2972
    //
 
2973
    // Simply put, I just don't care ATM.
 
2974
 
 
2975
    // Will be overwritten by the group self handle, if we can discover any.
 
2976
    Q_ASSERT(!mPriv->groupSelfHandle);
 
2977
    mPriv->groupSelfHandle = mPriv->connection->selfHandle();
 
2978
 
 
2979
    mPriv->introspectMainProperties();
 
2980
}
 
2981
 
 
2982
void Channel::onConnectionInvalidated()
 
2983
{
 
2984
    debug() << "Owning connection died leaving an orphan Channel, "
 
2985
        "changing to closed";
 
2986
    invalidate(TP_QT_ERROR_ORPHANED,
 
2987
               QLatin1String("Connection given as the owner of this channel was invalidated"));
 
2988
}
 
2989
 
 
2990
void Channel::gotGroupProperties(QDBusPendingCallWatcher *watcher)
 
2991
{
 
2992
    QDBusPendingReply<QVariantMap> reply = *watcher;
 
2993
    QVariantMap props;
 
2994
 
 
2995
    if (!reply.isError()) {
 
2996
        debug() << "Got reply to Properties::GetAll(Channel.Interface.Group)";
 
2997
        props = reply.value();
 
2998
    }
 
2999
    else {
 
3000
        warning().nospace() << "Properties::GetAll(Channel.Interface.Group) "
 
3001
            "failed with " << reply.error().name() << ": " <<
 
3002
            reply.error().message();
 
3003
    }
 
3004
 
 
3005
    mPriv->extract0176GroupProps(props);
 
3006
    // Add extraction (and possible fallbacks) in similar functions, called from here
 
3007
 
 
3008
    mPriv->continueIntrospection();
 
3009
}
 
3010
 
 
3011
void Channel::gotGroupFlags(QDBusPendingCallWatcher *watcher)
 
3012
{
 
3013
    QDBusPendingReply<uint> reply = *watcher;
 
3014
 
 
3015
    if (reply.isError()) {
 
3016
        warning().nospace() << "Channel.Interface.Group::GetGroupFlags() failed with " <<
 
3017
            reply.error().name() << ": " << reply.error().message();
 
3018
    }
 
3019
    else {
 
3020
        debug() << "Got reply to fallback Channel.Interface.Group::GetGroupFlags()";
 
3021
        mPriv->setGroupFlags(reply.value());
 
3022
 
 
3023
        if (mPriv->groupFlags & ChannelGroupFlagProperties) {
 
3024
            warning() << " Reply included ChannelGroupFlagProperties, even "
 
3025
                "though properties specified in 0.17.7 didn't work! - unsetting";
 
3026
            mPriv->groupFlags &= ~ChannelGroupFlagProperties;
 
3027
        }
 
3028
    }
 
3029
 
 
3030
    mPriv->continueIntrospection();
 
3031
}
 
3032
 
 
3033
void Channel::gotAllMembers(QDBusPendingCallWatcher *watcher)
 
3034
{
 
3035
    QDBusPendingReply<UIntList, UIntList, UIntList> reply = *watcher;
 
3036
 
 
3037
    if (reply.isError()) {
 
3038
        warning().nospace() << "Channel.Interface.Group::GetAllMembers() failed with " <<
 
3039
            reply.error().name() << ": " << reply.error().message();
 
3040
    } else {
 
3041
        debug() << "Got reply to fallback Channel.Interface.Group::GetAllMembers()";
 
3042
 
 
3043
        mPriv->groupInitialMembers = reply.argumentAt<0>();
 
3044
        mPriv->groupInitialRP = reply.argumentAt<2>();
 
3045
 
 
3046
        foreach (uint handle, reply.argumentAt<1>()) {
 
3047
            LocalPendingInfo info = {handle, 0, ChannelGroupChangeReasonNone,
 
3048
                QLatin1String("")};
 
3049
            mPriv->groupInitialLP.push_back(info);
 
3050
        }
 
3051
    }
 
3052
 
 
3053
    mPriv->continueIntrospection();
 
3054
}
 
3055
 
 
3056
void Channel::gotLocalPendingMembersWithInfo(QDBusPendingCallWatcher *watcher)
 
3057
{
 
3058
    QDBusPendingReply<LocalPendingInfoList> reply = *watcher;
 
3059
 
 
3060
    if (reply.isError()) {
 
3061
        warning().nospace() << "Channel.Interface.Group::GetLocalPendingMembersWithInfo() "
 
3062
            "failed with " << reply.error().name() << ": " << reply.error().message();
 
3063
        warning() << " Falling back to what GetAllMembers returned with no extended info";
 
3064
    }
 
3065
    else {
 
3066
        debug() << "Got reply to fallback "
 
3067
            "Channel.Interface.Group::GetLocalPendingMembersWithInfo()";
 
3068
        // Overrides the previous vague list provided by gotAllMembers
 
3069
        mPriv->groupInitialLP = reply.value();
 
3070
    }
 
3071
 
 
3072
    mPriv->continueIntrospection();
 
3073
}
 
3074
 
 
3075
void Channel::gotSelfHandle(QDBusPendingCallWatcher *watcher)
 
3076
{
 
3077
    QDBusPendingReply<uint> reply = *watcher;
 
3078
 
 
3079
    if (reply.isError()) {
 
3080
        warning().nospace() << "Channel.Interface.Group::GetSelfHandle() failed with " <<
 
3081
            reply.error().name() << ": " << reply.error().message();
 
3082
    } else {
 
3083
        debug() << "Got reply to fallback Channel.Interface.Group::GetSelfHandle()";
 
3084
        // Don't overwrite the self handle we got from the connection with 0
 
3085
        if (reply.value()) {
 
3086
            mPriv->groupSelfHandle = reply.value();
 
3087
        }
 
3088
    }
 
3089
 
 
3090
    mPriv->nowHaveInitialMembers();
 
3091
 
 
3092
    mPriv->continueIntrospection();
 
3093
}
 
3094
 
 
3095
void Channel::gotContacts(PendingOperation *op)
 
3096
{
 
3097
    PendingContacts *pending = qobject_cast<PendingContacts *>(op);
 
3098
 
 
3099
    mPriv->buildingContacts = false;
 
3100
 
 
3101
    QList<ContactPtr> contacts;
 
3102
    if (pending->isValid()) {
 
3103
        contacts = pending->contacts();
 
3104
 
 
3105
        if (!pending->invalidHandles().isEmpty()) {
 
3106
            warning() << "Unable to construct Contact objects for handles:" <<
 
3107
                pending->invalidHandles();
 
3108
 
 
3109
            if (mPriv->groupSelfHandle &&
 
3110
                pending->invalidHandles().contains(mPriv->groupSelfHandle)) {
 
3111
                warning() << "Unable to retrieve self contact";
 
3112
                mPriv->groupSelfContact.reset();
 
3113
                emit groupSelfContactChanged();
 
3114
            }
 
3115
        }
 
3116
    } else {
 
3117
        warning().nospace() << "Getting contacts failed with " <<
 
3118
            pending->errorName() << ":" << pending->errorMessage();
 
3119
    }
 
3120
 
 
3121
    mPriv->updateContacts(contacts);
 
3122
}
 
3123
 
 
3124
void Channel::onGroupFlagsChanged(uint added, uint removed)
 
3125
{
 
3126
    debug().nospace() << "Got Channel.Interface.Group::GroupFlagsChanged(" <<
 
3127
        hex << added << ", " << removed << ")";
 
3128
 
 
3129
    added &= ~(mPriv->groupFlags);
 
3130
    removed &= mPriv->groupFlags;
 
3131
 
 
3132
    debug().nospace() << "Arguments after filtering (" << hex << added <<
 
3133
        ", " << removed << ")";
 
3134
 
 
3135
    uint groupFlags = mPriv->groupFlags;
 
3136
    groupFlags |= added;
 
3137
    groupFlags &= ~removed;
 
3138
    // just emit groupFlagsChanged and related signals if the flags really
 
3139
    // changed and we are ready
 
3140
    if (mPriv->setGroupFlags(groupFlags) && isReady(Channel::FeatureCore)) {
 
3141
        debug() << "Emitting groupFlagsChanged with" << mPriv->groupFlags <<
 
3142
            "value" << added << "added" << removed << "removed";
 
3143
        emit groupFlagsChanged((ChannelGroupFlags) mPriv->groupFlags,
 
3144
                (ChannelGroupFlags) added, (ChannelGroupFlags) removed);
 
3145
 
 
3146
        if (added & ChannelGroupFlagCanAdd ||
 
3147
            removed & ChannelGroupFlagCanAdd) {
 
3148
            debug() << "Emitting groupCanAddContactsChanged";
 
3149
            emit groupCanAddContactsChanged(groupCanAddContacts());
 
3150
        }
 
3151
 
 
3152
        if (added & ChannelGroupFlagCanRemove ||
 
3153
            removed & ChannelGroupFlagCanRemove) {
 
3154
            debug() << "Emitting groupCanRemoveContactsChanged";
 
3155
            emit groupCanRemoveContactsChanged(groupCanRemoveContacts());
 
3156
        }
 
3157
 
 
3158
        if (added & ChannelGroupFlagCanRescind ||
 
3159
            removed & ChannelGroupFlagCanRescind) {
 
3160
            debug() << "Emitting groupCanRescindContactsChanged";
 
3161
            emit groupCanRescindContactsChanged(groupCanRescindContacts());
 
3162
        }
 
3163
    }
 
3164
}
 
3165
 
 
3166
void Channel::onMembersChanged(const QString &message,
 
3167
        const UIntList &added, const UIntList &removed,
 
3168
        const UIntList &localPending, const UIntList &remotePending,
 
3169
        uint actor, uint reason)
 
3170
{
 
3171
    // Ignore the signal if we're using the MCD signal to not duplicate events
 
3172
    if (mPriv->usingMembersChangedDetailed) {
 
3173
        return;
 
3174
    }
 
3175
 
 
3176
    debug() << "Got Channel.Interface.Group::MembersChanged with" << added.size() <<
 
3177
        "added," << removed.size() << "removed," << localPending.size() <<
 
3178
        "moved to LP," << remotePending.size() << "moved to RP," << actor <<
 
3179
        "being the actor," << reason << "the reason and" << message << "the message";
 
3180
    debug() << " synthesizing a corresponding MembersChangedDetailed signal";
 
3181
 
 
3182
    QVariantMap details;
 
3183
 
 
3184
    if (!message.isEmpty()) {
 
3185
        details.insert(QLatin1String("message"), message);
 
3186
    }
 
3187
 
 
3188
    if (actor != 0) {
 
3189
        details.insert(QLatin1String("actor"), actor);
 
3190
    }
 
3191
 
 
3192
    details.insert(QLatin1String("change-reason"), reason);
 
3193
 
 
3194
    mPriv->doMembersChangedDetailed(added, removed, localPending, remotePending, details);
 
3195
}
 
3196
 
 
3197
void Channel::onMembersChangedDetailed(
 
3198
        const UIntList &added, const UIntList &removed,
 
3199
        const UIntList &localPending, const UIntList &remotePending,
 
3200
        const QVariantMap &details)
 
3201
{
 
3202
    // Ignore the signal if we aren't (yet) using MCD to not duplicate events
 
3203
    if (!mPriv->usingMembersChangedDetailed) {
 
3204
        return;
 
3205
    }
 
3206
 
 
3207
    debug() << "Got Channel.Interface.Group::MembersChangedDetailed with" << added.size() <<
 
3208
        "added," << removed.size() << "removed," << localPending.size() <<
 
3209
        "moved to LP," << remotePending.size() << "moved to RP and with" << details.size() <<
 
3210
        "details";
 
3211
 
 
3212
    mPriv->doMembersChangedDetailed(added, removed, localPending, remotePending, details);
 
3213
}
 
3214
 
 
3215
void Channel::Private::doMembersChangedDetailed(
 
3216
        const UIntList &added, const UIntList &removed,
 
3217
        const UIntList &localPending, const UIntList &remotePending,
 
3218
        const QVariantMap &details)
 
3219
{
 
3220
    if (!groupHaveMembers) {
 
3221
        debug() << "Still waiting for initial group members, "
 
3222
            "so ignoring delta signal...";
 
3223
        return;
 
3224
    }
 
3225
 
 
3226
    if (added.isEmpty() && removed.isEmpty() &&
 
3227
        localPending.isEmpty() && remotePending.isEmpty()) {
 
3228
        debug() << "Nothing really changed, so skipping membersChanged";
 
3229
        return;
 
3230
    }
 
3231
 
 
3232
    // let's store groupSelfContactRemoveInfo here as we may not have time
 
3233
    // to build the contacts in case self contact is removed,
 
3234
    // as Closed will be emitted right after
 
3235
    if (removed.contains(groupSelfHandle)) {
 
3236
        if (qdbus_cast<uint>(details.value(QLatin1String("change-reason"))) ==
 
3237
                ChannelGroupChangeReasonRenamed) {
 
3238
            if (removed.size() != 1 ||
 
3239
                (added.size() + localPending.size() + remotePending.size()) != 1) {
 
3240
                // spec-incompliant CM, ignoring members changed
 
3241
                warning() << "Received MembersChangedDetailed with reason "
 
3242
                    "Renamed and removed.size != 1 or added.size + "
 
3243
                    "localPending.size + remotePending.size != 1. Ignoring";
 
3244
                return;
 
3245
            }
 
3246
            uint newHandle = 0;
 
3247
            if (!added.isEmpty()) {
 
3248
                newHandle = added.first();
 
3249
            } else if (!localPending.isEmpty()) {
 
3250
                newHandle = localPending.first();
 
3251
            } else if (!remotePending.isEmpty()) {
 
3252
                newHandle = remotePending.first();
 
3253
            }
 
3254
            parent->onSelfHandleChanged(newHandle);
 
3255
            return;
 
3256
        }
 
3257
 
 
3258
        // let's try to get the actor contact from contact manager if available
 
3259
        groupSelfContactRemoveInfo = GroupMemberChangeDetails(
 
3260
                connection->contactManager()->lookupContactByHandle(
 
3261
                    qdbus_cast<uint>(details.value(QLatin1String("actor")))),
 
3262
                details);
 
3263
    }
 
3264
 
 
3265
    HandleIdentifierMap contactIds = qdbus_cast<HandleIdentifierMap>(
 
3266
            details.value(GroupMembersChangedInfo::keyContactIds));
 
3267
    connection->lowlevel()->injectContactIds(contactIds);
 
3268
 
 
3269
    groupMembersChangedQueue.enqueue(
 
3270
            new Private::GroupMembersChangedInfo(
 
3271
                added, removed,
 
3272
                localPending, remotePending,
 
3273
                details));
 
3274
 
 
3275
    if (!buildingContacts) {
 
3276
        // if we are building contacts, we should wait it to finish so we don't
 
3277
        // present the user with wrong information
 
3278
        processMembersChanged();
 
3279
    }
 
3280
}
 
3281
 
 
3282
void Channel::onHandleOwnersChanged(const HandleOwnerMap &added,
 
3283
        const UIntList &removed)
 
3284
{
 
3285
    debug() << "Got Channel.Interface.Group::HandleOwnersChanged with" <<
 
3286
        added.size() << "added," << removed.size() << "removed";
 
3287
 
 
3288
    if (!mPriv->groupAreHandleOwnersAvailable) {
 
3289
        debug() << "Still waiting for initial handle owners, so ignoring "
 
3290
            "delta signal...";
 
3291
        return;
 
3292
    }
 
3293
 
 
3294
    UIntList emitAdded;
 
3295
    UIntList emitRemoved;
 
3296
 
 
3297
    for (HandleOwnerMap::const_iterator i = added.begin();
 
3298
                                        i != added.end();
 
3299
                                        ++i) {
 
3300
        uint handle = i.key();
 
3301
        uint global = i.value();
 
3302
 
 
3303
        if (!mPriv->groupHandleOwners.contains(handle)
 
3304
                || mPriv->groupHandleOwners[handle] != global) {
 
3305
            debug() << " +++/changed" << handle << "->" << global;
 
3306
            mPriv->groupHandleOwners[handle] = global;
 
3307
            emitAdded.append(handle);
 
3308
        }
 
3309
    }
 
3310
 
 
3311
    foreach (uint handle, removed) {
 
3312
        if (mPriv->groupHandleOwners.contains(handle)) {
 
3313
            debug() << " ---" << handle;
 
3314
            mPriv->groupHandleOwners.remove(handle);
 
3315
            emitRemoved.append(handle);
 
3316
        }
 
3317
    }
 
3318
 
 
3319
    // just emit groupHandleOwnersChanged if it really changed and
 
3320
    // we are ready
 
3321
    if ((emitAdded.size() || emitRemoved.size()) && isReady(Channel::FeatureCore)) {
 
3322
        debug() << "Emitting groupHandleOwnersChanged with" << emitAdded.size() <<
 
3323
            "added" << emitRemoved.size() << "removed";
 
3324
        emit groupHandleOwnersChanged(mPriv->groupHandleOwners,
 
3325
                emitAdded, emitRemoved);
 
3326
    }
 
3327
}
 
3328
 
 
3329
void Channel::onSelfHandleChanged(uint selfHandle)
 
3330
{
 
3331
    debug().nospace() << "Got Channel.Interface.Group::SelfHandleChanged";
 
3332
 
 
3333
    if (selfHandle != mPriv->groupSelfHandle) {
 
3334
        mPriv->groupSelfHandle = selfHandle;
 
3335
        debug() << " Emitting groupSelfHandleChanged with new self handle" <<
 
3336
            selfHandle;
 
3337
 
 
3338
        // FIXME: fix self contact building with no group
 
3339
        mPriv->pendingRetrieveGroupSelfContact = true;
 
3340
    }
 
3341
}
 
3342
 
 
3343
void Channel::gotConferenceProperties(QDBusPendingCallWatcher *watcher)
 
3344
{
 
3345
    QDBusPendingReply<QVariantMap> reply = *watcher;
 
3346
    QVariantMap props;
 
3347
 
 
3348
    mPriv->introspectingConference = false;
 
3349
 
 
3350
    if (!reply.isError()) {
 
3351
        debug() << "Got reply to Properties::GetAll(Channel.Interface.Conference)";
 
3352
        props = reply.value();
 
3353
 
 
3354
        ConnectionPtr conn = connection();
 
3355
        ChannelFactoryConstPtr chanFactory = conn->channelFactory();
 
3356
 
 
3357
        ObjectPathList channels =
 
3358
            qdbus_cast<ObjectPathList>(props[QLatin1String("Channels")]);
 
3359
        foreach (const QDBusObjectPath &channelPath, channels) {
 
3360
            if (mPriv->conferenceChannels.contains(channelPath.path())) {
 
3361
                continue;
 
3362
            }
 
3363
 
 
3364
            PendingReady *readyOp = chanFactory->proxy(conn,
 
3365
                    channelPath.path(), QVariantMap());
 
3366
            ChannelPtr channel(ChannelPtr::qObjectCast(readyOp->proxy()));
 
3367
            Q_ASSERT(!channel.isNull());
 
3368
 
 
3369
            mPriv->conferenceChannels.insert(channelPath.path(), channel);
 
3370
        }
 
3371
 
 
3372
        ObjectPathList initialChannels =
 
3373
            qdbus_cast<ObjectPathList>(props[QLatin1String("InitialChannels")]);
 
3374
        foreach (const QDBusObjectPath &channelPath, initialChannels) {
 
3375
            if (mPriv->conferenceInitialChannels.contains(channelPath.path())) {
 
3376
                continue;
 
3377
            }
 
3378
 
 
3379
            PendingReady *readyOp = chanFactory->proxy(conn,
 
3380
                    channelPath.path(), QVariantMap());
 
3381
            ChannelPtr channel(ChannelPtr::qObjectCast(readyOp->proxy()));
 
3382
            Q_ASSERT(!channel.isNull());
 
3383
 
 
3384
            mPriv->conferenceInitialChannels.insert(channelPath.path(), channel);
 
3385
        }
 
3386
 
 
3387
        mPriv->conferenceInitialInviteeHandles =
 
3388
            qdbus_cast<UIntList>(props[QLatin1String("InitialInviteeHandles")]);
 
3389
        QStringList conferenceInitialInviteeIds =
 
3390
            qdbus_cast<QStringList>(props[QLatin1String("InitialInviteeIDs")]);
 
3391
        if (mPriv->conferenceInitialInviteeHandles.size() == conferenceInitialInviteeIds.size()) {
 
3392
            HandleIdentifierMap contactIds;
 
3393
            int i = 0;
 
3394
            foreach (uint handle, mPriv->conferenceInitialInviteeHandles) {
 
3395
                contactIds.insert(handle, conferenceInitialInviteeIds.at(i++));
 
3396
            }
 
3397
            mPriv->connection->lowlevel()->injectContactIds(contactIds);
 
3398
        }
 
3399
 
 
3400
        mPriv->conferenceInvitationMessage =
 
3401
            qdbus_cast<QString>(props[QLatin1String("InvitationMessage")]);
 
3402
 
 
3403
        ChannelOriginatorMap originalChannels = qdbus_cast<ChannelOriginatorMap>(
 
3404
                props[QLatin1String("OriginalChannels")]);
 
3405
        for (ChannelOriginatorMap::const_iterator i = originalChannels.constBegin();
 
3406
                i != originalChannels.constEnd(); ++i) {
 
3407
            PendingReady *readyOp = chanFactory->proxy(conn,
 
3408
                    i.value().path(), QVariantMap());
 
3409
            ChannelPtr channel(ChannelPtr::qObjectCast(readyOp->proxy()));
 
3410
            Q_ASSERT(!channel.isNull());
 
3411
 
 
3412
            mPriv->conferenceOriginalChannels.insert(i.key(), channel);
 
3413
        }
 
3414
    } else {
 
3415
        warning().nospace() << "Properties::GetAll(Channel.Interface.Conference) "
 
3416
            "failed with " << reply.error().name() << ": " <<
 
3417
            reply.error().message();
 
3418
    }
 
3419
 
 
3420
    mPriv->continueIntrospection();
 
3421
}
 
3422
 
 
3423
void Channel::gotConferenceInitialInviteeContacts(PendingOperation *op)
 
3424
{
 
3425
    PendingContacts *pending = qobject_cast<PendingContacts *>(op);
 
3426
 
 
3427
    if (pending->isValid()) {
 
3428
        mPriv->conferenceInitialInviteeContacts = pending->contacts().toSet();
 
3429
    } else {
 
3430
        warning().nospace() << "Getting conference initial invitee contacts "
 
3431
            "failed with " << pending->errorName() << ":" <<
 
3432
            pending->errorMessage();
 
3433
    }
 
3434
 
 
3435
    mPriv->readinessHelper->setIntrospectCompleted(
 
3436
            FeatureConferenceInitialInviteeContacts, true);
 
3437
}
 
3438
 
 
3439
void Channel::onConferenceChannelMerged(const QDBusObjectPath &channelPath,
 
3440
        uint channelSpecificHandle, const QVariantMap &properties)
 
3441
{
 
3442
    if (mPriv->conferenceChannels.contains(channelPath.path())) {
 
3443
        return;
 
3444
    }
 
3445
 
 
3446
    ConnectionPtr conn = connection();
 
3447
    ChannelFactoryConstPtr chanFactory = conn->channelFactory();
 
3448
    PendingReady *readyOp = chanFactory->proxy(conn,
 
3449
            channelPath.path(), properties);
 
3450
    ChannelPtr channel(ChannelPtr::qObjectCast(readyOp->proxy()));
 
3451
    Q_ASSERT(!channel.isNull());
 
3452
 
 
3453
    mPriv->conferenceChannels.insert(channelPath.path(), channel);
 
3454
    emit conferenceChannelMerged(channel);
 
3455
 
 
3456
    if (channelSpecificHandle != 0) {
 
3457
        mPriv->conferenceOriginalChannels.insert(channelSpecificHandle, channel);
 
3458
    }
 
3459
}
 
3460
 
 
3461
void Channel::onConferenceChannelMerged(const QDBusObjectPath &channelPath)
 
3462
{
 
3463
    onConferenceChannelMerged(channelPath, 0, QVariantMap());
 
3464
}
 
3465
 
 
3466
void Channel::onConferenceChannelRemoved(const QDBusObjectPath &channelPath,
 
3467
        const QVariantMap &details)
 
3468
{
 
3469
    if (!mPriv->conferenceChannels.contains(channelPath.path())) {
 
3470
        return;
 
3471
    }
 
3472
 
 
3473
    HandleIdentifierMap contactIds = qdbus_cast<HandleIdentifierMap>(
 
3474
            details.value(Private::GroupMembersChangedInfo::keyContactIds));
 
3475
    mPriv->connection->lowlevel()->injectContactIds(contactIds);
 
3476
 
 
3477
    mPriv->conferenceChannelRemovedQueue.enqueue(
 
3478
            new Private::ConferenceChannelRemovedInfo(channelPath, details));
 
3479
    mPriv->processConferenceChannelRemoved();
 
3480
}
 
3481
 
 
3482
void Channel::onConferenceChannelRemoved(const QDBusObjectPath &channelPath)
 
3483
{
 
3484
    onConferenceChannelRemoved(channelPath, QVariantMap());
 
3485
}
 
3486
 
 
3487
void Channel::gotConferenceChannelRemovedActorContact(PendingOperation *op)
 
3488
{
 
3489
    ContactPtr actorContact;
 
3490
 
 
3491
    if (op) {
 
3492
        PendingContacts *pc = qobject_cast<PendingContacts *>(op);
 
3493
 
 
3494
        if (pc->isValid()) {
 
3495
            Q_ASSERT(pc->contacts().size() == 1);
 
3496
            actorContact = pc->contacts().first();
 
3497
        } else {
 
3498
            warning().nospace() << "Getting conference channel removed actor "
 
3499
                "failed with " << pc->errorName() << ":" <<
 
3500
                pc->errorMessage();
 
3501
        }
 
3502
    }
 
3503
 
 
3504
    Private::ConferenceChannelRemovedInfo *info = mPriv->conferenceChannelRemovedQueue.dequeue();
 
3505
 
 
3506
    ChannelPtr channel = mPriv->conferenceChannels[info->channelPath.path()];
 
3507
    mPriv->conferenceChannels.remove(info->channelPath.path());
 
3508
    emit conferenceChannelRemoved(channel, GroupMemberChangeDetails(actorContact,
 
3509
                info->details));
 
3510
 
 
3511
    for (QHash<uint, ChannelPtr>::iterator i = mPriv->conferenceOriginalChannels.begin();
 
3512
            i != mPriv->conferenceOriginalChannels.end();) {
 
3513
        if (i.value() == channel) {
 
3514
            i = mPriv->conferenceOriginalChannels.erase(i);
 
3515
        } else {
 
3516
            ++i;
 
3517
        }
 
3518
    }
 
3519
 
 
3520
    delete info;
 
3521
 
 
3522
    mPriv->buildingConferenceChannelRemovedActorContact = false;
 
3523
    mPriv->processConferenceChannelRemoved();
 
3524
}
 
3525
 
 
3526
/**
 
3527
 * \fn void Channel::groupFlagsChanged(uint flags, uint added, uint removed)
 
3528
 *
 
3529
 * Emitted when the value of groupFlags() changes.
 
3530
 *
 
3531
 * \param flags The value which would now be returned by groupFlags().
 
3532
 * \param added Flags added compared to the previous value.
 
3533
 * \param removed Flags removed compared to the previous value.
 
3534
 */
 
3535
 
 
3536
/**
 
3537
 * \fn void Channel::groupCanAddContactsChanged(bool canAddContacts)
 
3538
 *
 
3539
 * Emitted when the value of groupCanAddContacts() changes.
 
3540
 *
 
3541
 * \param canAddContacts Whether a contact can be added to this channel.
 
3542
 * \sa groupCanAddContacts()
 
3543
 */
 
3544
 
 
3545
/**
 
3546
 * \fn void Channel::groupCanRemoveContactsChanged(bool canRemoveContacts)
 
3547
 *
 
3548
 * Emitted when the value of groupCanRemoveContacts() changes.
 
3549
 *
 
3550
 * \param canRemoveContacts Whether a contact can be removed from this channel.
 
3551
 * \sa groupCanRemoveContacts()
 
3552
 */
 
3553
 
 
3554
/**
 
3555
 * \fn void Channel::groupCanRescindContactsChanged(bool canRescindContacts)
 
3556
 *
 
3557
 * Emitted when the value of groupCanRescindContacts() changes.
 
3558
 *
 
3559
 * \param canRescindContacts Whether contact invitations can be rescinded.
 
3560
 * \sa groupCanRescindContacts()
 
3561
 */
 
3562
 
 
3563
/**
 
3564
 * \fn void Channel::groupMembersChanged(
 
3565
 *     const Tp::Contacts &groupMembersAdded,
 
3566
 *     const Tp::Contacts &groupLocalPendingMembersAdded,
 
3567
 *     const Tp::Contacts &groupRemotePendingMembersAdded,
 
3568
 *     const Tp::Contacts &groupMembersRemoved,
 
3569
 *     const Channel::GroupMemberChangeDetails &details)
 
3570
 *
 
3571
 * Emitted when the value returned by groupContacts(), groupLocalPendingContacts() or
 
3572
 * groupRemotePendingContacts() changes.
 
3573
 *
 
3574
 * \param groupMembersAdded The contacts that were added to this channel.
 
3575
 * \param groupLocalPendingMembersAdded The local pending contacts that were
 
3576
 *                                      added to this channel.
 
3577
 * \param groupRemotePendingMembersAdded The remote pending contacts that were
 
3578
 *                                       added to this channel.
 
3579
 * \param groupMembersRemoved The contacts removed from this channel.
 
3580
 * \param details Additional details such as the contact requesting or causing
 
3581
 *                the change.
 
3582
 */
 
3583
 
 
3584
/**
 
3585
 * \fn void Channel::groupHandleOwnersChanged(const HandleOwnerMap &owners,
 
3586
 *            const Tp::UIntList &added, const Tp::UIntList &removed)
 
3587
 *
 
3588
 * Emitted when the value returned by groupHandleOwners() changes.
 
3589
 *
 
3590
 * \param owners The value which would now be returned by
 
3591
 *               groupHandleOwners().
 
3592
 * \param added Handles which have been added to the mapping as keys, or
 
3593
 *              existing handle keys for which the mapped-to value has changed.
 
3594
 * \param removed Handles which have been removed from the mapping.
 
3595
 */
 
3596
 
 
3597
/**
 
3598
 * \fn void Channel::groupSelfContactChanged()
 
3599
 *
 
3600
 * Emitted when the value returned by groupSelfContact() changes.
 
3601
 */
 
3602
 
 
3603
/**
 
3604
 * \fn void Channel::conferenceChannelMerged(const Tp::ChannelPtr &channel)
 
3605
 *
 
3606
 * Emitted when a new channel is added to the value of conferenceChannels().
 
3607
 *
 
3608
 * \param channel The channel that was added to conferenceChannels().
 
3609
 */
 
3610
 
 
3611
/**
 
3612
 * \fn void Channel::conferenceChannelRemoved(const Tp::ChannelPtr &channel,
 
3613
 *          const Tp::Channel::GroupMemberChangeDetails &details)
 
3614
 *
 
3615
 * Emitted when a new channel is removed from the value of conferenceChannels().
 
3616
 *
 
3617
 * \param channel The channel that was removed from conferenceChannels().
 
3618
 * \param details The change details.
 
3619
 */
 
3620
 
 
3621
} // Tp