<commit_before>/**
 * This file is part of TelepathyQt4
 *
 * @copyright Copyright (C) 2008 Collabora Ltd. <http://www.collabora.co.uk/>
 * @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 <TelepathyQt4/PendingContacts>
#include "TelepathyQt4/_gen/pending-contacts.moc.hpp"

#include <TelepathyQt4/Connection>
#include <TelepathyQt4/ConnectionLowlevel>
#include <TelepathyQt4/ContactManager>
#include <TelepathyQt4/ContactFactory>
#include <TelepathyQt4/PendingContactAttributes>
#include <TelepathyQt4/PendingHandles>
#include <TelepathyQt4/ReferencedHandles>

#include "TelepathyQt4/debug-internal.h"

namespace Tp
{

struct TELEPATHY_QT4_NO_EXPORT PendingContacts::Private
{
    enum RequestType
    {
        ForHandles,
        ForIdentifiers,
        Upgrade
    };

    Private(PendingContacts *parent, const ContactManagerPtr &manager, const UIntList &handles,
            const Features &features, const Features &missingFeatures,
            const QMap<uint, ContactPtr> &satisfyingContacts)
        : parent(parent),
          manager(manager),
          features(features),
          missingFeatures(missingFeatures),
          satisfyingContacts(satisfyingContacts),
          requestType(ForHandles),
          handles(handles),
          nested(0)
    {
    }

    Private(PendingContacts *parent, const ContactManagerPtr &manager, const QStringList &identifiers,
            const Features &features)
        : parent(parent),
          manager(manager),
          features(features),
          requestType(ForIdentifiers),
          identifiers(identifiers),
          nested(0)
    {
    }

    Private(PendingContacts *parent,
            const ContactManagerPtr &manager, const QList<ContactPtr> &contactsToUpgrade,
            const Features &features)
        : parent(parent),
          manager(manager),
          features(features),
          requestType(Upgrade),
          contactsToUpgrade(contactsToUpgrade),
          nested(0)
    {
    }

    void setFinished();

    // Public object
    PendingContacts *parent;

    // Generic parameters
    ContactManagerPtr manager;
    Features features;
    Features missingFeatures;
    QMap<uint, ContactPtr> satisfyingContacts;

    // Request type specific parameters
    RequestType requestType;
    UIntList handles;
    QStringList identifiers;
    QList<ContactPtr> contactsToUpgrade;
    PendingContacts *nested;

    // Results
    QList<ContactPtr> contacts;
    UIntList invalidHandles;
    QStringList validIds;
    QHash<QString, QPair<QString, QString> > invalidIds;

    ReferencedHandles handlesToInspect;
};

void PendingContacts::Private::setFinished()
{
    ConnectionLowlevelPtr connLowlevel = manager->connection()->lowlevel();
    UIntList handles = invalidHandles;
    foreach (uint handle, handles) {
        if (connLowlevel->hasContactId(handle)) {
            satisfyingContacts.insert(handle, manager->ensureContact(handle,
                        connLowlevel->contactId(handle), missingFeatures));
            invalidHandles.removeOne(handle);
        }
    }

    parent->setFinished();
}

/**
 * \class PendingContacts
 * \ingroup clientconn
 * \headerfile TelepathyQt4/pending-contacts.h <TelepathyQt4/PendingContacts>
 *
 * \brief The PendingContacts class is used by ContactManager when
 * creating/updating Contact objects.
 */

PendingContacts::PendingContacts(const ContactManagerPtr &manager,
        const UIntList &handles,
        const Features &features,
        const Features &missingFeatures,
        const QStringList &interfaces,
        const QMap<uint, ContactPtr> &satisfyingContacts,
        const QSet<uint> &otherContacts,
        const QString &errorName,
        const QString &errorMessage)
    : PendingOperation(manager),
      mPriv(new Private(this, manager, handles, features, missingFeatures, satisfyingContacts))
{
    if (!errorName.isEmpty()) {
        setFinishedWithError(errorName, errorMessage);
        return;
    }

    if (!otherContacts.isEmpty()) {
        ConnectionPtr conn = manager->connection();
        if (conn->interfaces().contains(QLatin1String(TELEPATHY_INTERFACE_CONNECTION_INTERFACE_CONTACTS))) {
            PendingContactAttributes *attributes =
                conn->lowlevel()->contactAttributes(otherContacts.toList(),
                        interfaces, true);

            connect(attributes,
                    SIGNAL(finished(Tp::PendingOperation*)),
                    SLOT(onAttributesFinished(Tp::PendingOperation*)));
        } else {
            // fallback to just create the contacts
            PendingHandles *handles = conn->lowlevel()->referenceHandles(HandleTypeContact,
                    otherContacts.toList());
            connect(handles,
                    SIGNAL(finished(Tp::PendingOperation*)),
                    SLOT(onReferenceHandlesFinished(Tp::PendingOperation*)));
        }
    } else {
        allAttributesFetched();
    }
}

PendingContacts::PendingContacts(const ContactManagerPtr &manager,
        const QStringList &identifiers, const Features &features,
        const QString &errorName, const QString &errorMessage)
    : PendingOperation(manager),
      mPriv(new Private(this, manager, identifiers, features))
{
    if (!errorName.isEmpty()) {
        setFinishedWithError(errorName, errorMessage);
        return;
    }

    ConnectionPtr conn = manager->connection();
    PendingHandles *handles = conn->lowlevel()->requestHandles(HandleTypeContact, identifiers);

    connect(handles,
            SIGNAL(finished(Tp::PendingOperation*)),
            SLOT(onRequestHandlesFinished(Tp::PendingOperation*)));
}

PendingContacts::PendingContacts(const ContactManagerPtr &manager,
        const QList<ContactPtr> &contacts, const Features &features,
        const QString &errorName, const QString &errorMessage)
    : PendingOperation(manager),
      mPriv(new Private(this, manager, contacts, features))
{
    if (!errorName.isEmpty()) {
        setFinishedWithError(errorName, errorMessage);
        return;
    }

    UIntList handles;
    foreach (const ContactPtr &contact, contacts) {
        handles.push_back(contact->handle()[0]);
    }

    mPriv->nested = manager->contactsForHandles(handles, features);
    connect(mPriv->nested,
            SIGNAL(finished(Tp::PendingOperation*)),
            SLOT(onNestedFinished(Tp::PendingOperation*)));
}

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

ContactManagerPtr PendingContacts::manager() const
{
    return mPriv->manager;
}

Features PendingContacts::features() const
{
    return mPriv->features;
}

bool PendingContacts::isForHandles() const
{
    return mPriv->requestType == Private::ForHandles;
}

UIntList PendingContacts::handles() const
{
    if (!isForHandles()) {
        warning() << "Tried to get handles from" << this << "which is not for handles!";
    }

    return mPriv->handles;
}

bool PendingContacts::isForIdentifiers() const
{
    return mPriv->requestType == Private::ForIdentifiers;
}

QStringList PendingContacts::identifiers() const
{
    if (!isForIdentifiers()) {
        warning() << "Tried to get identifiers from" << this << "which is not for identifiers!";
    }

    return mPriv->identifiers;
}

bool PendingContacts::isUpgrade() const
{
    return mPriv->requestType == Private::Upgrade;
}

QList<ContactPtr> PendingContacts::contactsToUpgrade() const
{
    if (!isUpgrade()) {
        warning() << "Tried to get contacts to upgrade from" << this << "which is not an upgrade!";
    }

    return mPriv->contactsToUpgrade;
}

QList<ContactPtr> PendingContacts::contacts() const
{
    if (!isFinished()) {
        warning() << "PendingContacts::contacts() called before finished";
    } else if (isError()) {
        warning() << "PendingContacts::contacts() called when errored";
    }

    return mPriv->contacts;
}

UIntList PendingContacts::invalidHandles() const
{
    if (!isFinished()) {
        warning() << "PendingContacts::invalidHandles() called before finished";
    } else if (isError()) {
        warning() << "PendingContacts::invalidHandles() called when errored";
    } else if (!isForHandles()) {
        warning() << "PendingContacts::invalidHandles() called for" << this << "which is for IDs!";
    }

    return mPriv->invalidHandles;
}

QStringList PendingContacts::validIdentifiers() const
{
    if (!isFinished()) {
        warning() << "PendingContacts::validIdentifiers called before finished";
    } else if (!isValid()) {
        warning() << "PendingContacts::validIdentifiers called when not valid";
    }

    return mPriv->validIds;
}

QHash<QString, QPair<QString, QString> > PendingContacts::invalidIdentifiers() const
{
    if (!isFinished()) {
        warning() << "PendingContacts::invalidIdentifiers called before finished";
    }

    return mPriv->invalidIds;
}

void PendingContacts::onAttributesFinished(PendingOperation *operation)
{
    PendingContactAttributes *pendingAttributes =
        qobject_cast<PendingContactAttributes *>(operation);

    if (pendingAttributes->isError()) {
        debug() << "PendingAttrs error" << pendingAttributes->errorName()
                << "message" << pendingAttributes->errorMessage();
        setFinishedWithError(pendingAttributes->errorName(), pendingAttributes->errorMessage());
        return;
    }

    ReferencedHandles validHandles = pendingAttributes->validHandles();
    ContactAttributesMap attributes = pendingAttributes->attributes();

    foreach (uint handle, mPriv->handles) {
        if (!mPriv->satisfyingContacts.contains(handle)) {
            int indexInValid = validHandles.indexOf(handle);
            if (indexInValid >= 0) {
                ReferencedHandles referencedHandle = validHandles.mid(indexInValid, 1);
                QVariantMap handleAttributes = attributes[handle];
                mPriv->satisfyingContacts.insert(handle, manager()->ensureContact(referencedHandle,
                            mPriv->missingFeatures, handleAttributes));
            } else {
                mPriv->invalidHandles.push_back(handle);
            }
        }
    }

    allAttributesFetched();
}

void PendingContacts::onRequestHandlesFinished(PendingOperation *operation)
{
    PendingHandles *pendingHandles = qobject_cast<PendingHandles *>(operation);

    mPriv->validIds = pendingHandles->validNames();
    mPriv->invalidIds = pendingHandles->invalidNames();

    if (pendingHandles->isError()) {
        debug() << "RequestHandles error" << operation->errorName()
                << "message" << operation->errorMessage();
        setFinishedWithError(operation->errorName(), operation->errorMessage());
        return;
    }

    mPriv->nested = manager()->contactsForHandles(pendingHandles->handles(), features());
    connect(mPriv->nested,
            SIGNAL(finished(Tp::PendingOperation*)),
            SLOT(onNestedFinished(Tp::PendingOperation*)));
}

void PendingContacts::onReferenceHandlesFinished(PendingOperation *operation)
{
    PendingHandles *pendingHandles = qobject_cast<PendingHandles *>(operation);

    if (pendingHandles->isError()) {
        debug() << "ReferenceHandles error" << operation->errorName()
                << "message" << operation->errorMessage();
        setFinishedWithError(operation->errorName(), operation->errorMessage());
        return;
    }

    ReferencedHandles validHandles = pendingHandles->handles();
    UIntList invalidHandles = pendingHandles->invalidHandles();
    ConnectionPtr conn = mPriv->manager->connection();
    mPriv->handlesToInspect = ReferencedHandles(conn, HandleTypeContact, UIntList());
    foreach (uint handle, mPriv->handles) {
        if (!mPriv->satisfyingContacts.contains(handle)) {
            int indexInValid = validHandles.indexOf(handle);
            if (indexInValid >= 0) {
                ReferencedHandles referencedHandle = validHandles.mid(indexInValid, 1);
                mPriv->handlesToInspect.append(referencedHandle);
            } else {
                mPriv->invalidHandles.push_back(handle);
            }
        }
    }

    QDBusPendingCallWatcher *watcher =
        new QDBusPendingCallWatcher(
                conn->baseInterface()->InspectHandles(HandleTypeContact,
                    mPriv->handlesToInspect.toList()),
                this);
    connect(watcher,
            SIGNAL(finished(QDBusPendingCallWatcher*)),
            SLOT(onInspectHandlesFinished(QDBusPendingCallWatcher*)));
}

void PendingContacts::onNestedFinished(PendingOperation *operation)
{
    Q_ASSERT(operation == mPriv->nested);

    if (operation->isError()) {
        debug() << " error" << operation->errorName()
                << "message" << operation->errorMessage();
        setFinishedWithError(operation->errorName(), operation->errorMessage());
        return;
    }

    mPriv->contacts = mPriv->nested->contacts();
    mPriv->nested = 0;
    mPriv->setFinished();
}

void PendingContacts::onInspectHandlesFinished(QDBusPendingCallWatcher *watcher)
{
    QDBusPendingReply<QStringList> reply = *watcher;

    if (reply.isError()) {
        debug().nospace() << "InspectHandles: error " << reply.error().name() << ": "
            << reply.error().message();
        setFinishedWithError(reply.error());
        return;
    }

    QStringList names = reply.value();
    int i = 0;
    ConnectionPtr conn = mPriv->manager->connection();
    foreach (uint handle, mPriv->handlesToInspect) {
        QVariantMap handleAttributes;
        handleAttributes.insert(QLatin1String(TELEPATHY_INTERFACE_CONNECTION "/contact-id"),
                names[i++]);
        ReferencedHandles referencedHandle(conn, HandleTypeContact,
                UIntList() << handle);
        mPriv->satisfyingContacts.insert(handle, manager()->ensureContact(referencedHandle,
                    mPriv->missingFeatures, handleAttributes));
    }

    allAttributesFetched();

    watcher->deleteLater();
}

void PendingContacts::allAttributesFetched()
{
    foreach (uint handle, mPriv->handles) {
        if (mPriv->satisfyingContacts.contains(handle)) {
            mPriv->contacts.push_back(mPriv->satisfyingContacts[handle]);
        }
    }

    mPriv->setFinished();
}

} // Tp
<commit_msg>PendingContacts: Update docs.<commit_after>/**
 * This file is part of TelepathyQt4
 *
 * @copyright Copyright (C) 2008 Collabora Ltd. <http://www.collabora.co.uk/>
 * @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 <TelepathyQt4/PendingContacts>
#include "TelepathyQt4/_gen/pending-contacts.moc.hpp"

#include <TelepathyQt4/Connection>
#include <TelepathyQt4/ConnectionLowlevel>
#include <TelepathyQt4/ContactManager>
#include <TelepathyQt4/ContactFactory>
#include <TelepathyQt4/PendingContactAttributes>
#include <TelepathyQt4/PendingHandles>
#include <TelepathyQt4/ReferencedHandles>

#include "TelepathyQt4/debug-internal.h"

namespace Tp
{

struct TELEPATHY_QT4_NO_EXPORT PendingContacts::Private
{
    enum RequestType
    {
        ForHandles,
        ForIdentifiers,
        Upgrade
    };

    Private(PendingContacts *parent, const ContactManagerPtr &manager, const UIntList &handles,
            const Features &features, const Features &missingFeatures,
            const QMap<uint, ContactPtr> &satisfyingContacts)
        : parent(parent),
          manager(manager),
          features(features),
          missingFeatures(missingFeatures),
          satisfyingContacts(satisfyingContacts),
          requestType(ForHandles),
          handles(handles),
          nested(0)
    {
    }

    Private(PendingContacts *parent, const ContactManagerPtr &manager, const QStringList &identifiers,
            const Features &features)
        : parent(parent),
          manager(manager),
          features(features),
          requestType(ForIdentifiers),
          identifiers(identifiers),
          nested(0)
    {
    }

    Private(PendingContacts *parent,
            const ContactManagerPtr &manager, const QList<ContactPtr> &contactsToUpgrade,
            const Features &features)
        : parent(parent),
          manager(manager),
          features(features),
          requestType(Upgrade),
          contactsToUpgrade(contactsToUpgrade),
          nested(0)
    {
    }

    void setFinished();

    // Public object
    PendingContacts *parent;

    // Generic parameters
    ContactManagerPtr manager;
    Features features;
    Features missingFeatures;
    QMap<uint, ContactPtr> satisfyingContacts;

    // Request type specific parameters
    RequestType requestType;
    UIntList handles;
    QStringList identifiers;
    QList<ContactPtr> contactsToUpgrade;
    PendingContacts *nested;

    // Results
    QList<ContactPtr> contacts;
    UIntList invalidHandles;
    QStringList validIds;
    QHash<QString, QPair<QString, QString> > invalidIds;

    ReferencedHandles handlesToInspect;
};

void PendingContacts::Private::setFinished()
{
    ConnectionLowlevelPtr connLowlevel = manager->connection()->lowlevel();
    UIntList handles = invalidHandles;
    foreach (uint handle, handles) {
        if (connLowlevel->hasContactId(handle)) {
            satisfyingContacts.insert(handle, manager->ensureContact(handle,
                        connLowlevel->contactId(handle), missingFeatures));
            invalidHandles.removeOne(handle);
        }
    }

    parent->setFinished();
}

/**
 * \class PendingContacts
 * \ingroup clientconn
 * \headerfile TelepathyQt4/pending-contacts.h <TelepathyQt4/PendingContacts>
 *
 * \brief The PendingContacts class is used by ContactManager when
 * creating/updating Contact objects.
 *
 * See \ref async_model
 */

PendingContacts::PendingContacts(const ContactManagerPtr &manager,
        const UIntList &handles,
        const Features &features,
        const Features &missingFeatures,
        const QStringList &interfaces,
        const QMap<uint, ContactPtr> &satisfyingContacts,
        const QSet<uint> &otherContacts,
        const QString &errorName,
        const QString &errorMessage)
    : PendingOperation(manager),
      mPriv(new Private(this, manager, handles, features, missingFeatures, satisfyingContacts))
{
    if (!errorName.isEmpty()) {
        setFinishedWithError(errorName, errorMessage);
        return;
    }

    if (!otherContacts.isEmpty()) {
        ConnectionPtr conn = manager->connection();
        if (conn->interfaces().contains(QLatin1String(TELEPATHY_INTERFACE_CONNECTION_INTERFACE_CONTACTS))) {
            PendingContactAttributes *attributes =
                conn->lowlevel()->contactAttributes(otherContacts.toList(),
                        interfaces, true);

            connect(attributes,
                    SIGNAL(finished(Tp::PendingOperation*)),
                    SLOT(onAttributesFinished(Tp::PendingOperation*)));
        } else {
            // fallback to just create the contacts
            PendingHandles *handles = conn->lowlevel()->referenceHandles(HandleTypeContact,
                    otherContacts.toList());
            connect(handles,
                    SIGNAL(finished(Tp::PendingOperation*)),
                    SLOT(onReferenceHandlesFinished(Tp::PendingOperation*)));
        }
    } else {
        allAttributesFetched();
    }
}

PendingContacts::PendingContacts(const ContactManagerPtr &manager,
        const QStringList &identifiers, const Features &features,
        const QString &errorName, const QString &errorMessage)
    : PendingOperation(manager),
      mPriv(new Private(this, manager, identifiers, features))
{
    if (!errorName.isEmpty()) {
        setFinishedWithError(errorName, errorMessage);
        return;
    }

    ConnectionPtr conn = manager->connection();
    PendingHandles *handles = conn->lowlevel()->requestHandles(HandleTypeContact, identifiers);

    connect(handles,
            SIGNAL(finished(Tp::PendingOperation*)),
            SLOT(onRequestHandlesFinished(Tp::PendingOperation*)));
}

PendingContacts::PendingContacts(const ContactManagerPtr &manager,
        const QList<ContactPtr> &contacts, const Features &features,
        const QString &errorName, const QString &errorMessage)
    : PendingOperation(manager),
      mPriv(new Private(this, manager, contacts, features))
{
    if (!errorName.isEmpty()) {
        setFinishedWithError(errorName, errorMessage);
        return;
    }

    UIntList handles;
    foreach (const ContactPtr &contact, contacts) {
        handles.push_back(contact->handle()[0]);
    }

    mPriv->nested = manager->contactsForHandles(handles, features);
    connect(mPriv->nested,
            SIGNAL(finished(Tp::PendingOperation*)),
            SLOT(onNestedFinished(Tp::PendingOperation*)));
}

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

ContactManagerPtr PendingContacts::manager() const
{
    return mPriv->manager;
}

Features PendingContacts::features() const
{
    return mPriv->features;
}

bool PendingContacts::isForHandles() const
{
    return mPriv->requestType == Private::ForHandles;
}

UIntList PendingContacts::handles() const
{
    if (!isForHandles()) {
        warning() << "Tried to get handles from" << this << "which is not for handles!";
    }

    return mPriv->handles;
}

bool PendingContacts::isForIdentifiers() const
{
    return mPriv->requestType == Private::ForIdentifiers;
}

QStringList PendingContacts::identifiers() const
{
    if (!isForIdentifiers()) {
        warning() << "Tried to get identifiers from" << this << "which is not for identifiers!";
    }

    return mPriv->identifiers;
}

bool PendingContacts::isUpgrade() const
{
    return mPriv->requestType == Private::Upgrade;
}

QList<ContactPtr> PendingContacts::contactsToUpgrade() const
{
    if (!isUpgrade()) {
        warning() << "Tried to get contacts to upgrade from" << this << "which is not an upgrade!";
    }

    return mPriv->contactsToUpgrade;
}

QList<ContactPtr> PendingContacts::contacts() const
{
    if (!isFinished()) {
        warning() << "PendingContacts::contacts() called before finished";
    } else if (isError()) {
        warning() << "PendingContacts::contacts() called when errored";
    }

    return mPriv->contacts;
}

UIntList PendingContacts::invalidHandles() const
{
    if (!isFinished()) {
        warning() << "PendingContacts::invalidHandles() called before finished";
    } else if (isError()) {
        warning() << "PendingContacts::invalidHandles() called when errored";
    } else if (!isForHandles()) {
        warning() << "PendingContacts::invalidHandles() called for" << this << "which is for IDs!";
    }

    return mPriv->invalidHandles;
}

QStringList PendingContacts::validIdentifiers() const
{
    if (!isFinished()) {
        warning() << "PendingContacts::validIdentifiers called before finished";
    } else if (!isValid()) {
        warning() << "PendingContacts::validIdentifiers called when not valid";
    }

    return mPriv->validIds;
}

QHash<QString, QPair<QString, QString> > PendingContacts::invalidIdentifiers() const
{
    if (!isFinished()) {
        warning() << "PendingContacts::invalidIdentifiers called before finished";
    }

    return mPriv->invalidIds;
}

void PendingContacts::onAttributesFinished(PendingOperation *operation)
{
    PendingContactAttributes *pendingAttributes =
        qobject_cast<PendingContactAttributes *>(operation);

    if (pendingAttributes->isError()) {
        debug() << "PendingAttrs error" << pendingAttributes->errorName()
                << "message" << pendingAttributes->errorMessage();
        setFinishedWithError(pendingAttributes->errorName(), pendingAttributes->errorMessage());
        return;
    }

    ReferencedHandles validHandles = pendingAttributes->validHandles();
    ContactAttributesMap attributes = pendingAttributes->attributes();

    foreach (uint handle, mPriv->handles) {
        if (!mPriv->satisfyingContacts.contains(handle)) {
            int indexInValid = validHandles.indexOf(handle);
            if (indexInValid >= 0) {
                ReferencedHandles referencedHandle = validHandles.mid(indexInValid, 1);
                QVariantMap handleAttributes = attributes[handle];
                mPriv->satisfyingContacts.insert(handle, manager()->ensureContact(referencedHandle,
                            mPriv->missingFeatures, handleAttributes));
            } else {
                mPriv->invalidHandles.push_back(handle);
            }
        }
    }

    allAttributesFetched();
}

void PendingContacts::onRequestHandlesFinished(PendingOperation *operation)
{
    PendingHandles *pendingHandles = qobject_cast<PendingHandles *>(operation);

    mPriv->validIds = pendingHandles->validNames();
    mPriv->invalidIds = pendingHandles->invalidNames();

    if (pendingHandles->isError()) {
        debug() << "RequestHandles error" << operation->errorName()
                << "message" << operation->errorMessage();
        setFinishedWithError(operation->errorName(), operation->errorMessage());
        return;
    }

    mPriv->nested = manager()->contactsForHandles(pendingHandles->handles(), features());
    connect(mPriv->nested,
            SIGNAL(finished(Tp::PendingOperation*)),
            SLOT(onNestedFinished(Tp::PendingOperation*)));
}

void PendingContacts::onReferenceHandlesFinished(PendingOperation *operation)
{
    PendingHandles *pendingHandles = qobject_cast<PendingHandles *>(operation);

    if (pendingHandles->isError()) {
        debug() << "ReferenceHandles error" << operation->errorName()
                << "message" << operation->errorMessage();
        setFinishedWithError(operation->errorName(), operation->errorMessage());
        return;
    }

    ReferencedHandles validHandles = pendingHandles->handles();
    UIntList invalidHandles = pendingHandles->invalidHandles();
    ConnectionPtr conn = mPriv->manager->connection();
    mPriv->handlesToInspect = ReferencedHandles(conn, HandleTypeContact, UIntList());
    foreach (uint handle, mPriv->handles) {
        if (!mPriv->satisfyingContacts.contains(handle)) {
            int indexInValid = validHandles.indexOf(handle);
            if (indexInValid >= 0) {
                ReferencedHandles referencedHandle = validHandles.mid(indexInValid, 1);
                mPriv->handlesToInspect.append(referencedHandle);
            } else {
                mPriv->invalidHandles.push_back(handle);
            }
        }
    }

    QDBusPendingCallWatcher *watcher =
        new QDBusPendingCallWatcher(
                conn->baseInterface()->InspectHandles(HandleTypeContact,
                    mPriv->handlesToInspect.toList()),
                this);
    connect(watcher,
            SIGNAL(finished(QDBusPendingCallWatcher*)),
            SLOT(onInspectHandlesFinished(QDBusPendingCallWatcher*)));
}

void PendingContacts::onNestedFinished(PendingOperation *operation)
{
    Q_ASSERT(operation == mPriv->nested);

    if (operation->isError()) {
        debug() << " error" << operation->errorName()
                << "message" << operation->errorMessage();
        setFinishedWithError(operation->errorName(), operation->errorMessage());
        return;
    }

    mPriv->contacts = mPriv->nested->contacts();
    mPriv->nested = 0;
    mPriv->setFinished();
}

void PendingContacts::onInspectHandlesFinished(QDBusPendingCallWatcher *watcher)
{
    QDBusPendingReply<QStringList> reply = *watcher;

    if (reply.isError()) {
        debug().nospace() << "InspectHandles: error " << reply.error().name() << ": "
            << reply.error().message();
        setFinishedWithError(reply.error());
        return;
    }

    QStringList names = reply.value();
    int i = 0;
    ConnectionPtr conn = mPriv->manager->connection();
    foreach (uint handle, mPriv->handlesToInspect) {
        QVariantMap handleAttributes;
        handleAttributes.insert(QLatin1String(TELEPATHY_INTERFACE_CONNECTION "/contact-id"),
                names[i++]);
        ReferencedHandles referencedHandle(conn, HandleTypeContact,
                UIntList() << handle);
        mPriv->satisfyingContacts.insert(handle, manager()->ensureContact(referencedHandle,
                    mPriv->missingFeatures, handleAttributes));
    }

    allAttributesFetched();

    watcher->deleteLater();
}

void PendingContacts::allAttributesFetched()
{
    foreach (uint handle, mPriv->handles) {
        if (mPriv->satisfyingContacts.contains(handle)) {
            mPriv->contacts.push_back(mPriv->satisfyingContacts[handle]);
        }
    }

    mPriv->setFinished();
}

} // Tp
<|endoftext|>


<commit_before>/*!
    \file encoding.cpp
    \brief Encoding utilities implementation
    \author Ivan Shynkarenka
    \date 12.08.2016
    \copyright MIT License
*/

#include "string/encoding.h"

#include <codecvt>
#include <locale>

namespace CppCommon {

std::string Encoding::ToUTF8(const std::wstring& wstr)
{
#if defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
    std::wstring_convert<std::codecvt_utf8<wchar_t>> convert;
    return convert.to_bytes(wstr);
#elif defined(_WIN32) || defined(_WIN64)
    std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> convert;
    return convert.to_bytes(wstr);
#endif
}

std::wstring Encoding::FromUTF8(const std::string& str)
{
#if defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
    std::wstring_convert<std::codecvt_utf8<wchar_t>> convert;
    return convert.from_bytes(str);
#elif defined(_WIN32) || defined(_WIN64)
    std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> convert;
    return convert.from_bytes(str);
#endif
}

std::u16string Encoding::UTF8toUTF16(const std::string& str)
{
#if defined(_MSC_VER) && (_MSC_VER == 1900)
    std::wstring_convert<std::codecvt_utf8_utf16<uint16_t>, uint16_t> convert;
    auto tmp = convert.from_bytes(str);
    return std::u16string(tmp.data(), tmp.data() + tmp.size());
#else
    std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> convert;
    return convert.from_bytes(str);
#endif
}

std::u32string Encoding::UTF8toUTF32(const std::string& str)
{
#if defined(_MSC_VER) && (_MSC_VER == 1900)
    std::wstring_convert<std::codecvt_utf8<uint32_t>, uint32_t> convert;
    auto tmp = convert.from_bytes(str);
    return std::u32string(tmp.data(), tmp.data() + tmp.size());
#else
    std::wstring_convert<std::codecvt_utf8<char32_t>, char32_t> convert;
    return convert.from_bytes(str);
#endif
}

std::string Encoding::UTF16toUTF8(const std::u16string& str)
{
#if defined(_MSC_VER) && (_MSC_VER == 1900)
    std::wstring_convert<std::codecvt_utf8_utf16<uint16_t>, uint16_t> convert;
    return convert.to_bytes((uint16_t*)str.data(), (uint16_t*)str.data() + str.size());
#else
    std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> convert;
    return convert.to_bytes(str);
#endif
}

std::u32string Encoding::UTF16toUTF32(const std::u16string& str)
{
    std::string bytes;
    bytes.reserve(str.size() * 2);

    for (const char16_t ch : str)
    {
        bytes.push_back((uint8_t)(ch / 256));
        bytes.push_back((uint8_t)(ch % 256));
    }

#if defined(_MSC_VER) && (_MSC_VER == 1900)
    std::wstring_convert<std::codecvt_utf16<uint32_t>, uint32_t> convert;
    auto tmp = convert.from_bytes(bytes);
    return std::u32string(tmp.data(), tmp.data() + tmp.size());
#else
    std::wstring_convert<std::codecvt_utf16<char32_t>, char32_t> convert;
    return convert.from_bytes(bytes);
#endif
}

std::string Encoding::UTF32toUTF8(const std::u32string& str)
{
#if defined(_MSC_VER) && (_MSC_VER == 1900)
    std::wstring_convert<std::codecvt_utf8<uint32_t>, uint32_t> convert;
    return convert.to_bytes((uint32_t*)str.data(), (uint32_t*)str.data() + str.size());
#else
    std::wstring_convert<std::codecvt_utf8<char32_t>, char32_t> convert;
    return convert.to_bytes(str);
#endif
}

std::u16string Encoding::UTF32toUTF16(const std::u32string& str)
{
#if defined(_MSC_VER) && (_MSC_VER == 1900)
    std::wstring_convert<std::codecvt_utf16<uint32_t>, uint32_t> convert;
    std::string bytes = convert.to_bytes((uint32_t*)str.data(), (uint32_t*)str.data() + str.size());
#else
    std::wstring_convert<std::codecvt_utf16<char32_t>, char32_t> convert;
    std::string bytes = convert.to_bytes(str);
#endif

    std::u16string result;
    result.reserve(bytes.size() / 2);

    for (size_t i = 0; i < bytes.size(); i += 2)
        result.push_back((char16_t)((uint8_t)(bytes[i]) * 256 + (uint8_t)(bytes[i + 1])));

    return result;
}

} // namespace CppCommon
<commit_msg>Fix Cygwin build<commit_after>/*!
    \file encoding.cpp
    \brief Encoding utilities implementation
    \author Ivan Shynkarenka
    \date 12.08.2016
    \copyright MIT License
*/

#include "string/encoding.h"

#include <codecvt>
#include <locale>

namespace CppCommon {

std::string Encoding::ToUTF8(const std::wstring& wstr)
{
#if defined(__CYGWIN__)
    std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> convert;
    return convert.to_bytes((char16_t*)wstr.data(), (char16_t*)wstr.data() + wstr.size());
#elif defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
    std::wstring_convert<std::codecvt_utf8<wchar_t>> convert;
    return convert.to_bytes(wstr);
#elif defined(_WIN32) || defined(_WIN64)
    std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> convert;
    return convert.to_bytes(wstr);
#endif
}

std::wstring Encoding::FromUTF8(const std::string& str)
{
#if defined(__CYGWIN__)
    std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> convert;
    auto tmp = convert.from_bytes(str);
    return std::wstring(tmp.data(), tmp.data() + tmp.size());
#elif defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
    std::wstring_convert<std::codecvt_utf8<wchar_t>> convert;
    return convert.from_bytes(str);
#elif defined(_WIN32) || defined(_WIN64)
    std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> convert;
    return convert.from_bytes(str);
#endif
}

std::u16string Encoding::UTF8toUTF16(const std::string& str)
{
#if defined(_MSC_VER) && (_MSC_VER == 1900)
    std::wstring_convert<std::codecvt_utf8_utf16<uint16_t>, uint16_t> convert;
    auto tmp = convert.from_bytes(str);
    return std::u16string(tmp.data(), tmp.data() + tmp.size());
#else
    std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> convert;
    return convert.from_bytes(str);
#endif
}

std::u32string Encoding::UTF8toUTF32(const std::string& str)
{
#if defined(_MSC_VER) && (_MSC_VER == 1900)
    std::wstring_convert<std::codecvt_utf8<uint32_t>, uint32_t> convert;
    auto tmp = convert.from_bytes(str);
    return std::u32string(tmp.data(), tmp.data() + tmp.size());
#else
    std::wstring_convert<std::codecvt_utf8<char32_t>, char32_t> convert;
    return convert.from_bytes(str);
#endif
}

std::string Encoding::UTF16toUTF8(const std::u16string& str)
{
#if defined(_MSC_VER) && (_MSC_VER == 1900)
    std::wstring_convert<std::codecvt_utf8_utf16<uint16_t>, uint16_t> convert;
    return convert.to_bytes((uint16_t*)str.data(), (uint16_t*)str.data() + str.size());
#else
    std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> convert;
    return convert.to_bytes(str);
#endif
}

std::u32string Encoding::UTF16toUTF32(const std::u16string& str)
{
    std::string bytes;
    bytes.reserve(str.size() * 2);

    for (const char16_t ch : str)
    {
        bytes.push_back((uint8_t)(ch / 256));
        bytes.push_back((uint8_t)(ch % 256));
    }

#if defined(_MSC_VER) && (_MSC_VER == 1900)
    std::wstring_convert<std::codecvt_utf16<uint32_t>, uint32_t> convert;
    auto tmp = convert.from_bytes(bytes);
    return std::u32string(tmp.data(), tmp.data() + tmp.size());
#else
    std::wstring_convert<std::codecvt_utf16<char32_t>, char32_t> convert;
    return convert.from_bytes(bytes);
#endif
}

std::string Encoding::UTF32toUTF8(const std::u32string& str)
{
#if defined(_MSC_VER) && (_MSC_VER == 1900)
    std::wstring_convert<std::codecvt_utf8<uint32_t>, uint32_t> convert;
    return convert.to_bytes((uint32_t*)str.data(), (uint32_t*)str.data() + str.size());
#else
    std::wstring_convert<std::codecvt_utf8<char32_t>, char32_t> convert;
    return convert.to_bytes(str);
#endif
}

std::u16string Encoding::UTF32toUTF16(const std::u32string& str)
{
#if defined(_MSC_VER) && (_MSC_VER == 1900)
    std::wstring_convert<std::codecvt_utf16<uint32_t>, uint32_t> convert;
    std::string bytes = convert.to_bytes((uint32_t*)str.data(), (uint32_t*)str.data() + str.size());
#else
    std::wstring_convert<std::codecvt_utf16<char32_t>, char32_t> convert;
    std::string bytes = convert.to_bytes(str);
#endif

    std::u16string result;
    result.reserve(bytes.size() / 2);

    for (size_t i = 0; i < bytes.size(); i += 2)
        result.push_back((char16_t)((uint8_t)(bytes[i]) * 256 + (uint8_t)(bytes[i + 1])));

    return result;
}

} // namespace CppCommon
<|endoftext|>


<commit_before>// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you 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 <mutex>
#include <vector>

#include <openssl/err.h>
#include <openssl/rand.h>
#include <openssl/ssl.h>
#include <openssl/x509v3.h>

#include "kudu/util/debug/leakcheck_disabler.h"
#include "kudu/util/mutex.h"
#include "kudu/util/thread.h"
#include "kudu/util/net/ssl_factory.h"
#include "kudu/util/net/ssl_socket.h"

namespace kudu {

// These non-POD elements will be alive for the lifetime of the process, so don't allocate in
// static storage.
static std::vector<Mutex*> ssl_mutexes;

// Lock/Unlock the nth lock. Only to be used by OpenSSL.
static void CryptoLockingCallback(int mode, int n, const char* /*unused*/, int /*unused*/) {
  if (mode & CRYPTO_LOCK) {
    ssl_mutexes[n]->Acquire();
  } else {
    ssl_mutexes[n]->Release();
  }
}

// Return the current pthread's tid. Only to be used by OpenSSL.
static void CryptoThreadIDCallback(CRYPTO_THREADID* id) {
  return CRYPTO_THREADID_set_numeric(id, Thread::UniqueThreadId());
}

void DoSSLInit() {
  SSL_library_init();
  SSL_load_error_strings();
  OpenSSL_add_all_algorithms();
  RAND_poll();

  for (int i = 0; i < CRYPTO_num_locks(); ++i) {
    debug::ScopedLeakCheckDisabler d;
    ssl_mutexes.push_back(new Mutex());
  }

  // Callbacks used by OpenSSL required in a multi-threaded setting.
  CRYPTO_set_locking_callback(CryptoLockingCallback);
  CRYPTO_THREADID_set_callback(CryptoThreadIDCallback);
}

SSLFactory::SSLFactory() : ctx_(nullptr, SSL_CTX_free) {
  static std::once_flag ssl_once;
  std::call_once(ssl_once, DoSSLInit);
}

SSLFactory::~SSLFactory() {
}

Status SSLFactory::Init() {
  CHECK(!ctx_.get());
  // NOTE: 'SSLv23 method' sounds like it would enable only SSLv2 and SSLv3, but in fact
  // this is a sort of wildcard which enables all methods (including TLSv1 and later).
  // We explicitly disable SSLv2 and SSLv3 below so that only TLS methods remain.
  // See the discussion on https://trac.torproject.org/projects/tor/ticket/11598 for more
  // info.
  ctx_.reset(SSL_CTX_new(SSLv23_method()));
  if (!ctx_) {
    return Status::RuntimeError("Could not create SSL context");
  }
  SSL_CTX_set_mode(ctx_.get(), SSL_MODE_AUTO_RETRY);

  // Disable SSLv2 and SSLv3 which are vulnerable to various issues such as POODLE.
  // We support versions back to TLSv1.0 since OpenSSL on RHEL 6.4 and earlier does not
  // not support TLSv1.1 or later.
  SSL_CTX_set_options(ctx_.get(), SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
  SSL_CTX_set_verify(ctx_.get(),
      SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT | SSL_VERIFY_CLIENT_ONCE, nullptr);
  return Status::OK();
}

std::string SSLFactory::GetLastError(int errno_copy) {
  int error_code = ERR_get_error();
  if (error_code == 0) return kudu::ErrnoToString(errno_copy);
  const char* error_reason = ERR_reason_error_string(error_code);
  if (error_reason != NULL) return error_reason;
  return strings::Substitute("SSL error $0", error_code);
}

Status SSLFactory::LoadCertificate(const std::string& certificate_path) {
  ERR_clear_error();
  errno = 0;
  if (SSL_CTX_use_certificate_file(ctx_.get(), certificate_path.c_str(), SSL_FILETYPE_PEM) != 1) {
    return Status::NotFound(
        "Failed to load certificate file '" + certificate_path + "': " + GetLastError(errno));
  }
  return Status::OK();
}

Status SSLFactory::LoadPrivateKey(const std::string& key_path) {
  ERR_clear_error();
  errno = 0;
  if (SSL_CTX_use_PrivateKey_file(ctx_.get(), key_path.c_str(), SSL_FILETYPE_PEM) != 1) {
    return Status::NotFound(
        "Failed to load private key file '" + key_path + "': " + GetLastError(errno));
  }
  return Status::OK();
}

Status SSLFactory::LoadCertificateAuthority(const std::string& certificate_path) {
  ERR_clear_error();
  errno = 0;
  if (SSL_CTX_load_verify_locations(ctx_.get(), certificate_path.c_str(), nullptr) != 1) {
    return Status::NotFound(
        "Failed to load certificate authority file '" + certificate_path + "': " +
            GetLastError(errno));
  }
  return Status::OK();
}

std::unique_ptr<SSLSocket> SSLFactory::CreateSocket(int socket_fd, bool is_server) {
  CHECK(ctx_);
  // Create SSL object and transfer ownership to the SSLSocket object created.
  SSL* ssl = SSL_new(ctx_.get());
  if (ssl == nullptr) {
    return nullptr;
  }
  std::unique_ptr<SSLSocket> socket(new SSLSocket(socket_fd, ssl, is_server));
  return socket;
  //return new SSLSocket(socket_fd, ssl, is_server);
}

} // namespace kudu
<commit_msg>[ssl] disable SSL/TLS compression<commit_after>// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you 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 <mutex>
#include <vector>

#include <openssl/err.h>
#include <openssl/rand.h>
#include <openssl/ssl.h>
#include <openssl/x509v3.h>

#include "kudu/util/debug/leakcheck_disabler.h"
#include "kudu/util/mutex.h"
#include "kudu/util/thread.h"
#include "kudu/util/net/ssl_factory.h"
#include "kudu/util/net/ssl_socket.h"

namespace kudu {

// These non-POD elements will be alive for the lifetime of the process, so don't allocate in
// static storage.
static std::vector<Mutex*> ssl_mutexes;

// Lock/Unlock the nth lock. Only to be used by OpenSSL.
static void CryptoLockingCallback(int mode, int n, const char* /*unused*/, int /*unused*/) {
  if (mode & CRYPTO_LOCK) {
    ssl_mutexes[n]->Acquire();
  } else {
    ssl_mutexes[n]->Release();
  }
}

// Return the current pthread's tid. Only to be used by OpenSSL.
static void CryptoThreadIDCallback(CRYPTO_THREADID* id) {
  return CRYPTO_THREADID_set_numeric(id, Thread::UniqueThreadId());
}

void DoSSLInit() {
  SSL_library_init();
  SSL_load_error_strings();
  OpenSSL_add_all_algorithms();
  RAND_poll();

  for (int i = 0; i < CRYPTO_num_locks(); ++i) {
    debug::ScopedLeakCheckDisabler d;
    ssl_mutexes.push_back(new Mutex());
  }

  // Callbacks used by OpenSSL required in a multi-threaded setting.
  CRYPTO_set_locking_callback(CryptoLockingCallback);
  CRYPTO_THREADID_set_callback(CryptoThreadIDCallback);
}

SSLFactory::SSLFactory() : ctx_(nullptr, SSL_CTX_free) {
  static std::once_flag ssl_once;
  std::call_once(ssl_once, DoSSLInit);
}

SSLFactory::~SSLFactory() {
}

Status SSLFactory::Init() {
  CHECK(!ctx_.get());
  // NOTE: 'SSLv23 method' sounds like it would enable only SSLv2 and SSLv3, but in fact
  // this is a sort of wildcard which enables all methods (including TLSv1 and later).
  // We explicitly disable SSLv2 and SSLv3 below so that only TLS methods remain.
  // See the discussion on https://trac.torproject.org/projects/tor/ticket/11598 for more
  // info.
  ctx_.reset(SSL_CTX_new(SSLv23_method()));
  if (!ctx_) {
    return Status::RuntimeError("Could not create SSL context");
  }
  SSL_CTX_set_mode(ctx_.get(), SSL_MODE_AUTO_RETRY);

  // Disable SSLv2 and SSLv3 which are vulnerable to various issues such as POODLE.
  // We support versions back to TLSv1.0 since OpenSSL on RHEL 6.4 and earlier does not
  // not support TLSv1.1 or later.
  //
  // Disable SSL/TLS compression to free up CPU resources and be less prone
  // to attacks exploiting the compression feature:
  //   https://tools.ietf.org/html/rfc7525#section-3.3
  SSL_CTX_set_options(ctx_.get(),
                      SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 |
                      SSL_OP_NO_COMPRESSION);
  SSL_CTX_set_verify(ctx_.get(),
      SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT | SSL_VERIFY_CLIENT_ONCE, nullptr);
  return Status::OK();
}

std::string SSLFactory::GetLastError(int errno_copy) {
  int error_code = ERR_get_error();
  if (error_code == 0) return kudu::ErrnoToString(errno_copy);
  const char* error_reason = ERR_reason_error_string(error_code);
  if (error_reason != NULL) return error_reason;
  return strings::Substitute("SSL error $0", error_code);
}

Status SSLFactory::LoadCertificate(const std::string& certificate_path) {
  ERR_clear_error();
  errno = 0;
  if (SSL_CTX_use_certificate_file(ctx_.get(), certificate_path.c_str(), SSL_FILETYPE_PEM) != 1) {
    return Status::NotFound(
        "Failed to load certificate file '" + certificate_path + "': " + GetLastError(errno));
  }
  return Status::OK();
}

Status SSLFactory::LoadPrivateKey(const std::string& key_path) {
  ERR_clear_error();
  errno = 0;
  if (SSL_CTX_use_PrivateKey_file(ctx_.get(), key_path.c_str(), SSL_FILETYPE_PEM) != 1) {
    return Status::NotFound(
        "Failed to load private key file '" + key_path + "': " + GetLastError(errno));
  }
  return Status::OK();
}

Status SSLFactory::LoadCertificateAuthority(const std::string& certificate_path) {
  ERR_clear_error();
  errno = 0;
  if (SSL_CTX_load_verify_locations(ctx_.get(), certificate_path.c_str(), nullptr) != 1) {
    return Status::NotFound(
        "Failed to load certificate authority file '" + certificate_path + "': " +
            GetLastError(errno));
  }
  return Status::OK();
}

std::unique_ptr<SSLSocket> SSLFactory::CreateSocket(int socket_fd, bool is_server) {
  CHECK(ctx_);
  // Create SSL object and transfer ownership to the SSLSocket object created.
  SSL* ssl = SSL_new(ctx_.get());
  if (ssl == nullptr) {
    return nullptr;
  }
  std::unique_ptr<SSLSocket> socket(new SSLSocket(socket_fd, ssl, is_server));
  return socket;
  //return new SSLSocket(socket_fd, ssl, is_server);
}

} // namespace kudu
<|endoftext|>


<commit_before>#include "matrix.hpp"

Matrix::Matrix(int length) {
	row = length;
	col = length;

	mas = new int*[row];
	for (int i = 0; i < row; i++) {
		mas[i] = new int[col];
		for (int j = 0; j < col; j++) {
			mas[i][j] = 0;
		}
	}
}

Matrix::Matrix(int r, int c) {
	row = r;
	col = c;

	mas = new int*[row];
	for (int i = 0; i < row; i++) {
		mas[i] = new int[col];
		for (int j = 0; j < col; j++) {
			mas[i][j] = 0;
		}
	}
}

Matrix::~Matrix() {
	for (int i = 0; i < row; i++) {
		delete[] mas[i];
	}

	delete[] mas;
}

Matrix::Matrix(const Matrix&a) {
	row = a.row;
	col = a.col;

	mas = new int*[row];
	for (int i = 0; i < row; i++) {
		mas[i] = new int[col];
		for (int j = 0; j < col; j++) {
			mas[i][j] = a.mas[i][j];
		}
	}
}

void Matrix::fill( const char*file) {
	ifstream fin1(file);
	for (int i = 0; i < row; i++) {
		for (int j = 0; j < col; j++) {
			fin1 >> mas[i][j];
		}
	}
}

void Matrix::show() const {

	for (int i = 0; i < row; i++) {
		for (int j = 0; j < col; j++) {
			cout << mas[i][j] << " ";
		}
		cout << endl;
	}
	cout << endl;
}

Matrix Matrix::operator+(const Matrix& a) const {

	Matrix help(row, col);

	for (int i = 0; i < row; i++) {
		for (int j = 0; j < col; j++) {
			help.mas[i][j] = mas[i][j] + a.mas[i][j];
		}
	}
	return help;
}

Matrix Matrix::operator*(const Matrix& a) const {

	Matrix help(row, col);

	for (int i = 0; i < row; i++) {
		for (int j = 0; j < col; j++) {
			help.mas[i][j] = 0;
			for (int k = 0; k < col; k++) {
				help.mas[i][j] += mas[i][k] * a.mas[k][j];
			}
		}
	}
	return help;
}

int Matrix::rows() {
	return row;
}

int Matrix::columns() {
	return col;
}

int Matrix::Element(int i, int j)
        {
            if (i<row && j<col)
                return mas[i][j];
            else
                cout << "Error: 1";
        }

bool Matrix::operator==(const Matrix& m) const
{
	if (row != m.row || col != m.col)
	{
		return false;
	}
	else
	{
		for (int i = 0; i < row; i++)
		{
			for (int j = 0; j < col; j++)
			{

				if (mas[i][j] != m.mas[i][j])
				{
					return false;
				}
			}
		}
	}
	return true;
}

Matrix Matrix::operator=(const Matrix& a) const
{
	for (int i = 0; i < row; i++)
	{
		for (int j = 0; j < col; j++)
		{
			mas[i][j] = a.mas[i][j];
		}
	}
	return *this;
}

istream& operator >> (std::istream& is, Matrix& a)
{
	for (int i = 0; i < a.row; i++)
	{
		for (int j = 0; j < a.col; j++)
		{
			cout << "mas[" << i << "][" << j << "] = ";
			is >> a.mas[i][j];
			cout << endl;
		}
	}
	return is;
}
ostream& operator << (std::ostream& os, const Matrix& a)
{
	for (int i = 0; i < a.row; i++)
	{
		for (int j = 0; j < a.col; j++)
		{
			os.width(4);
			os << a.mas[i][j] << " ";
		}
		os << '\n';
	}
	os << std::endl;
	return os;
}

<commit_msg>Update matrix.cpp<commit_after>#include "matrix.hpp"

Matrix::Matrix(int length) {
	row = length;
	col = length;

	mas = new int*[row];
	for (int i = 0; i < row; i++) {
		mas[i] = new int[col];
		for (int j = 0; j < col; j++) {
			mas[i][j] = 0;
		}
	}
}

Matrix::Matrix(int r, int c) {
	row = r;
	col = c;

	mas = new int*[row];
	for (int i = 0; i < row; i++) {
		mas[i] = new int[col];
		for (int j = 0; j < col; j++) {
			mas[i][j] = 0;
		}
	}
}

Matrix::~Matrix() {
	for (int i = 0; i < row; i++) {
		delete[] mas[i];
	}

	delete[] mas;
}

Matrix::Matrix(const Matrix&a) {
	row = a.row;
	col = a.col;

	mas = new int*[row];
	for (int i = 0; i < row; i++) {
		mas[i] = new int[col];
		for (int j = 0; j < col; j++) {
			mas[i][j] = a.mas[i][j];
		}
	}
}

void Matrix::fill( const char*file) {
	ifstream fin1(file);
	for (int i = 0; i < row; i++) {
		for (int j = 0; j < col; j++) {
			fin1 >> mas[i][j];
		}
	}
}

void Matrix::show() const {

	for (int i = 0; i < row; i++) {
		for (int j = 0; j < col; j++) {
			cout << mas[i][j] << " ";
		}
		cout << endl;
	}
	cout << endl;
}

Matrix Matrix::operator+(const Matrix& a) const {

	Matrix help(row, a.col);

	for (int i = 0; i < row; i++) {
		for (int j = 0; j < col; j++) {
			help.mas[i][j] = mas[i][j] + a.mas[i][j];
		}
	}
	return help;
}

Matrix Matrix::operator*(const Matrix& a) const {

	Matrix help(row, col);

	for (int i = 0; i < row; i++) {
		for (int j = 0; j < col; j++) {
			help.mas[i][j] = 0;
			for (int k = 0; k < col; k++) {
				help.mas[i][j] += mas[i][k] * a.mas[k][j];
			}
		}
	}
	return help;
}

int Matrix::rows() const {
	return row;
}

int Matrix::columns() const {
	return col;
}

int Matrix::Element(int i, int j) const
        {
            if (i<row && j<col)
                return mas[i][j];
            else
                cout << "Error: 1";
        }

bool Matrix::operator==(const Matrix& m) const
{
	if (row != m.row || col != m.col)
	{
		return false;
	}
	else
	{
		for (int i = 0; i < row; i++)
		{
			for (int j = 0; j < col; j++)
			{

				if (mas[i][j] != m.mas[i][j])
				{
					return false;
				}
			}
		}
	}
	return true;
}

Matrix Matrix::operator=(const Matrix& a) const
{
	for (int i = 0; i < row; i++)
	{
		for (int j = 0; j < col; j++)
		{
			mas[i][j] = a.mas[i][j];
		}
	}
	return *this;
}

istream& operator >> (std::istream& is, Matrix& a)
{
	for (int i = 0; i < a.row; i++)
	{
		for (int j = 0; j < a.col; j++)
		{
			cout << "mas[" << i << "][" << j << "] = ";
			is >> a.mas[i][j];
			cout << endl;
		}
	}
	return is;
}
ostream& operator << (std::ostream& os, const Matrix& a)
{
	for (int i = 0; i < a.row; i++)
	{
		for (int j = 0; j < a.col; j++)
		{
			os.width(4);
			os << a.mas[i][j] << " ";
		}
		os << '\n';
	}
	os << std::endl;
	return os;
}

<|endoftext|>


<commit_before>// http://msdn.microsoft.com/en-us/library/aa365968(VS.85).aspx

// Targets: Windows
// return vector<string> of name servers from /etc/resolv.conf

//
// Link with IPHlpAPI.lib
//
#include <winsock2.h>
#include <iphlpapi.h>
#include <stdio.h>
#include <windows.h>
#pragma comment(lib, "IPHLPAPI.lib")

#define MALLOC(x) HeapAlloc(GetProcessHeap(), 0, (x))
#define FREE(x) HeapFree(GetProcessHeap(), 0, (x))

/* Note: could also use malloc() and free() */

int __cdecl main()
{

  FIXED_INFO *pFixedInfo;
  ULONG ulOutBufLen;
  DWORD dwRetVal;
  IP_ADDR_STRING *pIPAddr;

  pFixedInfo = (FIXED_INFO *) MALLOC(sizeof (FIXED_INFO));
  ulOutBufLen = sizeof (FIXED_INFO);

  GetNetworkParams(pFixedInfo, &ulOutBufLen);

  printf("DNS Servers:\n");
  printf("\t%s\n", pFixedInfo->DnsServerList.IpAddress.String);

  pIPAddr = pFixedInfo->DnsServerList.Next;
  while (pIPAddr) {
    printf("\t%s\n", pIPAddr->IpAddress.String);
    pIPAddr = pIPAddr->Next;
  }

  FREE(pFixedInfo);
  return 0;
}<commit_msg>renamed to -win<commit_after><|endoftext|>


<commit_before>#include <xcb/xcb_icccm.h>

#include "utils/math.hpp"
#include "x11/atoms.hpp"
#include "x11/window.hpp"
#include "x11/xutils.hpp"

#include "components/types.hpp"

LEMONBUDDY_NS

window window::create_checked(int16_t x, int16_t y, uint16_t w, uint16_t h, uint32_t mask, const xcb_params_cw_t* p) {
  if (*this == XCB_NONE) {
    resource(connection(), connection().generate_id());
  }

  auto root{connection().screen()->root};
  auto copy{XCB_COPY_FROM_PARENT};
  uint32_t values[16]{0};
  xutils::pack_values(mask, p, values);
  connection().create_window_checked(copy, *this, root, x, y, w, h, 0, copy, copy, mask, values);

  return *this;
}

window window::reconfigure_geom(uint16_t w, uint16_t h, int16_t x, int16_t y) {
  uint32_t mask{0};
  uint32_t values[7]{0};

  xcb_params_configure_window_t params;
  XCB_AUX_ADD_PARAM(&mask, &params, width, w);
  XCB_AUX_ADD_PARAM(&mask, &params, height, h);
  XCB_AUX_ADD_PARAM(&mask, &params, x, x);
  XCB_AUX_ADD_PARAM(&mask, &params, y, y);

  xutils::pack_values(mask, &params, values);
  connection().configure_window_checked(*this, mask, values);

  return *this;
}

window window::reconfigure_pos(int16_t x, int16_t y) {
  uint32_t mask{0};
  uint32_t values[2]{0};

  xcb_params_configure_window_t params;
  XCB_AUX_ADD_PARAM(&mask, &params, x, x);
  XCB_AUX_ADD_PARAM(&mask, &params, y, y);

  xutils::pack_values(mask, &params, values);
  connection().configure_window_checked(*this, mask, values);

  return *this;
}

window window::reconfigure_struts(uint16_t w, uint16_t h, int16_t x, bool bottom) {
  auto& conn = connection();

  uint32_t none{0};
  uint32_t values[12]{none};

  if (bottom) {
    values[static_cast<int>(strut::BOTTOM)] = h;
    values[static_cast<int>(strut::BOTTOM_START_X)] = x;
    values[static_cast<int>(strut::BOTTOM_END_X)] = x + w;
  } else {
    values[static_cast<int>(strut::TOP)] = h;
    values[static_cast<int>(strut::TOP_START_X)] = x;
    values[static_cast<int>(strut::TOP_END_X)] = x + w;
  }

  conn.change_property_checked(XCB_PROP_MODE_REPLACE, *this, _NET_WM_STRUT, XCB_ATOM_CARDINAL, 32, 4, values);
  conn.change_property_checked(XCB_PROP_MODE_REPLACE, *this, _NET_WM_STRUT_PARTIAL, XCB_ATOM_CARDINAL, 32, 12, values);

  return *this;
}

void window::redraw() {
  xutils::visibility_notify(connection(), *this, XCB_VISIBILITY_FULLY_OBSCURED);
  xutils::visibility_notify(connection(), *this, XCB_VISIBILITY_UNOBSCURED);
  connection().flush();
}

LEMONBUDDY_NS_END
<commit_msg>fix: Strut end values should be inclusive<commit_after>#include <xcb/xcb_icccm.h>

#include "utils/math.hpp"
#include "x11/atoms.hpp"
#include "x11/window.hpp"
#include "x11/xutils.hpp"

#include "components/types.hpp"

LEMONBUDDY_NS

window window::create_checked(int16_t x, int16_t y, uint16_t w, uint16_t h, uint32_t mask, const xcb_params_cw_t* p) {
  if (*this == XCB_NONE) {
    resource(connection(), connection().generate_id());
  }

  auto root{connection().screen()->root};
  auto copy{XCB_COPY_FROM_PARENT};
  uint32_t values[16]{0};
  xutils::pack_values(mask, p, values);
  connection().create_window_checked(copy, *this, root, x, y, w, h, 0, copy, copy, mask, values);

  return *this;
}

window window::reconfigure_geom(uint16_t w, uint16_t h, int16_t x, int16_t y) {
  uint32_t mask{0};
  uint32_t values[7]{0};

  xcb_params_configure_window_t params;
  XCB_AUX_ADD_PARAM(&mask, &params, width, w);
  XCB_AUX_ADD_PARAM(&mask, &params, height, h);
  XCB_AUX_ADD_PARAM(&mask, &params, x, x);
  XCB_AUX_ADD_PARAM(&mask, &params, y, y);

  xutils::pack_values(mask, &params, values);
  connection().configure_window_checked(*this, mask, values);

  return *this;
}

window window::reconfigure_pos(int16_t x, int16_t y) {
  uint32_t mask{0};
  uint32_t values[2]{0};

  xcb_params_configure_window_t params;
  XCB_AUX_ADD_PARAM(&mask, &params, x, x);
  XCB_AUX_ADD_PARAM(&mask, &params, y, y);

  xutils::pack_values(mask, &params, values);
  connection().configure_window_checked(*this, mask, values);

  return *this;
}

window window::reconfigure_struts(uint16_t w, uint16_t h, int16_t x, bool bottom) {
  auto& conn = connection();

  uint32_t none{0};
  uint32_t values[12]{none};

  if (bottom) {
    values[static_cast<int>(strut::BOTTOM)] = h;
    values[static_cast<int>(strut::BOTTOM_START_X)] = x;
    values[static_cast<int>(strut::BOTTOM_END_X)] = x + w - 1;
  } else {
    values[static_cast<int>(strut::TOP)] = h;
    values[static_cast<int>(strut::TOP_START_X)] = x;
    values[static_cast<int>(strut::TOP_END_X)] = x + w - 1;
  }

  conn.change_property_checked(XCB_PROP_MODE_REPLACE, *this, _NET_WM_STRUT, XCB_ATOM_CARDINAL, 32, 4, values);
  conn.change_property_checked(XCB_PROP_MODE_REPLACE, *this, _NET_WM_STRUT_PARTIAL, XCB_ATOM_CARDINAL, 32, 12, values);

  return *this;
}

void window::redraw() {
  xutils::visibility_notify(connection(), *this, XCB_VISIBILITY_FULLY_OBSCURED);
  xutils::visibility_notify(connection(), *this, XCB_VISIBILITY_UNOBSCURED);
  connection().flush();
}

LEMONBUDDY_NS_END
<|endoftext|>


<commit_before>#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <syslog.h>
#include <string.h>

#include <algorithm>
#include <fstream>
#include <iostream>
#include <limits>
#include <map>
#include <sstream>
#include <streambuf>
#include <string>
#include <vector>

#include <fuse.h>

using namespace std;

static const char image_path[] = "/sparsebundle.dmg";

struct sparsebundle_data {
    char *path;
    off_t band_size;
    off_t size;
    off_t times_opened;
};

#define SB_DATA_CAST(ptr) ((struct sparsebundle_data *) ptr)
#define SB_DATA (SB_DATA_CAST(fuse_get_context()->private_data))

static int sparsebundle_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
              off_t offset, struct fuse_file_info *fi)
{
    if (strcmp(path, "/") != 0)
        return -ENOENT;

    filler(buf, ".", 0, 0);
    filler(buf, "..", 0, 0);
    filler(buf, image_path + 1, 0, 0);

    return 0;
}

static int sparsebundle_getattr(const char *path, struct stat *stbuf)
{
    memset(stbuf, 0, sizeof(struct stat));

    if (strcmp(path, "/") == 0) {
        stbuf->st_mode = S_IFDIR | 0755;
        stbuf->st_nlink = 3;
    } else if (strcmp(path, image_path) == 0) {
        stbuf->st_mode = S_IFREG | 0444;
        stbuf->st_nlink = 1;
        stbuf->st_size = SB_DATA->size;
    } else
        return -ENOENT;

    return 0;
}

static int sparsebundle_open(const char *path, struct fuse_file_info *fi)
{
    if (strcmp(path, image_path) != 0)
        return -ENOENT;

    if ((fi->flags & O_ACCMODE) != O_RDONLY)
        return -EACCES;

    SB_DATA->times_opened++;
    syslog(LOG_DEBUG, "opened %s, now referenced %llx times",
        SB_DATA->path, SB_DATA->times_opened);

    return 0;
}

static int sparsebundle_read(const char *path, char *buffer, size_t length, off_t offset,
           struct fuse_file_info *fi)
{
    if (strcmp(path, image_path) != 0)
        return -ENOENT;

    if (offset >= SB_DATA->size)
        return 0;

    if (offset + length > SB_DATA->size)
        length = SB_DATA->size - offset;

    syslog(LOG_DEBUG, "asked to read %zu bytes at offset %llu", length, offset);

    size_t bytes_read = 0;
    while (bytes_read < length) {
        off_t band_number = (offset + bytes_read) / SB_DATA->band_size;
        off_t band_offset = (offset + bytes_read) % SB_DATA->band_size;

        ssize_t to_read = min(static_cast<off_t>(length - bytes_read),
            SB_DATA->band_size - band_offset);

        char *band_name;
        if (asprintf(&band_name, "%s/bands/%llx", SB_DATA->path, band_number) == -1) {
            syslog(LOG_ERR, "failed to resolve band name");
            return -1;
        }

        syslog(LOG_DEBUG, "reading %zu bytes from band %llx at offset %llu",
            to_read, band_number, band_offset);

        ssize_t read = 0;
        int band_file = open(band_name, O_RDONLY);
        if (band_file != -1) {
            read = pread(band_file, buffer + bytes_read, to_read, band_offset);
            close(band_file);

            if (read == -1) {
                syslog(LOG_ERR, "failed to read band: %s", strerror(errno));
                free(band_name);
                return -1;
            }
        } else if (errno != ENOENT) {
            syslog(LOG_ERR, "failed to open band %s: %s", band_name, strerror(errno));
            free(band_name);
            return -1;
        }

        free(band_name);

        if (read < to_read) {
            syslog(LOG_DEBUG, "missing %zu bytes from band %llx, padding with zeroes",
                to_read - read, band_number);
            memset(buffer + bytes_read + read, 0, to_read - read);
        }

        bytes_read += to_read;

        syslog(LOG_DEBUG, "done reading from band %llx, %zu bytes left to read",
                band_number, length - bytes_read);
    }

    assert(bytes_read == length);
    return bytes_read;
}

static int sparsebundle_release(const char *path, struct fuse_file_info *fi)
{
    SB_DATA->times_opened--;
    syslog(LOG_DEBUG, "closed %s, now referenced %llx times",
        SB_DATA->path, SB_DATA->times_opened);

    if (SB_DATA->times_opened == 0) {
        syslog(LOG_DEBUG, "no more references to sparsebundle, cleaning up");
    }

    return 0;
}

static int sparsebundle_show_usage(char *program_name)
{
    fprintf(stderr, "usage: %s [-o options] [-f] [-D] <sparsebundle> <mountpoint>\n", program_name);
    return 1;
}

enum { SPARSEBUNDLE_OPT_DEBUG };

static int sparsebundle_opt_proc(void *data, const char *arg, int key, struct fuse_args *outargs)
{
    switch (key) {
    case SPARSEBUNDLE_OPT_DEBUG:
        setlogmask(LOG_UPTO(LOG_DEBUG));
        return 0;
    case FUSE_OPT_KEY_NONOPT:
        if (SB_DATA_CAST(data)->path)
            return 1;

        SB_DATA_CAST(data)->path = strdup(arg);
        return 0;
    }

    return 1;
}

static off_t read_size(const string &str)
{
    uintmax_t value = strtoumax(str.c_str(), 0, 10);
    if (errno == ERANGE || value > static_cast<uintmax_t>(numeric_limits<off_t>::max())) {
        fprintf(stderr, "Disk image too large to be mounted (%s bytes)\n", str.c_str());
        exit(-1);
    }

    return value;
}

int main(int argc, char **argv)
{
    openlog("sparsebundlefs", LOG_CONS | LOG_PERROR, LOG_USER);
    setlogmask(~(LOG_MASK(LOG_DEBUG)));

    struct sparsebundle_data data = {};

    static struct fuse_opt sparsebundle_options[] = {
        FUSE_OPT_KEY("-D",  SPARSEBUNDLE_OPT_DEBUG), FUSE_OPT_END
    };

    struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
    fuse_opt_parse(&args, &data, sparsebundle_options, sparsebundle_opt_proc);
    fuse_opt_add_arg(&args, "-oro"); // Force read-only mount

    if (!data.path)
        return sparsebundle_show_usage(argv[0]);

    char *abs_path = realpath(data.path, 0);
    if (!abs_path) {
        perror("Could not resolve absolute path");
        return -1;
    }

    free(data.path);
    data.path = abs_path;

    char *plist_path;
    if (asprintf(&plist_path, "%s/Info.plist", data.path) == -1) {
        perror("Failed to resolve Info.plist path");
        return -1;
    }

    ifstream plist_file(plist_path);
    stringstream plist_data;
    plist_data << plist_file.rdbuf();

    string key, line;
    while (getline(plist_data, line)) {
        static const char whitespace_chars[] = " \n\r\t";
        line.erase(0, line.find_first_not_of(whitespace_chars));
        line.erase(line.find_last_not_of(whitespace_chars) + 1);

        if (line.compare(0, 5, "<key>") == 0) {
            key = line.substr(5, line.length() - 11);
        } else if (!key.empty()) {
            line.erase(0, line.find_first_of('>') + 1);
            line.erase(line.find_first_of('<'));

            if (key == "band-size")
                data.band_size = read_size(line);
            else if (key == "size")
                data.size = read_size(line);

            key.clear();
        }
    }

    syslog(LOG_DEBUG, "initialized %s, band size %llu, total size %llu",
        data.path, data.band_size, data.size);

    struct fuse_operations sparsebundle_filesystem_operations = {};
    sparsebundle_filesystem_operations.getattr = sparsebundle_getattr;
    sparsebundle_filesystem_operations.open = sparsebundle_open;
    sparsebundle_filesystem_operations.read = sparsebundle_read;
    sparsebundle_filesystem_operations.readdir = sparsebundle_readdir;
    sparsebundle_filesystem_operations.release = sparsebundle_release;

    return fuse_main(args.argc, args.argv, &sparsebundle_filesystem_operations, &data);
}
<commit_msg>Clean up use of error return codes<commit_after>#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <syslog.h>
#include <string.h>

#include <algorithm>
#include <fstream>
#include <iostream>
#include <limits>
#include <map>
#include <sstream>
#include <streambuf>
#include <string>
#include <vector>

#include <fuse.h>

using namespace std;

static const char image_path[] = "/sparsebundle.dmg";

struct sparsebundle_data {
    char *path;
    off_t band_size;
    off_t size;
    off_t times_opened;
};

#define SB_DATA_CAST(ptr) ((struct sparsebundle_data *) ptr)
#define SB_DATA (SB_DATA_CAST(fuse_get_context()->private_data))

static int sparsebundle_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
              off_t offset, struct fuse_file_info *fi)
{
    if (strcmp(path, "/") != 0)
        return -ENOENT;

    filler(buf, ".", 0, 0);
    filler(buf, "..", 0, 0);
    filler(buf, image_path + 1, 0, 0);

    return 0;
}

static int sparsebundle_getattr(const char *path, struct stat *stbuf)
{
    memset(stbuf, 0, sizeof(struct stat));

    if (strcmp(path, "/") == 0) {
        stbuf->st_mode = S_IFDIR | 0755;
        stbuf->st_nlink = 3;
    } else if (strcmp(path, image_path) == 0) {
        stbuf->st_mode = S_IFREG | 0444;
        stbuf->st_nlink = 1;
        stbuf->st_size = SB_DATA->size;
    } else
        return -ENOENT;

    return 0;
}

static int sparsebundle_open(const char *path, struct fuse_file_info *fi)
{
    if (strcmp(path, image_path) != 0)
        return -ENOENT;

    if ((fi->flags & O_ACCMODE) != O_RDONLY)
        return -EACCES;

    SB_DATA->times_opened++;
    syslog(LOG_DEBUG, "opened %s, now referenced %llx times",
        SB_DATA->path, SB_DATA->times_opened);

    return 0;
}

static int sparsebundle_read(const char *path, char *buffer, size_t length, off_t offset,
           struct fuse_file_info *fi)
{
    if (strcmp(path, image_path) != 0)
        return -ENOENT;

    if (offset >= SB_DATA->size)
        return 0;

    if (offset + length > SB_DATA->size)
        length = SB_DATA->size - offset;

    syslog(LOG_DEBUG, "asked to read %zu bytes at offset %llu", length, offset);

    size_t bytes_read = 0;
    while (bytes_read < length) {
        off_t band_number = (offset + bytes_read) / SB_DATA->band_size;
        off_t band_offset = (offset + bytes_read) % SB_DATA->band_size;

        ssize_t to_read = min(static_cast<off_t>(length - bytes_read),
            SB_DATA->band_size - band_offset);

        char *band_name;
        if (asprintf(&band_name, "%s/bands/%llx", SB_DATA->path, band_number) == -1) {
            syslog(LOG_ERR, "failed to resolve band name");
            return -errno;
        }

        syslog(LOG_DEBUG, "reading %zu bytes from band %llx at offset %llu",
            to_read, band_number, band_offset);

        ssize_t read = 0;
        int band_file = open(band_name, O_RDONLY);
        if (band_file != -1) {
            read = pread(band_file, buffer + bytes_read, to_read, band_offset);
            close(band_file);

            if (read == -1) {
                syslog(LOG_ERR, "failed to read band: %s", strerror(errno));
                free(band_name);
                return -errno;
            }
        } else if (errno != ENOENT) {
            syslog(LOG_ERR, "failed to open band %s: %s", band_name, strerror(errno));
            free(band_name);
            return -errno;
        }

        free(band_name);

        if (read < to_read) {
            syslog(LOG_DEBUG, "missing %zu bytes from band %llx, padding with zeroes",
                to_read - read, band_number);
            memset(buffer + bytes_read + read, 0, to_read - read);
        }

        bytes_read += to_read;

        syslog(LOG_DEBUG, "done reading from band %llx, %zu bytes left to read",
                band_number, length - bytes_read);
    }

    assert(bytes_read == length);
    return bytes_read;
}

static int sparsebundle_release(const char *path, struct fuse_file_info *fi)
{
    SB_DATA->times_opened--;
    syslog(LOG_DEBUG, "closed %s, now referenced %llx times",
        SB_DATA->path, SB_DATA->times_opened);

    if (SB_DATA->times_opened == 0) {
        syslog(LOG_DEBUG, "no more references to sparsebundle, cleaning up");
    }

    return 0;
}

static int sparsebundle_show_usage(char *program_name)
{
    fprintf(stderr, "usage: %s [-o options] [-f] [-D] <sparsebundle> <mountpoint>\n", program_name);
    return 1;
}

enum { SPARSEBUNDLE_OPT_DEBUG };

static int sparsebundle_opt_proc(void *data, const char *arg, int key, struct fuse_args *outargs)
{
    switch (key) {
    case SPARSEBUNDLE_OPT_DEBUG:
        setlogmask(LOG_UPTO(LOG_DEBUG));
        return 0;
    case FUSE_OPT_KEY_NONOPT:
        if (SB_DATA_CAST(data)->path)
            return 1;

        SB_DATA_CAST(data)->path = strdup(arg);
        return 0;
    }

    return 1;
}

static off_t read_size(const string &str)
{
    uintmax_t value = strtoumax(str.c_str(), 0, 10);
    if (errno == ERANGE || value > static_cast<uintmax_t>(numeric_limits<off_t>::max())) {
        fprintf(stderr, "Disk image too large to be mounted (%s bytes)\n", str.c_str());
        exit(EXIT_FAILURE);
    }

    return value;
}

int main(int argc, char **argv)
{
    openlog("sparsebundlefs", LOG_CONS | LOG_PERROR, LOG_USER);
    setlogmask(~(LOG_MASK(LOG_DEBUG)));

    struct sparsebundle_data data = {};

    static struct fuse_opt sparsebundle_options[] = {
        FUSE_OPT_KEY("-D",  SPARSEBUNDLE_OPT_DEBUG), FUSE_OPT_END
    };

    struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
    fuse_opt_parse(&args, &data, sparsebundle_options, sparsebundle_opt_proc);
    fuse_opt_add_arg(&args, "-oro"); // Force read-only mount

    if (!data.path)
        return sparsebundle_show_usage(argv[0]);

    char *abs_path = realpath(data.path, 0);
    if (!abs_path) {
        perror("Could not resolve absolute path");
        return EXIT_FAILURE;
    }

    free(data.path);
    data.path = abs_path;

    char *plist_path;
    if (asprintf(&plist_path, "%s/Info.plist", data.path) == -1) {
        perror("Failed to resolve Info.plist path");
        return EXIT_FAILURE;
    }

    ifstream plist_file(plist_path);
    stringstream plist_data;
    plist_data << plist_file.rdbuf();

    string key, line;
    while (getline(plist_data, line)) {
        static const char whitespace_chars[] = " \n\r\t";
        line.erase(0, line.find_first_not_of(whitespace_chars));
        line.erase(line.find_last_not_of(whitespace_chars) + 1);

        if (line.compare(0, 5, "<key>") == 0) {
            key = line.substr(5, line.length() - 11);
        } else if (!key.empty()) {
            line.erase(0, line.find_first_of('>') + 1);
            line.erase(line.find_first_of('<'));

            if (key == "band-size")
                data.band_size = read_size(line);
            else if (key == "size")
                data.size = read_size(line);

            key.clear();
        }
    }

    syslog(LOG_DEBUG, "initialized %s, band size %llu, total size %llu",
        data.path, data.band_size, data.size);

    struct fuse_operations sparsebundle_filesystem_operations = {};
    sparsebundle_filesystem_operations.getattr = sparsebundle_getattr;
    sparsebundle_filesystem_operations.open = sparsebundle_open;
    sparsebundle_filesystem_operations.read = sparsebundle_read;
    sparsebundle_filesystem_operations.readdir = sparsebundle_readdir;
    sparsebundle_filesystem_operations.release = sparsebundle_release;

    return fuse_main(args.argc, args.argv, &sparsebundle_filesystem_operations, &data);
}
<|endoftext|>


<commit_before>
#include <xmppstream.h>
#include <xmppserver.h>
#include <tagbuilder.h>
#include <nanosoft/base64.h>

// for debug only
#include <string>
#include <iostream>

using namespace std;
using namespace nanosoft;

/**
* Конструктор потока
*/
XMPPStream::XMPPStream(XMPPServer *srv, int sock): AsyncXMLStream(sock), server(srv), XMLWriter(1024), state(init)
{
	depth = 0;
	builder = new ATTagBuilder();
}

/**
* Деструктор потока
*/
XMPPStream::~XMPPStream()
{
	delete builder;
}

/**
* Запись XML
*/
void XMPPStream::onWriteXML(const char *data, size_t len)
{
	int r = write(data, len);
	if ( r != len ) onError("write fault");
	cout << "written: \033[22;34m" << string(data, len) << "\033[0m\n";
}

/**
* Событие готовности к записи
*
* Вызывается, когда в поток готов принять
* данные для записи без блокировки
*/
void XMPPStream::onWrite()
{
	cerr << "not implemented XMPPStream::onWrite()" << endl;
}

/**
* Событие закрытие соединения
*
* Вызывается если peer закрыл соединение
*/
void XMPPStream::onShutdown()
{
	server->onOffline(this);
	AsyncXMLStream::onShutdown();
	XMLWriter::flush();
	cerr << "[TestStream]: peer shutdown connection" << endl;
	if ( shutdown(fd, SHUT_RDWR) != 0 )
	{
		stderror();
	}
	server->daemon->removeObject(this);
	delete this;
}

/**
* Обработчик открытия тега
*/
void XMPPStream::onStartElement(const std::string &name, const attributtes_t &attributes)
{
	depth ++;
	switch ( depth )
	{
	case 1:
		onStartStream(name, attributes);
		break;
	case 2: // начало станзы
		builder->startElement(name, attributes, depth);
	break;
	default: // добавить тег в станзу
		builder->startElement(name, attributes, depth);
	}
}

/**
* Обработчик символьных данных
*/
void XMPPStream::onCharacterData(const std::string &cdata)
{
	builder->characterData(cdata);
}

/**
* Обработчик закрытия тега
*/
void XMPPStream::onEndElement(const std::string &name)
{
	switch (depth)
	{
	case 1:
		onEndStream();
		break;
	case 2: {
		builder->endElement(name);
		Stanza *s = new Stanza(builder->fetchResult());
		onStanza(s);
		delete s; // Внимание — станза удаляется здесь
		break;
	}
	default:
		builder->endElement(name);
	}
	depth --;
}

/**
* Обработчик станз
*/
void XMPPStream::onStanza(Stanza *stanza)
{
	cout << "stanza: " << stanza->tag()->name() << endl;
	if (stanza->tag()->name() == "iq") onIqStanza(stanza);
	else if (stanza->tag()->name() == "auth") onAuthStanza(stanza);
	else if (stanza->tag()->name() == "response" ) onResponseStanza(stanza);
	else if (stanza->tag()->name() == "message") onMessageStanza(stanza);
	else if (stanza->tag()->name() == "presence") onPresenceStanza(stanza);
	else ; // ...
}

/**
* Обработчик авторизации
*/
void XMPPStream::onAuthStanza(Stanza *stanza)
{
	string mechanism = stanza->tag()->getAttribute("mechanism");

	sasl = server->start("xmpp", client_jid.hostname(), mechanism);
	onSASLStep(string());
}

/**
* Обработка этапа авторизации SASL
*/
void XMPPStream::onSASLStep(const std::string &input)
{
	string output;
	switch ( server->step(sasl, input, output) )
	{
	case SASLServer::ok:
		client_jid.setUsername(server->getUsername(sasl));
		server->close(sasl);
		startElement("success");
			setAttribute("xmlns", "urn:ietf:params:xml:ns:xmpp-sasl");
		endElement("success");
		flush();
		resetWriter();
		state = authorized;
		depth = 1; // после выхода из onAuthStanza/onStanza() будет стандартный depth--
		resetParser();
		break;
	case SASLServer::next:
		startElement("challenge");
			setAttribute("xmlns", "urn:ietf:params:xml:ns:xmpp-sasl");
			characterData(base64_encode(output));
		endElement("challenge");
		flush();
		break;
	case SASLServer::error:
		server->close(sasl);
		startElement("failure");
			setAttribute("xmlns", "urn:ietf:params:xml:ns:xmpp-sasl");
			addElement("temporary-auth-failure", "");
		endElement("failure");
		flush();
		break;
	}
}

/**
* Обработчик авторизации: ответ клиента
*/
void XMPPStream::onResponseStanza(Stanza *stanza)
{
	onSASLStep(base64_decode(stanza->tag()->getCharacterData()));
}

/**
* Обработчик iq-станзы
*/
void XMPPStream::onIqStanza(Stanza *stanza)
{
	if(stanza->tag()->hasChild("bind") && (stanza->type() == "set" || stanza->type() == "get")) {
		client_jid.setResource(stanza->type() == "set" ? stanza->tag()->getChild("bind")->getChild("resource")->getCharacterData() : "foo");
		startElement("iq");
			setAttribute("type", "result");
			setAttribute("id", stanza->tag()->getAttribute("id"));
			startElement("bind");
				setAttribute("xmlns", "urn:ietf:params:xml:ns:xmpp-bind");
				startElement("jid");
					characterData(jid().full());
				endElement("jid");
			endElement("bind");
		endElement("iq");
		flush();
	}
	if(stanza->tag()->hasChild("session") && stanza->type() == "set") {
		startElement("iq");
			setAttribute("id", stanza->tag()->getAttribute("id"));
			setAttribute("type", "result");
			startElement("session");
				setAttribute("xmlns", "urn:ietf:params:xml:ns:xmpp-session");
			endElement("session");
		endElement("iq");
		flush();
		server->onOnline(this);
	}
	if(stanza->tag()->hasChild("query") && stanza->type() == "get") {
		// Входящие запросы информации
		std::string query_xmlns = stanza->tag()->getChild("query")->getAttribute("xmlns");
		if(query_xmlns == "jabber:iq:version") {
			// Отправить версию сервера
			// send(Stanza::serverVersion(сервер, stanza->from(), stanza->id()));
		} else if (query_xmlns == "jabber:iq:roster") {
			startElement("iq");
				setAttribute("type", "result");
				setAttribute("id", stanza->tag()->getAttribute("id"));
				startElement("query");
					setAttribute("xmlns", "jabber:iq:roster");
					XMPPServer::users_t users = server->getUserList();
					for(XMPPServer::users_t::iterator pos = users.begin(); pos != users.end(); ++pos)
					{
						startElement("item");
							setAttribute("jid", *pos);
							setAttribute("name", *pos);
							setAttribute("subscription", "both");
							addElement("group", "Friends");
						endElement("item");
					}
				endElement("query");
			endElement("iq");
			flush();
		} else {
		    // Неизвестный запрос
			//Stanza s = Stanza::badRequest(JID(""), stanza->from(), stanza->id());
		    //sendStanza(&s);
		}
	}
}

ClientPresence XMPPStream::presence() {
	return client_presence;
}

void XMPPStream::onMessageStanza(Stanza *stanza) {
	stanza->tag()->insertAttribute("from", jid().full());

	XMPPServer::sessions_t::iterator it;
	XMPPServer::reslist_t reslist;
	XMPPServer::reslist_t::iterator jt;

	it = server->onliners.find(stanza->to().bare());
	if(it != server->onliners.end()) {
		// Проверить, есть ли ресурс, если он указан
		JID to = stanza->to();
		if(to.resource() != "") {
			jt = it->second.find(to.resource());
			if(jt != it->second.end()) {
				jt->second->sendStanza(stanza);
				return;
			}
			// Не отправили на выбранный ресурс, смотрим дальше…
		}
		std::list<XMPPStream *> sendto_list;
		std::list<XMPPStream *>::iterator kt;
		int max_priority = 0;
		for(jt = it->second.begin(); jt != it->second.end(); jt++) {
			if(jt->second->presence().priority == max_priority) {
				sendto_list.push_back(jt->second);
			} else if(jt->second->presence().priority > max_priority) {
				sendto_list.clear();
				sendto_list.push_back(jt->second);
			}
		}
		if(sendto_list.empty()) {
			return;
			cout << "Offline message for: " << stanza->to().bare() << endl;
		}
		for(kt = sendto_list.begin() ; kt != sendto_list.end(); kt++) {
			(*kt)->sendStanza(stanza);
		}
	} else {
		cout << "Offline message for: " << stanza->to().bare() << endl;
	}
}

void XMPPStream::onPresenceStanza(Stanza *stanza)
{
	client_presence.priority = atoi(stanza->tag()->getChildValue("priority", "0").c_str()); // TODO
	client_presence.status_text = stanza->tag()->getChildValue("status", "");

	for(XMPPServer::sessions_t::iterator it = server->onliners.begin(); it != server->onliners.end(); it++) {
		stanza->tag()->insertAttribute("to", it->first);
		for(XMPPServer::reslist_t::iterator jt = it->second.begin(); jt != it->second.end(); jt++) {
			jt->second->sendStanza(stanza);
		}
	}
}

/**
* Событие: начало потока
*/
void XMPPStream::onStartStream(const std::string &name, const attributes_t &attributes)
{
	cout << "new stream" << endl;
	initXML();
	startElement("stream:stream");
	setAttribute("xmlns", "jabber:client");
	setAttribute("xmlns:stream", "http://etherx.jabber.org/streams");
	setAttribute("id", "123456"); // Требования к id — непредсказуемость и уникальность
	setAttribute("from", "localhost");
	setAttribute("version", "1.0");
	setAttribute("xml:lang", "en");

	startElement("stream:features");
	if ( state == init )
	{
		client_jid.setHostname(attributes.find("to")->second);
		startElement("mechanisms");
			setAttribute("xmlns", "urn:ietf:params:xml:ns:xmpp-sasl");
			SASLServer::mechanisms_t list = server->getMechanisms();
			for(SASLServer::mechanisms_t::const_iterator pos = list.begin(); pos != list.end(); ++pos)
			{
				addElement("mechanism", *pos);
			}
		endElement("mechanisms");
		startElement("register");
			setAttribute("xmlns", "http://jabber.org/features/iq-register");
		endElement("register");
	}
	else
	{
		startElement("bind");
			setAttribute("xmlns", "urn:ietf:params:xml:ns:xmpp-bind");
		endElement("bind");
		/*
		startElement("session");
			setAttribute("xmlns", "urn:ietf:params:xml:ns:xmpp-session");
		endElement("session");
		*/
	}
	endElement("stream:features");
	flush();
}

/**
* Событие: конец потока
*/
void XMPPStream::onEndStream()
{
	cerr << "session closed" << endl;
	endElement("stream:stream");
}

/**
* JID потока
*/
JID XMPPStream::jid()
{
	return client_jid;
}

void XMPPStream::sendTag(ATXmlTag * tag) {
	startElement(tag->name());
	attributes_t attributes = tag->getAttributes();
	for(attributes_t::iterator it = attributes.begin(); it != attributes.end(); it++) {
		setAttribute(it->first, it->second);
	}
	nodes_list_t nodes = tag->getChildNodes();
	for(nodes_list_t::iterator it = nodes.begin(); it != nodes.end(); it++) {
		if((*it)->type == TTag) {
			sendTag((*it)->tag);
		} else {
			characterData((*it)->cdata);
		}
	}
	endElement(tag->name());
	// send(tag->asString()); — так будет куда проще…
}

void XMPPStream::sendStanza(Stanza * stanza) {
	sendTag(stanza->tag());
	flush();
}
<commit_msg>Тьфу, return строкой выше нужного написал<commit_after>
#include <xmppstream.h>
#include <xmppserver.h>
#include <tagbuilder.h>
#include <nanosoft/base64.h>

// for debug only
#include <string>
#include <iostream>

using namespace std;
using namespace nanosoft;

/**
* Конструктор потока
*/
XMPPStream::XMPPStream(XMPPServer *srv, int sock): AsyncXMLStream(sock), server(srv), XMLWriter(1024), state(init)
{
	depth = 0;
	builder = new ATTagBuilder();
}

/**
* Деструктор потока
*/
XMPPStream::~XMPPStream()
{
	delete builder;
}

/**
* Запись XML
*/
void XMPPStream::onWriteXML(const char *data, size_t len)
{
	int r = write(data, len);
	if ( r != len ) onError("write fault");
	cout << "written: \033[22;34m" << string(data, len) << "\033[0m\n";
}

/**
* Событие готовности к записи
*
* Вызывается, когда в поток готов принять
* данные для записи без блокировки
*/
void XMPPStream::onWrite()
{
	cerr << "not implemented XMPPStream::onWrite()" << endl;
}

/**
* Событие закрытие соединения
*
* Вызывается если peer закрыл соединение
*/
void XMPPStream::onShutdown()
{
	server->onOffline(this);
	AsyncXMLStream::onShutdown();
	XMLWriter::flush();
	cerr << "[TestStream]: peer shutdown connection" << endl;
	if ( shutdown(fd, SHUT_RDWR) != 0 )
	{
		stderror();
	}
	server->daemon->removeObject(this);
	delete this;
}

/**
* Обработчик открытия тега
*/
void XMPPStream::onStartElement(const std::string &name, const attributtes_t &attributes)
{
	depth ++;
	switch ( depth )
	{
	case 1:
		onStartStream(name, attributes);
		break;
	case 2: // начало станзы
		builder->startElement(name, attributes, depth);
	break;
	default: // добавить тег в станзу
		builder->startElement(name, attributes, depth);
	}
}

/**
* Обработчик символьных данных
*/
void XMPPStream::onCharacterData(const std::string &cdata)
{
	builder->characterData(cdata);
}

/**
* Обработчик закрытия тега
*/
void XMPPStream::onEndElement(const std::string &name)
{
	switch (depth)
	{
	case 1:
		onEndStream();
		break;
	case 2: {
		builder->endElement(name);
		Stanza *s = new Stanza(builder->fetchResult());
		onStanza(s);
		delete s; // Внимание — станза удаляется здесь
		break;
	}
	default:
		builder->endElement(name);
	}
	depth --;
}

/**
* Обработчик станз
*/
void XMPPStream::onStanza(Stanza *stanza)
{
	cout << "stanza: " << stanza->tag()->name() << endl;
	if (stanza->tag()->name() == "iq") onIqStanza(stanza);
	else if (stanza->tag()->name() == "auth") onAuthStanza(stanza);
	else if (stanza->tag()->name() == "response" ) onResponseStanza(stanza);
	else if (stanza->tag()->name() == "message") onMessageStanza(stanza);
	else if (stanza->tag()->name() == "presence") onPresenceStanza(stanza);
	else ; // ...
}

/**
* Обработчик авторизации
*/
void XMPPStream::onAuthStanza(Stanza *stanza)
{
	string mechanism = stanza->tag()->getAttribute("mechanism");

	sasl = server->start("xmpp", client_jid.hostname(), mechanism);
	onSASLStep(string());
}

/**
* Обработка этапа авторизации SASL
*/
void XMPPStream::onSASLStep(const std::string &input)
{
	string output;
	switch ( server->step(sasl, input, output) )
	{
	case SASLServer::ok:
		client_jid.setUsername(server->getUsername(sasl));
		server->close(sasl);
		startElement("success");
			setAttribute("xmlns", "urn:ietf:params:xml:ns:xmpp-sasl");
		endElement("success");
		flush();
		resetWriter();
		state = authorized;
		depth = 1; // после выхода из onAuthStanza/onStanza() будет стандартный depth--
		resetParser();
		break;
	case SASLServer::next:
		startElement("challenge");
			setAttribute("xmlns", "urn:ietf:params:xml:ns:xmpp-sasl");
			characterData(base64_encode(output));
		endElement("challenge");
		flush();
		break;
	case SASLServer::error:
		server->close(sasl);
		startElement("failure");
			setAttribute("xmlns", "urn:ietf:params:xml:ns:xmpp-sasl");
			addElement("temporary-auth-failure", "");
		endElement("failure");
		flush();
		break;
	}
}

/**
* Обработчик авторизации: ответ клиента
*/
void XMPPStream::onResponseStanza(Stanza *stanza)
{
	onSASLStep(base64_decode(stanza->tag()->getCharacterData()));
}

/**
* Обработчик iq-станзы
*/
void XMPPStream::onIqStanza(Stanza *stanza)
{
	if(stanza->tag()->hasChild("bind") && (stanza->type() == "set" || stanza->type() == "get")) {
		client_jid.setResource(stanza->type() == "set" ? stanza->tag()->getChild("bind")->getChild("resource")->getCharacterData() : "foo");
		startElement("iq");
			setAttribute("type", "result");
			setAttribute("id", stanza->tag()->getAttribute("id"));
			startElement("bind");
				setAttribute("xmlns", "urn:ietf:params:xml:ns:xmpp-bind");
				startElement("jid");
					characterData(jid().full());
				endElement("jid");
			endElement("bind");
		endElement("iq");
		flush();
	}
	if(stanza->tag()->hasChild("session") && stanza->type() == "set") {
		startElement("iq");
			setAttribute("id", stanza->tag()->getAttribute("id"));
			setAttribute("type", "result");
			startElement("session");
				setAttribute("xmlns", "urn:ietf:params:xml:ns:xmpp-session");
			endElement("session");
		endElement("iq");
		flush();
		server->onOnline(this);
	}
	if(stanza->tag()->hasChild("query") && stanza->type() == "get") {
		// Входящие запросы информации
		std::string query_xmlns = stanza->tag()->getChild("query")->getAttribute("xmlns");
		if(query_xmlns == "jabber:iq:version") {
			// Отправить версию сервера
			// send(Stanza::serverVersion(сервер, stanza->from(), stanza->id()));
		} else if (query_xmlns == "jabber:iq:roster") {
			startElement("iq");
				setAttribute("type", "result");
				setAttribute("id", stanza->tag()->getAttribute("id"));
				startElement("query");
					setAttribute("xmlns", "jabber:iq:roster");
					XMPPServer::users_t users = server->getUserList();
					for(XMPPServer::users_t::iterator pos = users.begin(); pos != users.end(); ++pos)
					{
						startElement("item");
							setAttribute("jid", *pos);
							setAttribute("name", *pos);
							setAttribute("subscription", "both");
							addElement("group", "Friends");
						endElement("item");
					}
				endElement("query");
			endElement("iq");
			flush();
		} else {
		    // Неизвестный запрос
			//Stanza s = Stanza::badRequest(JID(""), stanza->from(), stanza->id());
		    //sendStanza(&s);
		}
	}
}

ClientPresence XMPPStream::presence() {
	return client_presence;
}

void XMPPStream::onMessageStanza(Stanza *stanza) {
	stanza->tag()->insertAttribute("from", jid().full());

	XMPPServer::sessions_t::iterator it;
	XMPPServer::reslist_t reslist;
	XMPPServer::reslist_t::iterator jt;

	it = server->onliners.find(stanza->to().bare());
	if(it != server->onliners.end()) {
		// Проверить, есть ли ресурс, если он указан
		JID to = stanza->to();
		if(to.resource() != "") {
			jt = it->second.find(to.resource());
			if(jt != it->second.end()) {
				jt->second->sendStanza(stanza);
				return;
			}
			// Не отправили на выбранный ресурс, смотрим дальше…
		}
		std::list<XMPPStream *> sendto_list;
		std::list<XMPPStream *>::iterator kt;
		int max_priority = 0;
		for(jt = it->second.begin(); jt != it->second.end(); jt++) {
			if(jt->second->presence().priority == max_priority) {
				sendto_list.push_back(jt->second);
			} else if(jt->second->presence().priority > max_priority) {
				sendto_list.clear();
				sendto_list.push_back(jt->second);
			}
		}
		if(sendto_list.empty()) {
			cout << "Offline message for: " << stanza->to().bare() << endl;
			return;
		}
		for(kt = sendto_list.begin() ; kt != sendto_list.end(); kt++) {
			(*kt)->sendStanza(stanza);
		}
	} else {
		cout << "Offline message for: " << stanza->to().bare() << endl;
	}
}

void XMPPStream::onPresenceStanza(Stanza *stanza)
{
	client_presence.priority = atoi(stanza->tag()->getChildValue("priority", "0").c_str()); // TODO
	client_presence.status_text = stanza->tag()->getChildValue("status", "");

	for(XMPPServer::sessions_t::iterator it = server->onliners.begin(); it != server->onliners.end(); it++) {
		stanza->tag()->insertAttribute("to", it->first);
		for(XMPPServer::reslist_t::iterator jt = it->second.begin(); jt != it->second.end(); jt++) {
			jt->second->sendStanza(stanza);
		}
	}
}

/**
* Событие: начало потока
*/
void XMPPStream::onStartStream(const std::string &name, const attributes_t &attributes)
{
	cout << "new stream" << endl;
	initXML();
	startElement("stream:stream");
	setAttribute("xmlns", "jabber:client");
	setAttribute("xmlns:stream", "http://etherx.jabber.org/streams");
	setAttribute("id", "123456"); // Требования к id — непредсказуемость и уникальность
	setAttribute("from", "localhost");
	setAttribute("version", "1.0");
	setAttribute("xml:lang", "en");

	startElement("stream:features");
	if ( state == init )
	{
		client_jid.setHostname(attributes.find("to")->second);
		startElement("mechanisms");
			setAttribute("xmlns", "urn:ietf:params:xml:ns:xmpp-sasl");
			SASLServer::mechanisms_t list = server->getMechanisms();
			for(SASLServer::mechanisms_t::const_iterator pos = list.begin(); pos != list.end(); ++pos)
			{
				addElement("mechanism", *pos);
			}
		endElement("mechanisms");
		startElement("register");
			setAttribute("xmlns", "http://jabber.org/features/iq-register");
		endElement("register");
	}
	else
	{
		startElement("bind");
			setAttribute("xmlns", "urn:ietf:params:xml:ns:xmpp-bind");
		endElement("bind");
		/*
		startElement("session");
			setAttribute("xmlns", "urn:ietf:params:xml:ns:xmpp-session");
		endElement("session");
		*/
	}
	endElement("stream:features");
	flush();
}

/**
* Событие: конец потока
*/
void XMPPStream::onEndStream()
{
	cerr << "session closed" << endl;
	endElement("stream:stream");
}

/**
* JID потока
*/
JID XMPPStream::jid()
{
	return client_jid;
}

void XMPPStream::sendTag(ATXmlTag * tag) {
	startElement(tag->name());
	attributes_t attributes = tag->getAttributes();
	for(attributes_t::iterator it = attributes.begin(); it != attributes.end(); it++) {
		setAttribute(it->first, it->second);
	}
	nodes_list_t nodes = tag->getChildNodes();
	for(nodes_list_t::iterator it = nodes.begin(); it != nodes.end(); it++) {
		if((*it)->type == TTag) {
			sendTag((*it)->tag);
		} else {
			characterData((*it)->cdata);
		}
	}
	endElement(tag->name());
	// send(tag->asString()); — так будет куда проще…
}

void XMPPStream::sendStanza(Stanza * stanza) {
	sendTag(stanza->tag());
	flush();
}
<|endoftext|>