summaryrefslogtreecommitdiff
path: root/TelepathyQt/base-connection-manager.cpp
blob: fac2a7f56aa44557cc3741a61d5009573e461275 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
/**
 * This file is part of TelepathyQt
 *
 * @copyright Copyright (C) 2012 Collabora Ltd. <http://www.collabora.co.uk/>
 * @copyright Copyright (C) 2012 Nokia Corporation
 * @license LGPL 2.1
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include <TelepathyQt/BaseConnectionManager>
#include "TelepathyQt/base-connection-manager-internal.h"

#include "TelepathyQt/_gen/base-connection-manager.moc.hpp"
#include "TelepathyQt/_gen/base-connection-manager-internal.moc.hpp"

#include "TelepathyQt/debug-internal.h"

#include <TelepathyQt/BaseConnection>
#include <TelepathyQt/BaseProtocol>
#include <TelepathyQt/DBusObject>
#include <TelepathyQt/Constants>
#include <TelepathyQt/Utils>

#include <QDBusObjectPath>
#include <QString>
#include <QStringList>

namespace Tp
{

struct TP_QT_NO_EXPORT BaseConnectionManager::Private
{
    Private(BaseConnectionManager *parent, const QDBusConnection &dbusConnection,
            const QString &name)
        : parent(parent),
          name(name),
          adaptee(new BaseConnectionManager::Adaptee(dbusConnection, parent))
    {
    }

    BaseConnectionManager *parent;
    QString name;

    BaseConnectionManager::Adaptee *adaptee;
    QHash<QString, BaseProtocolPtr> protocols;
    QSet<BaseConnectionPtr> connections;
};

BaseConnectionManager::Adaptee::Adaptee(const QDBusConnection &dbusConnection,
        BaseConnectionManager *cm)
    : QObject(cm),
      mCM(cm)
{
    mAdaptor = new Service::ConnectionManagerAdaptor(dbusConnection, this, cm->dbusObject());
}

BaseConnectionManager::Adaptee::~Adaptee()
{
}

QStringList BaseConnectionManager::Adaptee::interfaces() const
{
    // No interfaces suitable for listing in this property are currently defined;
    // it's provided as a hook for possible future functionality.
    return QStringList();
}

ProtocolPropertiesMap BaseConnectionManager::Adaptee::protocols() const
{
    ProtocolPropertiesMap ret;
    foreach (const BaseProtocolPtr &protocol, mCM->protocols()) {
        ret.insert(protocol->name(), protocol->immutableProperties());
    }
    return ret;
}

void BaseConnectionManager::Adaptee::getParameters(const QString &protocolName,
        const Tp::Service::ConnectionManagerAdaptor::GetParametersContextPtr &context)
{
    if (!checkValidProtocolName(protocolName)) {
        context->setFinishedWithError(TP_QT_ERROR_INVALID_ARGUMENT,
                protocolName + QLatin1String(" is not a valid protocol name"));
        return;
    }

    if (!mCM->hasProtocol(protocolName)) {
        context->setFinishedWithError(TP_QT_ERROR_NOT_IMPLEMENTED,
                QLatin1String("unknown protocol ") + protocolName);
        return;
    }

    BaseProtocolPtr protocol = mCM->protocol(protocolName);
    ParamSpecList ret;
    foreach (const ProtocolParameter &param, protocol->parameters()) {
         ParamSpec paramSpec = param.bareParameter();
         if (!(paramSpec.flags & ConnMgrParamFlagHasDefault)) {
             // we cannot pass QVariant::Invalid over D-Bus, lets build a dummy value
             // that should be ignored according to the spec
             paramSpec.defaultValue = QDBusVariant(
                     parseValueWithDBusSignature(QString(), paramSpec.signature));
         }
         ret << paramSpec;
    }
    context->setFinished(ret);
}

void BaseConnectionManager::Adaptee::listProtocols(
        const Tp::Service::ConnectionManagerAdaptor::ListProtocolsContextPtr &context)
{
    QStringList ret;
    QList<BaseProtocolPtr> protocols = mCM->protocols();
    foreach (const BaseProtocolPtr &protocol, protocols) {
        ret << protocol->name();
    }
    context->setFinished(ret);
}

void BaseConnectionManager::Adaptee::requestConnection(const QString &protocolName,
        const QVariantMap &parameters,
        const Tp::Service::ConnectionManagerAdaptor::RequestConnectionContextPtr &context)
{
    if (!checkValidProtocolName(protocolName)) {
        context->setFinishedWithError(TP_QT_ERROR_INVALID_ARGUMENT,
                protocolName + QLatin1String(" is not a valid protocol name"));
        return;
    }

    if (!mCM->hasProtocol(protocolName)) {
        context->setFinishedWithError(TP_QT_ERROR_NOT_IMPLEMENTED,
                QLatin1String("unknown protocol ") + protocolName);
        return;
    }

    BaseProtocolPtr protocol = mCM->protocol(protocolName);

    DBusError error;
    BaseConnectionPtr connection;
    connection = protocol->createConnection(parameters, &error);
    if (!connection) {
        context->setFinishedWithError(error.name(), error.message());
        return;
    }

    if (!connection->registerObject(&error)) {
        context->setFinishedWithError(error.name(), error.message());
        return;
    }

    mCM->addConnection(connection);

    emit newConnection(connection->busName(), QDBusObjectPath(connection->objectPath()),
            protocol->name());
    context->setFinished(connection->busName(), QDBusObjectPath(connection->objectPath()));
}

/**
 * \class BaseConnectionManager
 * \ingroup servicecm
 * \headerfile TelepathyQt/base-connection-manager.h <TelepathyQt/BaseConnectionManager>
 *
 * \brief Base class for connection manager implementations.
 */

/**
 * Constructs a new BaseConnectionManager object that implements a connection manager
 * on the given \a dbusConnection and has the given \a name.
 *
 * \param dbusConnection The QDBusConnection to use.
 * \param name The name of the connection manager.
 */
BaseConnectionManager::BaseConnectionManager(const QDBusConnection &dbusConnection,
        const QString &name)
    : DBusService(dbusConnection),
      mPriv(new Private(this, dbusConnection, name))
{
}

/**
 * Class destructor.
 */
BaseConnectionManager::~BaseConnectionManager()
{
    delete mPriv;
}

/**
 * Return the connection manager's name, as given on the constructor.
 *
 * \return The connection manager's name.
 */
QString BaseConnectionManager::name() const
{
    return mPriv->name;
}

/**
 * Return the immutable properties of this connection manager object.
 *
 * Immutable properties cannot change after the object has been registered
 * on the bus with registerObject().
 *
 * \return The immutable properties of this connection manager object.
 */
QVariantMap BaseConnectionManager::immutableProperties() const
{
    QVariantMap ret;
    ret.insert(TP_QT_IFACE_CONNECTION_MANAGER + QLatin1String(".Protocols"),
            QVariant::fromValue(mPriv->adaptee->protocols()));
    return ret;
}

/**
 * Return a list of all protocols that this connection manager implements.
 *
 * This property is immutable and cannot change after the connection manager
 * has been registered on the bus with registerObject().
 *
 * \return A list of all protocols that this connection manager implements.
 * \sa addProtocol(), hasProtocol(), protocol()
 */
QList<BaseProtocolPtr> BaseConnectionManager::protocols() const
{
    return mPriv->protocols.values();
}

/**
 * Return a pointer to the BaseProtocol instance that implements the protocol
 * with the given \a protocolName, or a null BaseProtocolPtr if no such
 * protocol has been added to the connection manager.
 *
 * \param protocolName The name of the protocol in interest.
 * \return The BaseProtocol instance that implements the protocol with
 * the given \a protocolName.
 * \sa hasProtocol(), protocols(), addProtocol()
 */
BaseProtocolPtr BaseConnectionManager::protocol(const QString &protocolName) const
{
    return mPriv->protocols.value(protocolName);
}

/**
 * Return whether a protocol with the given \a protocolName has been
 * added to the connection manager.
 *
 * \param protocolName The name of the protocol in interest.
 * \return \c true if a protocol with the given \a protocolName has been
 * added to the connection manager, or \c false otherwise.
 * \sa addProtocol(), protocol(), protocols()
 */
bool BaseConnectionManager::hasProtocol(const QString &protocolName) const
{
    return mPriv->protocols.contains(protocolName);
}

/**
 * Add a new \a protocol to the list of protocols that this connection
 * manager implements.
 *
 * Note that you cannot add new protocols after the connection manager
 * has been registered on the bus with registerObject(). In addition,
 * you cannot add two protocols with the same name. If any of these
 * conditions is not met, this function will return false and print
 * a suitable warning.
 *
 * \param protocol The protocol to add.
 * \return \c true on success or \c false otherwise.
 */
bool BaseConnectionManager::addProtocol(const BaseProtocolPtr &protocol)
{
    if (isRegistered()) {
        warning() << "Unable to add protocol" << protocol->name() <<
            "- CM already registered";
        return false;
    }

    if (protocol->dbusConnection().name() != dbusConnection().name()) {
        warning() << "Unable to add protocol" << protocol->name() <<
            "- protocol must have the same D-Bus connection as the owning CM";
        return false;
    }

    if (protocol->isRegistered()) {
        warning() << "Unable to add protocol" << protocol->name() <<
            "- protocol already registered";
        return false;
    }

    if (mPriv->protocols.contains(protocol->name())) {
        warning() << "Unable to add protocol" << protocol->name() <<
            "- another protocol with same name already added";
        return false;
    }

    debug() << "Protocol" << protocol->name() << "added to CM";
    mPriv->protocols.insert(protocol->name(), protocol);
    return true;
}

/**
 * Register this connection manager on the bus.
 *
 * A connection manager can only be registered once, and it
 * should be registered only after all the protocols it implements
 * have been added with addProtocol().
 *
 * If \a error is passed, any D-Bus error that may occur will
 * be stored there.
 *
 * \param error A pointer to an empty DBusError where any
 * possible D-Bus error will be stored.
 * \return \c true on success and \c false if there was an error
 * or this connection manager is already registered.
 * \sa isRegistered()
 */
bool BaseConnectionManager::registerObject(DBusError *error)
{
    if (isRegistered()) {
        return true;
    }

    QString busName = TP_QT_CONNECTION_MANAGER_BUS_NAME_BASE;
    busName.append(mPriv->name);
    QString objectPath = TP_QT_CONNECTION_MANAGER_OBJECT_PATH_BASE;
    objectPath.append(mPriv->name);
    DBusError _error;
    bool ret = registerObject(busName, objectPath, &_error);
    if (!ret && error) {
        error->set(_error.name(), _error.message());
    }
    return ret;
}

/**
 * Reimplemented from DBusService.
 */
bool BaseConnectionManager::registerObject(const QString &busName, const QString &objectPath,
        DBusError *error)
{
    if (isRegistered()) {
        return true;
    }

    // register protocols
    foreach (const BaseProtocolPtr &protocol, protocols()) {
        QString escapedProtocolName = protocol->name();
        escapedProtocolName.replace(QLatin1Char('-'), QLatin1Char('_'));
        QString protoObjectPath = QString(
                QLatin1String("%1/%2")).arg(objectPath).arg(escapedProtocolName);
        debug() << "Registering protocol" << protocol->name() << "at path" << protoObjectPath <<
            "for CM" << objectPath << "at bus name" << busName;
        if (!protocol->registerObject(busName, protoObjectPath, error)) {
            return false;
        }
    }

    debug() << "Registering CM" << objectPath << "at bus name" << busName;
    // Only call DBusService::registerObject after registering the protocols as we don't want to
    // advertise isRegistered if some protocol cannot be registered
    if (!DBusService::registerObject(busName, objectPath, error)) {
        return false;
    }

    return true;
}

/**
 * Return a list of all connections that have currently been made.
 *
 * \return A list of all connections that have currently been made.
 */
QList<BaseConnectionPtr> BaseConnectionManager::connections() const
{
    return mPriv->connections.toList();
}

void BaseConnectionManager::addConnection(const BaseConnectionPtr &connection)
{
    Q_ASSERT(!mPriv->connections.contains(connection));
    mPriv->connections.insert(connection);
    connect(connection.data(),
            SIGNAL(disconnected()),
            SLOT(removeConnection()));
    emit newConnection(connection);
}

void BaseConnectionManager::removeConnection()
{
    BaseConnectionPtr connection = BaseConnectionPtr(
            qobject_cast<BaseConnection*>(sender()));
    Q_ASSERT(connection);
    Q_ASSERT(mPriv->connections.contains(connection));
    mPriv->connections.remove(connection);
}

/**
 * \fn void newConnection(const BaseConnectionPtr &connection)
 *
 * Emitted when a new connection has been requested by a client and
 * the connection object has been constructed.
 *
 * To handle the connection request before a connection has been created,
 * use BaseProtocol::setCreateConnectionCallback().
 *
 * \param connection The newly created connection
 */

}