/**
* This file is part of TelepathyQt
*
* @copyright Copyright (C) 2012 Collabora Ltd.
* @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
#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
#include
#include
#include
#include
#include
#include
#include
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 protocols;
QSet 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 ¶m, 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 protocols = mCM->protocols();
foreach (const BaseProtocolPtr &protocol, protocols) {
ret << protocol->name();
}
context->setFinished(ret);
}
void BaseConnectionManager::Adaptee::requestConnection(const QString &protocolName,
const QVariantMap ¶meters,
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
*
* \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 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 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(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
*/
}