<commit_before>/*
 * This file is part of TelepathyQt
 *
 * Copyright (C) 2010 Collabora Ltd. <http://www.collabora.co.uk/>
 *
 * 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/DBusTubeChannel>

#include "TelepathyQt/_gen/dbus-tube-channel.moc.hpp"

#include "TelepathyQt/debug-internal.h"
#include "TelepathyQt/outgoing-stream-tube-channel-internal.h"

#include <TelepathyQt/Connection>
#include <TelepathyQt/ContactManager>

namespace Tp
{

struct TP_QT_NO_EXPORT DBusTubeChannel::Private
{
    Private(DBusTubeChannel *parent);
    virtual ~Private();

    void extractProperties(const QVariantMap &props);
    void extractParticipants(const Tp::DBusTubeParticipants &participants);

    static void introspectDBusTube(Private *self);
    static void introspectBusNamesMonitoring(Private *self);

    ReadinessHelper *readinessHelper;

    // Public object
    DBusTubeChannel *parent;

    // Properties
    UIntList accessControls;
    QString serviceName;
    QHash<ContactPtr, QString> busNames;
    QString address;

    QHash<QUuid, QString> pendingNewBusNamesToAdd;
    QList<QUuid> pendingNewBusNamesToRemove;

    QueuedContactFactory *queuedContactFactory;
};

DBusTubeChannel::Private::Private(DBusTubeChannel *parent)
        : parent(parent),
          queuedContactFactory(new QueuedContactFactory(parent->connection()->contactManager(), parent))
{
    // Initialize readinessHelper + introspectables here
    readinessHelper = parent->readinessHelper();

    ReadinessHelper::Introspectables introspectables;

    ReadinessHelper::Introspectable introspectableDBusTube(
        QSet<uint>() << 0,                                                      // makesSenseForStatuses
        Features() << TubeChannel::FeatureCore,                                 // dependsOnFeatures (core)
        QStringList(),                                                          // dependsOnInterfaces
        (ReadinessHelper::IntrospectFunc) &Private::introspectDBusTube,
        this);
    introspectables[DBusTubeChannel::FeatureDBusTube] = introspectableDBusTube;

    ReadinessHelper::Introspectable introspectableBusNamesMonitoring(
        QSet<uint>() << 0,                                                      // makesSenseForStatuses
        Features() << DBusTubeChannel::FeatureDBusTube,                         // dependsOnFeatures (core)
        QStringList(),                                                          // dependsOnInterfaces
        (ReadinessHelper::IntrospectFunc) &Private::introspectBusNamesMonitoring,
        this);
    introspectables[DBusTubeChannel::FeatureBusNameMonitoring] = introspectableBusNamesMonitoring;

    readinessHelper->addIntrospectables(introspectables);
}

DBusTubeChannel::Private::~Private()
{
}

void DBusTubeChannel::Private::extractProperties(const QVariantMap &props)
{
    serviceName = qdbus_cast<QString>(props[QLatin1String("Service")]);
    accessControls = qdbus_cast<UIntList>(props[QLatin1String("SupportedAccessControls")]);
    extractParticipants(qdbus_cast<DBusTubeParticipants>(props[QLatin1String("DBusNames")]));
}

void DBusTubeChannel::Private::extractParticipants(const Tp::DBusTubeParticipants &participants)
{
    busNames.clear();
    for (DBusTubeParticipants::const_iterator i = participants.constBegin();
         i != participants.constEnd();
         ++i) {
        QUuid uuid = queuedContactFactory->appendNewRequest(UIntList() << i.key());
        pendingNewBusNamesToAdd.insert(uuid, i.value());
    }
}

void DBusTubeChannel::Private::introspectBusNamesMonitoring(DBusTubeChannel::Private *self)
{
    DBusTubeChannel *parent = self->parent;

    Client::ChannelTypeDBusTubeInterface *dbusTubeInterface =
            parent->interface<Client::ChannelTypeDBusTubeInterface>();

    // It must be present
    Q_ASSERT(dbusTubeInterface);

    // It makes sense only if this is a room, if that's not the case just spit a warning
    if (parent->targetHandleType() == static_cast<uint>(Tp::HandleTypeRoom)) {
        parent->connect(dbusTubeInterface, SIGNAL(DBusNamesChanged(Tp::DBusTubeParticipants,Tp::UIntList)),
                        parent, SLOT(onDBusNamesChanged(Tp::DBusTubeParticipants,Tp::UIntList)));
    } else {
        debug() << "FeatureBusNameMonitoring does not make sense in a P2P context";
    }

    self->readinessHelper->setIntrospectCompleted(DBusTubeChannel::FeatureBusNameMonitoring, true);
}

void DBusTubeChannel::Private::introspectDBusTube(DBusTubeChannel::Private *self)
{
    DBusTubeChannel *parent = self->parent;

    debug() << "Introspect dbus tube properties";

    Client::DBus::PropertiesInterface *properties =
            parent->interface<Client::DBus::PropertiesInterface>();

    QDBusPendingCallWatcher *watcher =
        new QDBusPendingCallWatcher(
                properties->GetAll(
                    QLatin1String(TP_QT_IFACE_CHANNEL_TYPE_DBUS_TUBE)),
                parent);
    parent->connect(watcher,
            SIGNAL(finished(QDBusPendingCallWatcher*)),
            parent,
            SLOT(gotDBusTubeProperties(QDBusPendingCallWatcher*)));
}

/**
 * \class DBusTubeChannel
 * \headerfile TelepathyQt/stream-tube-channel.h <TelepathyQt/DBusTubeChannel>
 *
 * A class representing a DBus Tube
 *
 * \c DBusTubeChannel is an high level wrapper for managing Telepathy interface
 * #TELEPATHY_INTERFACE_CHANNEL_TYPE_DBUS_TUBE.
 * It provides a private DBus connection, either peer-to-peer or between a group of contacts, which
 * can be used just like a standard DBus connection. As such, services exposed MUST adhere to the
 * DBus specification.
 *
 * This class provides high level methods for managing both incoming and outgoing tubes - however,
 * you probably want to use one of its subclasses, #OutgoingDBusTubeChannel or
 * #IncomingDBusTubeChannel, which both provide higher level methods for accepting
 * or offering tubes.
 *
 * For more details, please refer to Telepathy spec.
 */

// Features declaration and documentation
/**
 * Feature representing the core that needs to become ready to make the
 * DBusTubeChannel object usable.
 *
 * Note that this feature must be enabled in order to use most
 * DBusTubeChannel methods.
 * See specific methods documentation for more details.
 */
const Feature DBusTubeChannel::FeatureDBusTube = Feature(QLatin1String(DBusTubeChannel::staticMetaObject.className()), 0);
/**
 * Feature used in order to monitor connections to this tube.
 * Please note that this feature makes sense only in Group tubes.
 *
 * %busNamesChanged will be emitted when the participants of this tube change
 */
const Feature DBusTubeChannel::FeatureBusNameMonitoring = Feature(QLatin1String(DBusTubeChannel::staticMetaObject.className()), 1);

/**
 * Create a new DBusTubeChannel channel.
 *
 * \param connection Connection owning this channel, and specifying the
 *                   service.
 * \param objectPath The object path of this channel.
 * \param immutableProperties The immutable properties of this channel.
 * \return A DBusTubeChannelPtr object pointing to the newly created
 *         DBusTubeChannel object.
 */
DBusTubeChannelPtr DBusTubeChannel::create(const ConnectionPtr &connection,
        const QString &objectPath, const QVariantMap &immutableProperties)
{
    return DBusTubeChannelPtr(new DBusTubeChannel(connection, objectPath,
                immutableProperties));
}

/**
 * Construct a new DBusTubeChannel object.
 *
 * \param connection Connection owning this channel, and specifying the
 *                   service.
 * \param objectPath The object path of this channel.
 * \param immutableProperties The immutable properties of this channel.
 */
DBusTubeChannel::DBusTubeChannel(const ConnectionPtr &connection,
        const QString &objectPath,
        const QVariantMap &immutableProperties)
    : TubeChannel(connection, objectPath, immutableProperties),
      mPriv(new Private(this))
{
    connect(mPriv->queuedContactFactory,
            SIGNAL(contactsRetrieved(QUuid,QList<Tp::ContactPtr>)),
            this,
            SLOT(onContactsRetrieved(QUuid,QList<Tp::ContactPtr>)));
}

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

/**
 * Returns the service name which will be used over the tube.
 *
 * This method requires DBusTubeChannel::FeatureDBusTube to be enabled.
 *
 * \return the service name that will be used over the tube
 */
QString DBusTubeChannel::serviceName() const
{
    if (!isReady(FeatureDBusTube)) {
        warning() << "DBusTubeChannel::service() used with "
            "FeatureDBusTube not ready";
        return QString();
    }

    return mPriv->serviceName;
}

/**
 * Checks if this tube is capable to accept or offer a private bus which
 * will require credentials upon connection.
 *
 * When this capability is available and enabled, the connecting process must send a byte when
 * it first connects, which is not considered to be part of the data stream.
 * If the operating system uses sendmsg() with SCM_CREDS or SCM_CREDENTIALS to pass
 * credentials over sockets, the connecting process must do so if possible;
 * if not, it must still send the byte.
 *
 * The listening process will disconnect the connection unless it can determine
 * by OS-specific means that the connecting process has the same user ID as the listening process.
 *
 * This method requires DBusTubeChannel::FeatureDBusTube to be enabled.
 *
 * \note It is strongly advised to call this method before attempting to call
 *       #IncomingDBusTubeChannel::acceptTube or
 *       #OutgoingDBusTubeChannel::offerTube requiring
 *       credentials to prevent failures, as the spec implies
 *       this feature is not compulsory for connection managers.
 *
 * \return Whether this DBus tube is capable to accept or offer a private bus
 *         requiring credentials for connecting to it.
 *
 * \sa IncomingDBusTubeChannel::acceptTube
 * \sa OutgoingDBusTubeChannel::offerTube
 */
bool DBusTubeChannel::supportsCredentials() const
{
    if (!isReady(FeatureDBusTube)) {
        warning() << "DBusTubeChannel::supportsCredentials() used with "
            "FeatureDBusTube not ready";
        return false;
    }

    return mPriv->accessControls.contains(static_cast<uint>(Tp::SocketAccessControlCredentials));
}

/**
 * If the tube has been opened, this function returns the private bus address you should be connecting
 * to for using this tube.
 *
 * Please note this function will return a meaningful value only if the tube has already
 * been opened successfully: in case of failure or the tube being still pending, an empty QString will be
 * returned.
 *
 * \returns The address of the private bus opened by this tube
 */
QString DBusTubeChannel::address() const
{
    if (state() != TubeChannelStateOpen) {
        warning() << "DBusTubeChannel::address() can be called only if "
            "the tube has already been opened";
        return QString();
    }

    return mPriv->address;
}

/**
 * This function returns all the known active connections since FeatureBusNameMonitoring has
 * been enabled. For this method to return all known connections, you need to make
 * FeatureBusNameMonitoring ready before accepting or offering the tube.
 *
 * This function will always return an empty hash in case the tube is p2p, even if
 * FeatureBusNameMonitoring has been activated.
 *
 * \returns A list of active connection ids known to this tube
 */
QHash<ContactPtr, QString> DBusTubeChannel::busNames() const
{
    if (!isReady(FeatureBusNameMonitoring)) {
        warning() << "DBusTubeChannel::busNames() used with "
            "FeatureBusNameMonitoring not ready";
        return QHash<ContactPtr, QString>();
    }

    return mPriv->busNames;
}

void DBusTubeChannel::gotDBusTubeProperties(QDBusPendingCallWatcher *watcher)
{
    QDBusPendingReply<QVariantMap> reply = *watcher;

    if (!reply.isError()) {
        QVariantMap props = reply.value();
        debug() << "Got reply to Properties::GetAll(DBusTubeChannel)";
        mPriv->extractProperties(props);
        mPriv->readinessHelper->setIntrospectCompleted(DBusTubeChannel::FeatureDBusTube, true);
    } else {
        warning().nospace() << "Properties::GetAll(DBusTubeChannel) failed "
            "with " << reply.error().name() << ": " << reply.error().message();
        mPriv->readinessHelper->setIntrospectCompleted(DBusTubeChannel::FeatureDBusTube, false,
                reply.error());
    }
}

void DBusTubeChannel::onDBusNamesChanged(const Tp::DBusTubeParticipants &added,
        const Tp::UIntList &removed)
{
    for (DBusTubeParticipants::const_iterator i = added.constBegin();
         i != added.constEnd();
         ++i) {
        QUuid uuid = mPriv->queuedContactFactory->appendNewRequest(UIntList() << i.key());
        // Add it to our hash as well
        mPriv->pendingNewBusNamesToAdd.insert(uuid, i.value());
    }

    foreach (uint handle, removed) {
        QUuid uuid = mPriv->queuedContactFactory->appendNewRequest(UIntList() << handle);
        // Add it to pending removed as well
        mPriv->pendingNewBusNamesToRemove << uuid;
    }
}

void DBusTubeChannel::onContactsRetrieved(const QUuid &uuid, const QList<ContactPtr> &contacts)
{
    // Retrieve our hash
    if (mPriv->pendingNewBusNamesToAdd.contains(uuid)) {
        QString busName = mPriv->pendingNewBusNamesToAdd.take(uuid);
        QHash<ContactPtr, QString> added;

        // Add it to our connections hash
        foreach (const Tp::ContactPtr &contact, contacts) {
            mPriv->busNames.insert(contact, busName);
            added.insert(contact, busName);
        }

        // Time for us to emit the signal
        emit busNamesChanged(added, QList<ContactPtr>());
    } else if (mPriv->pendingNewBusNamesToRemove.contains(uuid)) {
        mPriv->pendingNewBusNamesToRemove.removeOne(uuid);
        QList<ContactPtr> removed;

        // Remove it from our connections hash
        foreach (const Tp::ContactPtr &contact, contacts) {
            mPriv->busNames.remove(contact);
            removed << contact;
        }

        // Time for us to emit the signal
        emit busNamesChanged(QHash<ContactPtr, QString>(), removed);
    } else {
        warning() << "Contacts retrieved but no pending bus names were found";
        return;
    }
}

void DBusTubeChannel::setAddress(const QString& address)
{
    mPriv->address = address;
}

// Signals documentation
/**
 * \fn void DBusTubeChannel::busNamesChanged(const QHash< ContactPtr, QString > &added, const QList< ContactPtr > &removed)
 *
 * Emitted when the participants of this tube change.
 *
 * This signal will be emitted only if the tube is a group tube (not p2p), and if the
 * FeatureBusNameMonitoring feature has been enabled. You usually want to wait until the
 * aforementioned feature is ready before connecting to this signal.
 *
 * \param added An hash containing the contacts who joined this tube, with their respective bus name.
 * \param removed A list containing the contacts who left this tube.
 */

}
<commit_msg>dbus-tubes: Don't emit a signal if the service is misbehaving; instead, print out a warning.<commit_after>/*
 * This file is part of TelepathyQt
 *
 * Copyright (C) 2010 Collabora Ltd. <http://www.collabora.co.uk/>
 *
 * 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/DBusTubeChannel>

#include "TelepathyQt/_gen/dbus-tube-channel.moc.hpp"

#include "TelepathyQt/debug-internal.h"
#include "TelepathyQt/outgoing-stream-tube-channel-internal.h"

#include <TelepathyQt/Connection>
#include <TelepathyQt/ContactManager>

namespace Tp
{

struct TP_QT_NO_EXPORT DBusTubeChannel::Private
{
    Private(DBusTubeChannel *parent);
    virtual ~Private();

    void extractProperties(const QVariantMap &props);
    void extractParticipants(const Tp::DBusTubeParticipants &participants);

    static void introspectDBusTube(Private *self);
    static void introspectBusNamesMonitoring(Private *self);

    ReadinessHelper *readinessHelper;

    // Public object
    DBusTubeChannel *parent;

    // Properties
    UIntList accessControls;
    QString serviceName;
    QHash<ContactPtr, QString> busNames;
    QString address;

    QHash<QUuid, QString> pendingNewBusNamesToAdd;
    QList<QUuid> pendingNewBusNamesToRemove;

    QueuedContactFactory *queuedContactFactory;
};

DBusTubeChannel::Private::Private(DBusTubeChannel *parent)
        : parent(parent),
          queuedContactFactory(new QueuedContactFactory(parent->connection()->contactManager(), parent))
{
    // Initialize readinessHelper + introspectables here
    readinessHelper = parent->readinessHelper();

    ReadinessHelper::Introspectables introspectables;

    ReadinessHelper::Introspectable introspectableDBusTube(
        QSet<uint>() << 0,                                                      // makesSenseForStatuses
        Features() << TubeChannel::FeatureCore,                                 // dependsOnFeatures (core)
        QStringList(),                                                          // dependsOnInterfaces
        (ReadinessHelper::IntrospectFunc) &Private::introspectDBusTube,
        this);
    introspectables[DBusTubeChannel::FeatureDBusTube] = introspectableDBusTube;

    ReadinessHelper::Introspectable introspectableBusNamesMonitoring(
        QSet<uint>() << 0,                                                      // makesSenseForStatuses
        Features() << DBusTubeChannel::FeatureDBusTube,                         // dependsOnFeatures (core)
        QStringList(),                                                          // dependsOnInterfaces
        (ReadinessHelper::IntrospectFunc) &Private::introspectBusNamesMonitoring,
        this);
    introspectables[DBusTubeChannel::FeatureBusNameMonitoring] = introspectableBusNamesMonitoring;

    readinessHelper->addIntrospectables(introspectables);
}

DBusTubeChannel::Private::~Private()
{
}

void DBusTubeChannel::Private::extractProperties(const QVariantMap &props)
{
    serviceName = qdbus_cast<QString>(props[QLatin1String("Service")]);
    accessControls = qdbus_cast<UIntList>(props[QLatin1String("SupportedAccessControls")]);
    extractParticipants(qdbus_cast<DBusTubeParticipants>(props[QLatin1String("DBusNames")]));
}

void DBusTubeChannel::Private::extractParticipants(const Tp::DBusTubeParticipants &participants)
{
    busNames.clear();
    for (DBusTubeParticipants::const_iterator i = participants.constBegin();
         i != participants.constEnd();
         ++i) {
        QUuid uuid = queuedContactFactory->appendNewRequest(UIntList() << i.key());
        pendingNewBusNamesToAdd.insert(uuid, i.value());
    }
}

void DBusTubeChannel::Private::introspectBusNamesMonitoring(DBusTubeChannel::Private *self)
{
    DBusTubeChannel *parent = self->parent;

    Client::ChannelTypeDBusTubeInterface *dbusTubeInterface =
            parent->interface<Client::ChannelTypeDBusTubeInterface>();

    // It must be present
    Q_ASSERT(dbusTubeInterface);

    // It makes sense only if this is a room, if that's not the case just spit a warning
    if (parent->targetHandleType() == static_cast<uint>(Tp::HandleTypeRoom)) {
        parent->connect(dbusTubeInterface, SIGNAL(DBusNamesChanged(Tp::DBusTubeParticipants,Tp::UIntList)),
                        parent, SLOT(onDBusNamesChanged(Tp::DBusTubeParticipants,Tp::UIntList)));
    } else {
        debug() << "FeatureBusNameMonitoring does not make sense in a P2P context";
    }

    self->readinessHelper->setIntrospectCompleted(DBusTubeChannel::FeatureBusNameMonitoring, true);
}

void DBusTubeChannel::Private::introspectDBusTube(DBusTubeChannel::Private *self)
{
    DBusTubeChannel *parent = self->parent;

    debug() << "Introspect dbus tube properties";

    Client::DBus::PropertiesInterface *properties =
            parent->interface<Client::DBus::PropertiesInterface>();

    QDBusPendingCallWatcher *watcher =
        new QDBusPendingCallWatcher(
                properties->GetAll(
                    QLatin1String(TP_QT_IFACE_CHANNEL_TYPE_DBUS_TUBE)),
                parent);
    parent->connect(watcher,
            SIGNAL(finished(QDBusPendingCallWatcher*)),
            parent,
            SLOT(gotDBusTubeProperties(QDBusPendingCallWatcher*)));
}

/**
 * \class DBusTubeChannel
 * \headerfile TelepathyQt/stream-tube-channel.h <TelepathyQt/DBusTubeChannel>
 *
 * A class representing a DBus Tube
 *
 * \c DBusTubeChannel is an high level wrapper for managing Telepathy interface
 * #TELEPATHY_INTERFACE_CHANNEL_TYPE_DBUS_TUBE.
 * It provides a private DBus connection, either peer-to-peer or between a group of contacts, which
 * can be used just like a standard DBus connection. As such, services exposed MUST adhere to the
 * DBus specification.
 *
 * This class provides high level methods for managing both incoming and outgoing tubes - however,
 * you probably want to use one of its subclasses, #OutgoingDBusTubeChannel or
 * #IncomingDBusTubeChannel, which both provide higher level methods for accepting
 * or offering tubes.
 *
 * For more details, please refer to Telepathy spec.
 */

// Features declaration and documentation
/**
 * Feature representing the core that needs to become ready to make the
 * DBusTubeChannel object usable.
 *
 * Note that this feature must be enabled in order to use most
 * DBusTubeChannel methods.
 * See specific methods documentation for more details.
 */
const Feature DBusTubeChannel::FeatureDBusTube = Feature(QLatin1String(DBusTubeChannel::staticMetaObject.className()), 0);
/**
 * Feature used in order to monitor connections to this tube.
 * Please note that this feature makes sense only in Group tubes.
 *
 * %busNamesChanged will be emitted when the participants of this tube change
 */
const Feature DBusTubeChannel::FeatureBusNameMonitoring = Feature(QLatin1String(DBusTubeChannel::staticMetaObject.className()), 1);

/**
 * Create a new DBusTubeChannel channel.
 *
 * \param connection Connection owning this channel, and specifying the
 *                   service.
 * \param objectPath The object path of this channel.
 * \param immutableProperties The immutable properties of this channel.
 * \return A DBusTubeChannelPtr object pointing to the newly created
 *         DBusTubeChannel object.
 */
DBusTubeChannelPtr DBusTubeChannel::create(const ConnectionPtr &connection,
        const QString &objectPath, const QVariantMap &immutableProperties)
{
    return DBusTubeChannelPtr(new DBusTubeChannel(connection, objectPath,
                immutableProperties));
}

/**
 * Construct a new DBusTubeChannel object.
 *
 * \param connection Connection owning this channel, and specifying the
 *                   service.
 * \param objectPath The object path of this channel.
 * \param immutableProperties The immutable properties of this channel.
 */
DBusTubeChannel::DBusTubeChannel(const ConnectionPtr &connection,
        const QString &objectPath,
        const QVariantMap &immutableProperties)
    : TubeChannel(connection, objectPath, immutableProperties),
      mPriv(new Private(this))
{
    connect(mPriv->queuedContactFactory,
            SIGNAL(contactsRetrieved(QUuid,QList<Tp::ContactPtr>)),
            this,
            SLOT(onContactsRetrieved(QUuid,QList<Tp::ContactPtr>)));
}

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

/**
 * Returns the service name which will be used over the tube.
 *
 * This method requires DBusTubeChannel::FeatureDBusTube to be enabled.
 *
 * \return the service name that will be used over the tube
 */
QString DBusTubeChannel::serviceName() const
{
    if (!isReady(FeatureDBusTube)) {
        warning() << "DBusTubeChannel::service() used with "
            "FeatureDBusTube not ready";
        return QString();
    }

    return mPriv->serviceName;
}

/**
 * Checks if this tube is capable to accept or offer a private bus which
 * will require credentials upon connection.
 *
 * When this capability is available and enabled, the connecting process must send a byte when
 * it first connects, which is not considered to be part of the data stream.
 * If the operating system uses sendmsg() with SCM_CREDS or SCM_CREDENTIALS to pass
 * credentials over sockets, the connecting process must do so if possible;
 * if not, it must still send the byte.
 *
 * The listening process will disconnect the connection unless it can determine
 * by OS-specific means that the connecting process has the same user ID as the listening process.
 *
 * This method requires DBusTubeChannel::FeatureDBusTube to be enabled.
 *
 * \note It is strongly advised to call this method before attempting to call
 *       #IncomingDBusTubeChannel::acceptTube or
 *       #OutgoingDBusTubeChannel::offerTube requiring
 *       credentials to prevent failures, as the spec implies
 *       this feature is not compulsory for connection managers.
 *
 * \return Whether this DBus tube is capable to accept or offer a private bus
 *         requiring credentials for connecting to it.
 *
 * \sa IncomingDBusTubeChannel::acceptTube
 * \sa OutgoingDBusTubeChannel::offerTube
 */
bool DBusTubeChannel::supportsCredentials() const
{
    if (!isReady(FeatureDBusTube)) {
        warning() << "DBusTubeChannel::supportsCredentials() used with "
            "FeatureDBusTube not ready";
        return false;
    }

    return mPriv->accessControls.contains(static_cast<uint>(Tp::SocketAccessControlCredentials));
}

/**
 * If the tube has been opened, this function returns the private bus address you should be connecting
 * to for using this tube.
 *
 * Please note this function will return a meaningful value only if the tube has already
 * been opened successfully: in case of failure or the tube being still pending, an empty QString will be
 * returned.
 *
 * \returns The address of the private bus opened by this tube
 */
QString DBusTubeChannel::address() const
{
    if (state() != TubeChannelStateOpen) {
        warning() << "DBusTubeChannel::address() can be called only if "
            "the tube has already been opened";
        return QString();
    }

    return mPriv->address;
}

/**
 * This function returns all the known active connections since FeatureBusNameMonitoring has
 * been enabled. For this method to return all known connections, you need to make
 * FeatureBusNameMonitoring ready before accepting or offering the tube.
 *
 * This function will always return an empty hash in case the tube is p2p, even if
 * FeatureBusNameMonitoring has been activated.
 *
 * \returns A list of active connection ids known to this tube
 */
QHash<ContactPtr, QString> DBusTubeChannel::busNames() const
{
    if (!isReady(FeatureBusNameMonitoring)) {
        warning() << "DBusTubeChannel::busNames() used with "
            "FeatureBusNameMonitoring not ready";
        return QHash<ContactPtr, QString>();
    }

    return mPriv->busNames;
}

void DBusTubeChannel::gotDBusTubeProperties(QDBusPendingCallWatcher *watcher)
{
    QDBusPendingReply<QVariantMap> reply = *watcher;

    if (!reply.isError()) {
        QVariantMap props = reply.value();
        debug() << "Got reply to Properties::GetAll(DBusTubeChannel)";
        mPriv->extractProperties(props);
        mPriv->readinessHelper->setIntrospectCompleted(DBusTubeChannel::FeatureDBusTube, true);
    } else {
        warning().nospace() << "Properties::GetAll(DBusTubeChannel) failed "
            "with " << reply.error().name() << ": " << reply.error().message();
        mPriv->readinessHelper->setIntrospectCompleted(DBusTubeChannel::FeatureDBusTube, false,
                reply.error());
    }
}

void DBusTubeChannel::onDBusNamesChanged(const Tp::DBusTubeParticipants &added,
        const Tp::UIntList &removed)
{
    for (DBusTubeParticipants::const_iterator i = added.constBegin();
         i != added.constEnd();
         ++i) {
        QUuid uuid = mPriv->queuedContactFactory->appendNewRequest(UIntList() << i.key());
        // Add it to our hash as well
        mPriv->pendingNewBusNamesToAdd.insert(uuid, i.value());
    }

    foreach (uint handle, removed) {
        QUuid uuid = mPriv->queuedContactFactory->appendNewRequest(UIntList() << handle);
        // Add it to pending removed as well
        mPriv->pendingNewBusNamesToRemove << uuid;
    }
}

void DBusTubeChannel::onContactsRetrieved(const QUuid &uuid, const QList<ContactPtr> &contacts)
{
    // Retrieve our hash
    if (mPriv->pendingNewBusNamesToAdd.contains(uuid)) {
        QString busName = mPriv->pendingNewBusNamesToAdd.take(uuid);
        QHash<ContactPtr, QString> added;

        // Add it to our connections hash
        foreach (const Tp::ContactPtr &contact, contacts) {
            mPriv->busNames.insert(contact, busName);
            added.insert(contact, busName);
        }

        // Time for us to emit the signal
        emit busNamesChanged(added, QList<ContactPtr>());
    } else if (mPriv->pendingNewBusNamesToRemove.contains(uuid)) {
        mPriv->pendingNewBusNamesToRemove.removeOne(uuid);
        QList<ContactPtr> removed;

        // Remove it from our connections hash
        foreach (const Tp::ContactPtr &contact, contacts) {
            if (mPriv->busNames.contains(contact)) {
                mPriv->busNames.remove(contact);
                removed << contact;
            } else {
                warning() << "Trying to remove a bus name which has not been retrieved previously!";
            }
        }

        // Time for us to emit the signal
        emit busNamesChanged(QHash<ContactPtr, QString>(), removed);
    } else {
        warning() << "Contacts retrieved but no pending bus names were found";
        return;
    }
}

void DBusTubeChannel::setAddress(const QString& address)
{
    mPriv->address = address;
}

// Signals documentation
/**
 * \fn void DBusTubeChannel::busNamesChanged(const QHash< ContactPtr, QString > &added, const QList< ContactPtr > &removed)
 *
 * Emitted when the participants of this tube change.
 *
 * This signal will be emitted only if the tube is a group tube (not p2p), and if the
 * FeatureBusNameMonitoring feature has been enabled. You usually want to wait until the
 * aforementioned feature is ready before connecting to this signal.
 *
 * \param added An hash containing the contacts who joined this tube, with their respective bus name.
 * \param removed A list containing the contacts who left this tube.
 */

}
<|endoftext|>


<commit_before>/*
*
* XMLFileReporter.cpp
*
* Copyright 2000 Lotus Development Corporation. All rights reserved.
* This software is subject to the Lotus Software Agreement, Restricted
* Rights for U.S. government users and applicable export regulations.
*/

#include <stdlib.h>
#include "XMLFileReporter.hpp"

#include "PlatformSupport/XalanUnicode.hpp"

const XalanDOMString  XMLFileReporter::OPT_FILENAME("filename");
const XalanDOMString  XMLFileReporter::ELEM_RESULTSFILE("resultsfile");
const XalanDOMString  XMLFileReporter::ELEM_TESTFILE("testfile");
const XalanDOMString  XMLFileReporter::ELEM_FILERESULT("fileresult");
const XalanDOMString  XMLFileReporter::ELEM_TESTCASE("testcase");
const XalanDOMString  XMLFileReporter::ELEM_CASERESULT("caseresult");
const XalanDOMString  XMLFileReporter::ELEM_CHECKRESULT("checkresult");
const XalanDOMString  XMLFileReporter::ELEM_STATISTIC("statistic");
const XalanDOMString  XMLFileReporter::ELEM_LONGVAL("longval");
const XalanDOMString  XMLFileReporter::ELEM_DOUBLEVAL("doubleval");
const XalanDOMString  XMLFileReporter::ELEM_MESSAGE("message");
const XalanDOMString  XMLFileReporter::ELEM_ARBITRARY("arbitrary");
const XalanDOMString  XMLFileReporter::ELEM_HASHTABLE("hashtable");
const XalanDOMString  XMLFileReporter::ELEM_HASHITEM("hashitem");
const XalanDOMString  XMLFileReporter::ATTR_LEVEL("level");
const XalanDOMString  XMLFileReporter::ATTR_DESC("desc");
const XalanDOMString  XMLFileReporter::ATTR_TIME("time");
const XalanDOMString  XMLFileReporter::ATTR_RESULT("result");
const XalanDOMString  XMLFileReporter::ATTR_KEY("key");
const XalanDOMString  XMLFileReporter::ATTR_FILENAME = XMLFileReporter::OPT_FILENAME;

const XalanDOMString XMLFileReporter::TESTCASEINIT_HDR("<" + ELEM_TESTCASE + " " + ATTR_DESC + "=\"");
const XalanDOMString XMLFileReporter::TESTCASECLOSE_HDR("<" + ELEM_CASERESULT + " " + ATTR_DESC + "=\"");
const XalanDOMString XMLFileReporter::MESSAGE_HDR("<" + ELEM_MESSAGE + " " + ATTR_LEVEL + "=\"");
const XalanDOMString XMLFileReporter::STATISTIC_HDR("<" + ELEM_STATISTIC + " " + ATTR_LEVEL + "=\"");
const XalanDOMString XMLFileReporter::ARBITRARY_HDR("<" + ELEM_ARBITRARY + " " + ATTR_LEVEL + "=\"");
const XalanDOMString XMLFileReporter::HASHTABLE_HDR("<" + ELEM_HASHTABLE + " " + ATTR_LEVEL + "=\"");
const XalanDOMString XMLFileReporter::HASHITEM_HDR("  <" + ELEM_HASHITEM + " " + ATTR_KEY + "=\"");
const XalanDOMString XMLFileReporter::CHECKPASS_HDR("<" + ELEM_CHECKRESULT + " " + ATTR_RESULT + "=\"" + "PASS" + "\" " + ATTR_DESC + "=\"");
const XalanDOMString XMLFileReporter::CHECKAMBG_HDR("<" + ELEM_CHECKRESULT + " " + ATTR_RESULT + "=\"" + "AMBG" + "\" " + ATTR_DESC + "=\"");
const XalanDOMString XMLFileReporter::CHECKERRR_HDR("<" + ELEM_CHECKRESULT + " " + ATTR_RESULT + "=\"" + "ERRR" + "\" " + ATTR_DESC + "=\"");
const XalanDOMString XMLFileReporter::CHECKFAIL_HDR("<" + ELEM_CHECKRESULT + " " + ATTR_RESULT + "=\"" + "FAIL" + "\" " + ATTR_DESC + "=\"");

XMLFileReporter::XMLFileReporter():
	m_anyOutput(false),
	m_fileName(""),
	m_fileHandle(0),
	m_ready(false),
	m_error(false),
	m_flushOnCaseClose(true)
{
}

XMLFileReporter::XMLFileReporter(const XalanDOMString& fileName):
	m_anyOutput(false),
	m_fileName(fileName),
	m_fileHandle(0),
	m_ready(false),
	m_error(false),
	m_flushOnCaseClose(true)
{
    m_ready = initialize();
}

XMLFileReporter::XMLFileReporter(const char* fileName):
	m_anyOutput(false),
	m_fileName(XalanDOMString(fileName)),
	m_fileHandle(0),
	m_ready(false),
	m_error(false),
	m_flushOnCaseClose(true)
{
    m_ready = initialize();
}

bool
XMLFileReporter::initialize()
{
    if (length(m_fileName) == 0)
    {
        // We don't have a valid file, so bail
        m_error = true;
        m_ready = false;
        fprintf(stderr, "XMLFileReporter.initialize() ERROR: No file name specified");
        return(false);
    }

	// Transcode down the file name...
	const CharVectorType	theTranscodedFileName(m_fileName.transcode());
	const char* const		theTranscodedFileNamePointer = &theTranscodedFileName.front();

    // Create a file and ensure it has a place to live
	m_fileHandle = fopen(theTranscodedFileNamePointer, "w");
	if (m_fileHandle == 0)
	{
        // Couldn't create or find the directory for the file to live in, so bail
        m_error = true;
        m_ready = false;
        fprintf(stderr, "XMLFileReporter.initialize() ERROR: unble to open file, %s", theTranscodedFileNamePointer);
        return(false);
	}

    m_ready = true;
    startResultsFile();
    // fprintf(stderr, "DEBUG:XMLFileReporter.initialize() complete with " + fileName);

    return m_ready;
}

bool
XMLFileReporter::getFlushOnCaseClose()
{
    return(m_flushOnCaseClose);
}

const XalanDOMString&
XMLFileReporter::getFileName() const
{
    return(m_fileName);
}

void
XMLFileReporter::setFileName(const XalanDOMString& fileName)
{
	m_fileName = fileName;
}

bool
XMLFileReporter::checkError()
{
    // Ensure our underlying reporter, if one, is still OK
    if (m_fileHandle == 0)
    {
        m_error = true;
    }
    return(m_error);
}

bool
XMLFileReporter::isReady()
{
    // Ensure our underlying reporter, if one, is still OK
    if (m_fileHandle == 0)
    {
        // NEEDSWORK: should we set m_ready = false in this case?
        //            errors in the PrintStream are not necessarily fatal
        m_error = true;
        m_ready = false;
    }
    return(m_ready);
}

void
XMLFileReporter::flush()
{
    if (isReady())
    {
		fflush(m_fileHandle);
    }
}

void
XMLFileReporter::close()
{
    fflush(m_fileHandle);
    if (isReady())
    {
		if (m_fileHandle != 0)
		{
			closeResultsFile();
			fclose(m_fileHandle);
		}
    }
    m_ready = false;
}

void
XMLFileReporter::logTestFileInit(const XalanDOMString& msg)
{
    if (isReady())
    {
        printToFile("<" + ELEM_TESTFILE
                              + " " + ATTR_DESC + "=\"" + escapestring(msg) + "\" " + ATTR_TIME + "=\"" + getDateTimeString() + "\">");
    }
}

void
XMLFileReporter::logTestFileClose(const XalanDOMString& msg, const XalanDOMString& result)
{
    if (isReady())
    {
        printToFile("<" + ELEM_FILERESULT
                              + " " + ATTR_DESC + "=\"" + escapestring(msg) + "\" " + ATTR_RESULT + "=\"" + result + "\" " + ATTR_TIME + "=\"" + getDateTimeString() + "\"/>");
        printToFile("</" + ELEM_TESTFILE + ">");
    }
    flush();
}

void
XMLFileReporter::logTestCaseInit(const XalanDOMString& msg)
{
    if (isReady())
    {
        printToFile(TESTCASEINIT_HDR + escapestring(msg) + "\">");
    }
}

void
XMLFileReporter::logTestCaseClose(const XalanDOMString& msg, const XalanDOMString& result)
{
    if (isReady())
    {
        printToFile(TESTCASECLOSE_HDR + escapestring(msg) + "\" " + ATTR_RESULT + "=\"" + result + "\"/>");
        printToFile("</" + ELEM_TESTCASE + ">");
    }
    if (getFlushOnCaseClose())
    {
        flush();
    }
}

void
XMLFileReporter::logMessage(int level, const XalanDOMString& msg)
{
	char tmp[20];
	sprintf(tmp, "%d", level);

    if (isReady())
    {
        printToFile(MESSAGE_HDR + tmp + "\">");
        printToFile(escapestring(msg));
        printToFile("</" + ELEM_MESSAGE +">");
    }
}

void
XMLFileReporter::logElement(int level, const XalanDOMString& element, Hashtable attrs,const XalanDOMString& msg)
{
	if (isReady()
        && (element.empty() == 0)
        && (attrs.empty() == 0)
       )
    {
		char tmp[20];
		sprintf(tmp, "%d", level);
//
//		Took out this level attribute cuz we don't use it.
//      printToFile("<" + escapestring(element) + " " + ATTR_LEVEL + "=\""
//                      + tmp + "\"");
        printToFile("<" + escapestring(element) + " ");

		Hashtable::iterator theEnd = attrs.end();

       	for(Hashtable::iterator i = attrs.begin(); i != theEnd; ++i)
        {

            printToFile((*i).first + "=\""
                                  + (*i).second + "\"");
        }

        printToFile(XalanDOMString(">"));
        if (msg.empty() != 0)
            printToFile(escapestring(msg));
        printToFile("</" + escapestring(element) + ">");
    }
}

void
XMLFileReporter::logStatistic (int level, long lVal, double dVal, const XalanDOMString& msg)
{
 	char tmp[20];

	if (isReady())
    {
		sprintf(tmp, "%d", level);
        printToFile(STATISTIC_HDR + tmp + "\" " + ATTR_DESC + "=\"" + escapestring(msg) + "\">");

		sprintf(tmp, "%d", lVal);
		printToFile("<" + ELEM_LONGVAL + ">" + tmp + "</" + ELEM_LONGVAL + ">");

		sprintf(tmp, "%d", dVal);
        printToFile("<" + ELEM_DOUBLEVAL + ">" + tmp + "</" + ELEM_DOUBLEVAL + ">");

		printToFile("</" + ELEM_STATISTIC + ">");

    }
}

void
XMLFileReporter::logArbitraryMessage (int level, const XalanDOMString& msg)
{
	char tmp[20];
	sprintf(tmp, "%d", level);

    if (isReady())
    {
		printToFile(ARBITRARY_HDR + tmp + "\">");
        printToFile(escapestring(msg));
        printToFile("</" + ELEM_ARBITRARY +">");
    }
}

/*
void logHashtable (int level, Hashtable hash, XalanDOMString msg)
{
    if (isReady())
    {
        printToFile(HASHTABLE_HDR + level + "\" " + ATTR_DESC + "=\"" + msg + "\">");
        if (hash == null)
        {
            printToFile("<" + ELEM_HASHITEM + " " + ATTR_KEY + "=\"null\">");
            printToFile("</" + ELEM_HASHITEM + ">");
        }
        try
        {
            for (Enumeration enum = hash.keys(); enum.hasMoreElements();)
            {
                Object key = enum.nextElement();
                // Ensure we'll have clean output by pre-fetching value before outputting anything
                XalanDOMString value = hash.get(key).tostring();
                printToFile(HASHITEM_HDR + key.tostring() + "\">");
                printToFile(value);
                printToFile("</" + ELEM_HASHITEM + ">");
            }
        }
        catch (Exception e)
        {
            // No-op: should ensure we have clean output
        }
        printToFile("</" + ELEM_HASHTABLE +">");
    }
}
*/

void
XMLFileReporter::logCheckPass(const XalanDOMString& comment)
{
    if (isReady())
    {
        printToFile(CHECKPASS_HDR + escapestring(comment) + "\"/>");
    }
}

void
XMLFileReporter::logCheckAmbiguous(const XalanDOMString& comment)
{
    if (isReady())
    {
        printToFile(CHECKAMBG_HDR + escapestring(comment) + "\"/>");
    }
}

void
XMLFileReporter::logCheckFail(const XalanDOMString& comment)
{
    if (isReady())
    {
        printToFile(CHECKFAIL_HDR + escapestring(comment) + "\"/>");
    }
}

void
XMLFileReporter::logCheckErr(const XalanDOMString& comment)
{
    if (isReady())
    {
        printToFile(CHECKERRR_HDR + escapestring(comment) + "\"/>");
    }
}

static const XalanDOMChar	theLessThanString[] =
{
	XalanUnicode::charAmpersand,
	XalanUnicode::charLetter_l,
	XalanUnicode::charLetter_t,
	XalanUnicode::charSemicolon,
	0
};

static const XalanDOMChar	theGreaterThanString[] =
{
	XalanUnicode::charAmpersand,
	XalanUnicode::charLetter_g,
	XalanUnicode::charLetter_t,
	XalanUnicode::charSemicolon,
	0
};

XalanDOMString
XMLFileReporter::escapestring(const XalanDOMString&  s)
{
    XalanDOMString		sb;

    const unsigned int	length = s.length();

    for (unsigned int i = 0; i < length; i++)
    {
        const XalanDOMChar	ch = charAt(s, i);

        if (XalanUnicode::charLessThanSign == ch)
        {
			append(sb, theLessThanString);
        }
        else if (XalanUnicode::charGreaterThanSign == ch)
        {
			append(sb, theGreaterThanString);
        }
        // Note: Skipping escaping of UTF-16 surrogates and & ampersands, since
        //  I don't think we'll be outputting them or they won't affect our output
        else
        {
            append(sb, ch);
        }
    }

	return sb;
}

bool
XMLFileReporter::startResultsFile()
{
    if (isReady())
    {
        // Write out XML header and root test result element
        printToFile(XalanDOMString("<?xml version=\"1.0\"?>"));

        // Note: this tag is closed in our .close() method, which the caller had better call!
        printToFile("<" + ELEM_RESULTSFILE + " " + ATTR_FILENAME + "=\"" + m_fileName + "\">");

        return true;
    }
    else
	{
        return false;
	}
}

bool
XMLFileReporter::closeResultsFile()
{
    if (isReady())
    {
		printToFile("</" + ELEM_RESULTSFILE + ">");
        return true;
    }
    else
        return false;
}

bool
XMLFileReporter::printToFile(const XalanDOMString&	output)
{
    if (isReady())
    {
        fprintf(m_fileHandle, &output.transcode().front());
		fprintf(m_fileHandle, "\n");
        return true;
    }
    else
        return false;
}

XalanDOMString
XMLFileReporter::getDateTimeString()
{
	struct tm *tmNow;
	time_t time_tNow;

	time(&time_tNow);
	tmNow = localtime(&time_tNow);

	const char* const	theTime = asctime(tmNow);

	return XalanDOMString(theTime, strlen(theTime) - 1);
}
<commit_msg>Added addMetricToAttrs method<commit_after>/*
*
* XMLFileReporter.cpp
*
* Copyright 2000 Lotus Development Corporation. All rights reserved.
* This software is subject to the Lotus Software Agreement, Restricted
* Rights for U.S. government users and applicable export regulations.
*/

#include <stdlib.h>
#include "XMLFileReporter.hpp"

#include "PlatformSupport/XalanUnicode.hpp"

const XalanDOMString  XMLFileReporter::OPT_FILENAME("filename");
const XalanDOMString  XMLFileReporter::ELEM_RESULTSFILE("resultsfile");
const XalanDOMString  XMLFileReporter::ELEM_TESTFILE("testfile");
const XalanDOMString  XMLFileReporter::ELEM_FILERESULT("fileresult");
const XalanDOMString  XMLFileReporter::ELEM_TESTCASE("testcase");
const XalanDOMString  XMLFileReporter::ELEM_CASERESULT("caseresult");
const XalanDOMString  XMLFileReporter::ELEM_CHECKRESULT("checkresult");
const XalanDOMString  XMLFileReporter::ELEM_STATISTIC("statistic");
const XalanDOMString  XMLFileReporter::ELEM_LONGVAL("longval");
const XalanDOMString  XMLFileReporter::ELEM_DOUBLEVAL("doubleval");
const XalanDOMString  XMLFileReporter::ELEM_MESSAGE("message");
const XalanDOMString  XMLFileReporter::ELEM_ARBITRARY("arbitrary");
const XalanDOMString  XMLFileReporter::ELEM_HASHTABLE("hashtable");
const XalanDOMString  XMLFileReporter::ELEM_HASHITEM("hashitem");
const XalanDOMString  XMLFileReporter::ATTR_LEVEL("level");
const XalanDOMString  XMLFileReporter::ATTR_DESC("desc");
const XalanDOMString  XMLFileReporter::ATTR_TIME("time");
const XalanDOMString  XMLFileReporter::ATTR_RESULT("result");
const XalanDOMString  XMLFileReporter::ATTR_KEY("key");
const XalanDOMString  XMLFileReporter::ATTR_FILENAME = XMLFileReporter::OPT_FILENAME;

const XalanDOMString XMLFileReporter::TESTCASEINIT_HDR("<" + ELEM_TESTCASE + " " + ATTR_DESC + "=\"");
const XalanDOMString XMLFileReporter::TESTCASECLOSE_HDR("<" + ELEM_CASERESULT + " " + ATTR_DESC + "=\"");
const XalanDOMString XMLFileReporter::MESSAGE_HDR("<" + ELEM_MESSAGE + " " + ATTR_LEVEL + "=\"");
const XalanDOMString XMLFileReporter::STATISTIC_HDR("<" + ELEM_STATISTIC + " " + ATTR_LEVEL + "=\"");
const XalanDOMString XMLFileReporter::ARBITRARY_HDR("<" + ELEM_ARBITRARY + " " + ATTR_LEVEL + "=\"");
const XalanDOMString XMLFileReporter::HASHTABLE_HDR("<" + ELEM_HASHTABLE + " " + ATTR_LEVEL + "=\"");
const XalanDOMString XMLFileReporter::HASHITEM_HDR("  <" + ELEM_HASHITEM + " " + ATTR_KEY + "=\"");
const XalanDOMString XMLFileReporter::CHECKPASS_HDR("<" + ELEM_CHECKRESULT + " " + ATTR_RESULT + "=\"" + "PASS" + "\" " + ATTR_DESC + "=\"");
const XalanDOMString XMLFileReporter::CHECKAMBG_HDR("<" + ELEM_CHECKRESULT + " " + ATTR_RESULT + "=\"" + "AMBG" + "\" " + ATTR_DESC + "=\"");
const XalanDOMString XMLFileReporter::CHECKERRR_HDR("<" + ELEM_CHECKRESULT + " " + ATTR_RESULT + "=\"" + "ERRR" + "\" " + ATTR_DESC + "=\"");
const XalanDOMString XMLFileReporter::CHECKFAIL_HDR("<" + ELEM_CHECKRESULT + " " + ATTR_RESULT + "=\"" + "FAIL" + "\" " + ATTR_DESC + "=\"");

XMLFileReporter::XMLFileReporter():
	m_anyOutput(false),
	m_fileName(""),
	m_fileHandle(0),
	m_ready(false),
	m_error(false),
	m_flushOnCaseClose(true)
{
}

XMLFileReporter::XMLFileReporter(const XalanDOMString& fileName):
	m_anyOutput(false),
	m_fileName(fileName),
	m_fileHandle(0),
	m_ready(false),
	m_error(false),
	m_flushOnCaseClose(true)
{
    m_ready = initialize();
}

XMLFileReporter::XMLFileReporter(const char* fileName):
	m_anyOutput(false),
	m_fileName(XalanDOMString(fileName)),
	m_fileHandle(0),
	m_ready(false),
	m_error(false),
	m_flushOnCaseClose(true)
{
    m_ready = initialize();
}

bool
XMLFileReporter::initialize()
{
    if (length(m_fileName) == 0)
    {
        // We don't have a valid file, so bail
        m_error = true;
        m_ready = false;
        fprintf(stderr, "XMLFileReporter.initialize() ERROR: No file name specified");
        return(false);
    }

	// Transcode down the file name...
	const CharVectorType	theTranscodedFileName(m_fileName.transcode());
	const char* const		theTranscodedFileNamePointer = &theTranscodedFileName.front();

    // Create a file and ensure it has a place to live
	m_fileHandle = fopen(theTranscodedFileNamePointer, "w");
	if (m_fileHandle == 0)
	{
        // Couldn't create or find the directory for the file to live in, so bail
        m_error = true;
        m_ready = false;
        fprintf(stderr, "XMLFileReporter.initialize() ERROR: unble to open file, %s", theTranscodedFileNamePointer);
        return(false);
	}

    m_ready = true;
    startResultsFile();
    // fprintf(stderr, "DEBUG:XMLFileReporter.initialize() complete with " + fileName);

    return m_ready;
}

bool
XMLFileReporter::getFlushOnCaseClose()
{
    return(m_flushOnCaseClose);
}

const XalanDOMString&
XMLFileReporter::getFileName() const
{
    return(m_fileName);
}

void
XMLFileReporter::setFileName(const XalanDOMString& fileName)
{
	m_fileName = fileName;
}

bool
XMLFileReporter::checkError()
{
    // Ensure our underlying reporter, if one, is still OK
    if (m_fileHandle == 0)
    {
        m_error = true;
    }
    return(m_error);
}

bool
XMLFileReporter::isReady()
{
    // Ensure our underlying reporter, if one, is still OK
    if (m_fileHandle == 0)
    {
        // NEEDSWORK: should we set m_ready = false in this case?
        //            errors in the PrintStream are not necessarily fatal
        m_error = true;
        m_ready = false;
    }
    return(m_ready);
}

void
XMLFileReporter::flush()
{
    if (isReady())
    {
		fflush(m_fileHandle);
    }
}

void
XMLFileReporter::close()
{
    fflush(m_fileHandle);
    if (isReady())
    {
		if (m_fileHandle != 0)
		{
			closeResultsFile();
			fclose(m_fileHandle);
		}
    }
    m_ready = false;
}

void
XMLFileReporter::logTestFileInit(const XalanDOMString& msg)
{
    if (isReady())
    {
        printToFile("<" + ELEM_TESTFILE
                              + " " + ATTR_DESC + "=\"" + escapestring(msg) + "\" " + ATTR_TIME + "=\"" + getDateTimeString() + "\">");
    }
}

void
XMLFileReporter::logTestFileClose(const XalanDOMString& msg, const XalanDOMString& result)
{
    if (isReady())
    {
        printToFile("<" + ELEM_FILERESULT
                              + " " + ATTR_DESC + "=\"" + escapestring(msg) + "\" " + ATTR_RESULT + "=\"" + result + "\" " + ATTR_TIME + "=\"" + getDateTimeString() + "\"/>");
        printToFile("</" + ELEM_TESTFILE + ">");
    }
    flush();
}

void
XMLFileReporter::logTestCaseInit(const XalanDOMString& msg)
{
    if (isReady())
    {
        printToFile(TESTCASEINIT_HDR + escapestring(msg) + "\">");
    }
}

void
XMLFileReporter::logTestCaseClose(const XalanDOMString& msg, const XalanDOMString& result)
{
    if (isReady())
    {
        printToFile(TESTCASECLOSE_HDR + escapestring(msg) + "\" " + ATTR_RESULT + "=\"" + result + "\"/>");
        printToFile("</" + ELEM_TESTCASE + ">");
    }
    if (getFlushOnCaseClose())
    {
        flush();
    }
}

void
XMLFileReporter::logMessage(int level, const XalanDOMString& msg)
{
	char tmp[20];
	sprintf(tmp, "%d", level);

    if (isReady())
    {
        printToFile(MESSAGE_HDR + tmp + "\">");
        printToFile(escapestring(msg));
        printToFile("</" + ELEM_MESSAGE +">");
    }
}

void XMLFileReporter::addMetricToAttrs(char* desc, double theMetric, Hashtable& attrs)
{
	XalanDOMString	temp;

	DoubleToDOMString(theMetric, temp);
	attrs.insert(Hashtable::value_type(XalanDOMString(desc), temp));

	return;
}

void
XMLFileReporter::logElement(int level, const XalanDOMString& element, Hashtable attrs,const XalanDOMString& msg)
{
	if (isReady()
        && (element.empty() == 0)
        && (attrs.empty() == 0)
       )
    {
		char tmp[20];
		sprintf(tmp, "%d", level);
//
//		Took out this level attribute cuz we don't use it.
//      printToFile("<" + escapestring(element) + " " + ATTR_LEVEL + "=\""
//                      + tmp + "\"");
        printToFile("<" + escapestring(element) + " ");

		Hashtable::iterator theEnd = attrs.end();

       	for(Hashtable::iterator i = attrs.begin(); i != theEnd; ++i)
        {

            printToFile((*i).first + "=\""
                                  + (*i).second + "\"");
        }

        printToFile(XalanDOMString(">"));
        if (msg.empty() != 0)
            printToFile(escapestring(msg));
        printToFile("</" + escapestring(element) + ">");
    }
}

void
XMLFileReporter::logStatistic (int level, long lVal, double dVal, const XalanDOMString& msg)
{
 	char tmp[20];

	if (isReady())
    {
		sprintf(tmp, "%d", level);
        printToFile(STATISTIC_HDR + tmp + "\" " + ATTR_DESC + "=\"" + escapestring(msg) + "\">");

		sprintf(tmp, "%d", lVal);
		printToFile("<" + ELEM_LONGVAL + ">" + tmp + "</" + ELEM_LONGVAL + ">");

		sprintf(tmp, "%d", dVal);
        printToFile("<" + ELEM_DOUBLEVAL + ">" + tmp + "</" + ELEM_DOUBLEVAL + ">");

		printToFile("</" + ELEM_STATISTIC + ">");

    }
}

void
XMLFileReporter::logArbitraryMessage (int level, const XalanDOMString& msg)
{
	char tmp[20];
	sprintf(tmp, "%d", level);

    if (isReady())
    {
		printToFile(ARBITRARY_HDR + tmp + "\">");
        printToFile(escapestring(msg));
        printToFile("</" + ELEM_ARBITRARY +">");
    }
}

/*
void logHashtable (int level, Hashtable hash, XalanDOMString msg)
{
    if (isReady())
    {
        printToFile(HASHTABLE_HDR + level + "\" " + ATTR_DESC + "=\"" + msg + "\">");
        if (hash == null)
        {
            printToFile("<" + ELEM_HASHITEM + " " + ATTR_KEY + "=\"null\">");
            printToFile("</" + ELEM_HASHITEM + ">");
        }
        try
        {
            for (Enumeration enum = hash.keys(); enum.hasMoreElements();)
            {
                Object key = enum.nextElement();
                // Ensure we'll have clean output by pre-fetching value before outputting anything
                XalanDOMString value = hash.get(key).tostring();
                printToFile(HASHITEM_HDR + key.tostring() + "\">");
                printToFile(value);
                printToFile("</" + ELEM_HASHITEM + ">");
            }
        }
        catch (Exception e)
        {
            // No-op: should ensure we have clean output
        }
        printToFile("</" + ELEM_HASHTABLE +">");
    }
}
*/

void
XMLFileReporter::logCheckPass(const XalanDOMString& comment)
{
    if (isReady())
    {
        printToFile(CHECKPASS_HDR + escapestring(comment) + "\"/>");
    }
}

void
XMLFileReporter::logCheckAmbiguous(const XalanDOMString& comment)
{
    if (isReady())
    {
        printToFile(CHECKAMBG_HDR + escapestring(comment) + "\"/>");
    }
}

void
XMLFileReporter::logCheckFail(const XalanDOMString& comment)
{
    if (isReady())
    {
        printToFile(CHECKFAIL_HDR + escapestring(comment) + "\"/>");
    }
}

void
XMLFileReporter::logCheckErr(const XalanDOMString& comment)
{
    if (isReady())
    {
        printToFile(CHECKERRR_HDR + escapestring(comment) + "\"/>");
    }
}

static const XalanDOMChar	theLessThanString[] =
{
	XalanUnicode::charAmpersand,
	XalanUnicode::charLetter_l,
	XalanUnicode::charLetter_t,
	XalanUnicode::charSemicolon,
	0
};

static const XalanDOMChar	theGreaterThanString[] =
{
	XalanUnicode::charAmpersand,
	XalanUnicode::charLetter_g,
	XalanUnicode::charLetter_t,
	XalanUnicode::charSemicolon,
	0
};

XalanDOMString
XMLFileReporter::escapestring(const XalanDOMString&  s)
{
    XalanDOMString		sb;

    const unsigned int	length = s.length();

    for (unsigned int i = 0; i < length; i++)
    {
        const XalanDOMChar	ch = charAt(s, i);

        if (XalanUnicode::charLessThanSign == ch)
        {
			append(sb, theLessThanString);
        }
        else if (XalanUnicode::charGreaterThanSign == ch)
        {
			append(sb, theGreaterThanString);
        }
        // Note: Skipping escaping of UTF-16 surrogates and & ampersands, since
        //  I don't think we'll be outputting them or they won't affect our output
        else
        {
            append(sb, ch);
        }
    }

	return sb;
}

bool
XMLFileReporter::startResultsFile()
{
    if (isReady())
    {
        // Write out XML header and root test result element
        printToFile(XalanDOMString("<?xml version=\"1.0\"?>"));

        // Note: this tag is closed in our .close() method, which the caller had better call!
        printToFile("<" + ELEM_RESULTSFILE + " " + ATTR_FILENAME + "=\"" + m_fileName + "\">");

        return true;
    }
    else
	{
        return false;
	}
}

bool
XMLFileReporter::closeResultsFile()
{
    if (isReady())
    {
		printToFile("</" + ELEM_RESULTSFILE + ">");
        return true;
    }
    else
        return false;
}

bool
XMLFileReporter::printToFile(const XalanDOMString&	output)
{
    if (isReady())
    {
        fprintf(m_fileHandle, &output.transcode().front());
		fprintf(m_fileHandle, "\n");
        return true;
    }
    else
        return false;
}

XalanDOMString
XMLFileReporter::getDateTimeString()
{
	struct tm *tmNow;
	time_t time_tNow;

	time(&time_tNow);
	tmNow = localtime(&time_tNow);

	const char* const	theTime = asctime(tmNow);

	return XalanDOMString(theTime, strlen(theTime) - 1);
}
<|endoftext|>


<commit_before>//---------------------------------------------------------- -*- Mode: C++ -*-
// $Id$
//
// Created 2005/03/01
//
// Copyright 2008-2012 Quantcast Corp.
// Copyright 2006-2008 Kosmix Corp.
//
// This file is part of Kosmos File System (KFS).
//
// Licensed under the Apache License, Version 2.0
// (the "License"); you may not use this file except in compliance with
// the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
// implied. See the License for the specific language governing
// permissions and limitations under the License.
//
//----------------------------------------------------------------------------

#include "MsgLogger.h"
#include "Version.h"
#include <stdio.h>
#include <limits>

namespace KFS
{

MsgLogger* MsgLogger::logger = 0;
const MsgLogger::LogLevel kMsgLoggerDefaultLogLevel =
#ifdef NDEBUG
    MsgLogger::kLogLevelINFO
#else
    MsgLogger::kLogLevelDEBUG
#endif
;

MsgLogger::MsgLogger(
    const char*         filename,
    MsgLogger::LogLevel logLevel,
    const Properties*   props,
    const char*         propPrefix)
    : BufferedLogWriter(filename ? -1 : fileno(stderr), filename)
{
    BufferedLogWriter::SetLogLevel(logLevel);
    BufferedLogWriter::SetMaxLogWaitTime(std::numeric_limits<int>::max());
    if (props) {
        BufferedLogWriter::SetParameters(*props, propPrefix);
    }
    BufferedLogWriter::Append(kLogLevelINFO, "version: %s %s",
        KFS_BUILD_VERSION_STRING.c_str(), KFS_SOURCE_REVISION_STRING.c_str());
}

MsgLogger::~MsgLogger()
{
    if (this == logger) {
        logger = 0;
    }
}

void
MsgLogger::Init(
    const char* filename)
{
    Init(filename, kMsgLoggerDefaultLogLevel, 0, 0);
}

void
MsgLogger::Init(
    const char*         filename,
    MsgLogger::LogLevel logLevel)
{
    Init(filename, logLevel, 0, 0);
}

void
MsgLogger::Init(
    const Properties& props,
    const char*       propPrefix)
{
    Init(0, kMsgLoggerDefaultLogLevel, &props, propPrefix);
}

void
MsgLogger::Init(
    const char*         filename,
    MsgLogger::LogLevel logLevel,
    const Properties*   props,
    const char*         propPrefix)
{
    if (logger) {
        if (props) {
            logger->SetParameters(*props, propPrefix);
        }
    } else {
        static MsgLogger sLogger(filename, logLevel, props, propPrefix);
        logger = &sLogger;
    }
}

void
MsgLogger::Stop()
{
    if (logger) {
        logger->BufferedLogWriter::Stop();
    }
}

} // namespace KFS
<commit_msg>logging: Reverting log level to DEBUG in MsgLogger. To get version information for 'Release' builds, one could use src/cc/common/buildversgit.sh -g <binary><commit_after>//---------------------------------------------------------- -*- Mode: C++ -*-
// $Id$
//
// Created 2005/03/01
//
// Copyright 2008-2012 Quantcast Corp.
// Copyright 2006-2008 Kosmix Corp.
//
// This file is part of Kosmos File System (KFS).
//
// Licensed under the Apache License, Version 2.0
// (the "License"); you may not use this file except in compliance with
// the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
// implied. See the License for the specific language governing
// permissions and limitations under the License.
//
//----------------------------------------------------------------------------

#include "MsgLogger.h"
#include "Version.h"
#include <stdio.h>
#include <limits>

namespace KFS
{

MsgLogger* MsgLogger::logger = 0;
const MsgLogger::LogLevel kMsgLoggerDefaultLogLevel =
#ifdef NDEBUG
    MsgLogger::kLogLevelINFO
#else
    MsgLogger::kLogLevelDEBUG
#endif
;

MsgLogger::MsgLogger(
    const char*         filename,
    MsgLogger::LogLevel logLevel,
    const Properties*   props,
    const char*         propPrefix)
    : BufferedLogWriter(filename ? -1 : fileno(stderr), filename)
{
    BufferedLogWriter::SetLogLevel(logLevel);
    BufferedLogWriter::SetMaxLogWaitTime(std::numeric_limits<int>::max());
    if (props) {
        BufferedLogWriter::SetParameters(*props, propPrefix);
    }
    BufferedLogWriter::Append(kLogLevelDEBUG, "version: %s %s",
        KFS_BUILD_VERSION_STRING.c_str(), KFS_SOURCE_REVISION_STRING.c_str());
}

MsgLogger::~MsgLogger()
{
    if (this == logger) {
        logger = 0;
    }
}

void
MsgLogger::Init(
    const char* filename)
{
    Init(filename, kMsgLoggerDefaultLogLevel, 0, 0);
}

void
MsgLogger::Init(
    const char*         filename,
    MsgLogger::LogLevel logLevel)
{
    Init(filename, logLevel, 0, 0);
}

void
MsgLogger::Init(
    const Properties& props,
    const char*       propPrefix)
{
    Init(0, kMsgLoggerDefaultLogLevel, &props, propPrefix);
}

void
MsgLogger::Init(
    const char*         filename,
    MsgLogger::LogLevel logLevel,
    const Properties*   props,
    const char*         propPrefix)
{
    if (logger) {
        if (props) {
            logger->SetParameters(*props, propPrefix);
        }
    } else {
        static MsgLogger sLogger(filename, logLevel, props, propPrefix);
        logger = &sLogger;
    }
}

void
MsgLogger::Stop()
{
    if (logger) {
        logger->BufferedLogWriter::Stop();
    }
}

} // namespace KFS
<|endoftext|>