/** * This file is part of TelepathyQt4 * * @copyright Copyright (C) 2008 Collabora Ltd. * @copyright Copyright (C) 2008 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 "TelepathyQt4/pending-channel-request-internal.h" #include "TelepathyQt4/_gen/pending-channel-request.moc.hpp" #include "TelepathyQt4/_gen/pending-channel-request-internal.moc.hpp" #include "TelepathyQt4/debug-internal.h" #include #include #include #include #include #include #include #include #include #include namespace Tp { struct TELEPATHY_QT4_NO_EXPORT PendingChannelRequest::Private { Private(const QDBusConnection &dbusConnection) : dbusConnection(dbusConnection), cancelOperation(0) { } QDBusConnection dbusConnection; ChannelRequestPtr channelRequest; PendingChannelRequestCancelOperation *cancelOperation; }; /** * \class PendingChannelRequest * \ingroup clientchannelrequest * \headerfile TelepathyQt4/pending-channel-request.h * * \brief The PendingChannelRequest class represents the parameters of and * the reply to an asynchronous ChannelRequest request. * * Instances of this class cannot be constructed directly; the only way to get * one is trough Account. * * See \ref async_model */ /** * Construct a new PendingChannelRequest object. * * \param account Account to use. * \param requestedProperties A dictionary containing the desirable properties. * \param userActionTime The time at which user action occurred, or QDateTime() * if this channel request is for some reason not * involving user action. * \param preferredHandler Either the well-known bus name (starting with * org.freedesktop.Telepathy.Client.) of the preferred * handler for this channel, or an empty string to * indicate that any handler would be acceptable. * \param create Whether createChannel or ensureChannel should be called. * \param account The account the request was made through. */ PendingChannelRequest::PendingChannelRequest(const AccountPtr &account, const QVariantMap &requestedProperties, const QDateTime &userActionTime, const QString &preferredHandler, bool create, const ChannelRequestHints &hints) : PendingOperation(account), mPriv(new Private(account->dbusConnection())) { QString channelDispatcherObjectPath = QString(QLatin1String("/%1")).arg(QLatin1String(TELEPATHY_INTERFACE_CHANNEL_DISPATCHER)); channelDispatcherObjectPath.replace(QLatin1String("."), QLatin1String("/")); Client::ChannelDispatcherInterface *channelDispatcherInterface = account->dispatcherInterface(); QDBusPendingCallWatcher *watcher = 0; if (create) { if (hints.isValid()) { if (account->supportsRequestHints()) { watcher = new QDBusPendingCallWatcher( channelDispatcherInterface->CreateChannelWithHints( QDBusObjectPath(account->objectPath()), requestedProperties, userActionTime.isNull() ? 0 : userActionTime.toTime_t(), preferredHandler, hints.allHints()), this); } else { warning() << "Hints passed to channel request won't have an effect" << "because the Channel Dispatcher service in use is too old"; } } if (!watcher) { watcher = new QDBusPendingCallWatcher( channelDispatcherInterface->CreateChannel( QDBusObjectPath(account->objectPath()), requestedProperties, userActionTime.isNull() ? 0 : userActionTime.toTime_t(), preferredHandler), this); } } else { if (hints.isValid()) { if (account->supportsRequestHints()) { watcher = new QDBusPendingCallWatcher( channelDispatcherInterface->EnsureChannelWithHints( QDBusObjectPath(account->objectPath()), requestedProperties, userActionTime.isNull() ? 0 : userActionTime.toTime_t(), preferredHandler, hints.allHints()), this); } else { warning() << "Hints passed to channel request won't have an effect" << "because the Channel Dispatcher service in use is too old"; } } if (!watcher) { watcher = new QDBusPendingCallWatcher( channelDispatcherInterface->EnsureChannel( QDBusObjectPath(account->objectPath()), requestedProperties, userActionTime.isNull() ? 0 : userActionTime.toTime_t(), preferredHandler), this); } } // TODO: This is a Qt bug fixed upstream, should be in the next Qt release. // We should not need to check watcher->isFinished() here, remove the // check when we depend on the fixed Qt version. if (watcher->isFinished()) { onWatcherFinished(watcher); } else { connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), SLOT(onWatcherFinished(QDBusPendingCallWatcher*))); } } /** * Construct a new PendingChannelRequest object that always fails. * * \param account Account to use. * \param errorName The name of a D-Bus error. * \param errorMessage The error message. */ PendingChannelRequest::PendingChannelRequest(const AccountPtr &account, const QString &errorName, const QString &errorMessage) : PendingOperation(ConnectionPtr()), mPriv(new Private(account->dbusConnection())) { setFinishedWithError(errorName, errorMessage); } /** * Class destructor. */ PendingChannelRequest::~PendingChannelRequest() { delete mPriv; } /** * Return the account through which the request was made. * * \return A pointer to the Account object. */ AccountPtr PendingChannelRequest::account() const { return AccountPtr(qobject_cast((Account*) _object().data())); } ChannelRequestPtr PendingChannelRequest::channelRequest() const { return mPriv->channelRequest; } PendingOperation *PendingChannelRequest::cancel() { if (isFinished()) { // CR has already succeeded or failed, so let's just fail here return new PendingFailure(QLatin1String(TELEPATHY_DBUS_ERROR_UNKNOWN_METHOD), QLatin1String("ChannnelRequest already finished"), _object()); } if (!mPriv->cancelOperation) { mPriv->cancelOperation = new PendingChannelRequestCancelOperation(); connect(mPriv->cancelOperation, SIGNAL(finished(Tp::PendingOperation*)), SLOT(onCancelOperationFinished(Tp::PendingOperation*))); if (mPriv->channelRequest) { mPriv->cancelOperation->go(mPriv->channelRequest); } } return mPriv->cancelOperation; } void PendingChannelRequest::onWatcherFinished(QDBusPendingCallWatcher *watcher) { QDBusPendingReply reply = *watcher; if (!reply.isError()) { QDBusObjectPath objectPath = reply.argumentAt<0>(); debug() << "Got reply to ChannelDispatcher.Ensure/CreateChannel " "- object path:" << objectPath.path(); if (!account().isNull()) { mPriv->channelRequest = ChannelRequest::create(account(), objectPath.path(), QVariantMap()); } if (mPriv->cancelOperation) { mPriv->cancelOperation->go(mPriv->channelRequest); } else { emit channelRequestCreated(mPriv->channelRequest); connect(mPriv->channelRequest.data(), SIGNAL(failed(QString,QString)), SLOT(setFinishedWithError(QString,QString))); connect(mPriv->channelRequest.data(), SIGNAL(succeeded(Tp::ChannelPtr)), SLOT(setFinished())); connect(mPriv->channelRequest->proceed(), SIGNAL(finished(Tp::PendingOperation*)), SLOT(onProceedOperationFinished(Tp::PendingOperation*))); } } else { debug().nospace() << "Ensure/CreateChannel failed:" << reply.error().name() << ": " << reply.error().message(); setFinishedWithError(reply.error()); } watcher->deleteLater(); } void PendingChannelRequest::onProceedOperationFinished(PendingOperation *op) { if (op->isError()) { setFinishedWithError(op->errorName(), op->errorMessage()); } } void PendingChannelRequest::onCancelOperationFinished(PendingOperation *op) { mPriv->cancelOperation = 0; if (!isFinished()) { setFinishedWithError(QLatin1String(TELEPATHY_ERROR_CANCELLED), QLatin1String("ChannelRequest cancelled")); } } } // Tp