<commit_before>/*
    Copyright (C) 2011  <NAME> <<EMAIL>>
    Copyright (C) 2011  <NAME> <<EMAIL>>

    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 Street, Fifth Floor, Boston, MA  02110-1301  USA
*/


#include "contact-request-handler.h"

#include <TelepathyQt4/Connection>
#include <TelepathyQt4/ContactManager>
#include <TelepathyQt4/PendingOperation>
#include <TelepathyQt4/Account>

#include <KTelepathy/error-dictionary.h>

#include <QtCore/QFutureWatcher>

#include <KDebug>
#include <KGlobal>
#include <KAboutData>
#include <KMenu>
#include <KAction>
#include <KStatusNotifierItem>

Q_DECLARE_METATYPE(Tp::ContactPtr)

bool kde_tp_filter_contacts_by_publication_status(const Tp::ContactPtr &contact)
{
    return contact->publishState() == Tp::Contact::PresenceStateAsk;
}

ContactRequestHandler::ContactRequestHandler(const Tp::AccountManagerPtr& am, QObject *parent)
    : QObject(parent)
{
    m_accountManager = am;
    connect(m_accountManager.data(), SIGNAL(newAccount(Tp::AccountPtr)),
            this, SLOT(onNewAccountAdded(Tp::AccountPtr)));

    QList<Tp::AccountPtr> accounts = m_accountManager->allAccounts();

    Q_FOREACH(const Tp::AccountPtr &account, accounts) {
        onNewAccountAdded(account);
    }

    m_noContactsAction = new KAction(i18n("No pending contact requests at the moment"), this);
    m_noContactsAction->setEnabled(false);
}

ContactRequestHandler::~ContactRequestHandler()
{

}

void ContactRequestHandler::onNewAccountAdded(const Tp::AccountPtr& account)
{
    kWarning();
    Q_ASSERT(account->isReady(Tp::Account::FeatureCore));

    if (account->connection()) {
        monitorPresence(account->connection());
    }

    connect(account.data(),
            SIGNAL(connectionChanged(Tp::ConnectionPtr)),
            this, SLOT(onConnectionChanged(Tp::ConnectionPtr)));
}

void ContactRequestHandler::onConnectionChanged(const Tp::ConnectionPtr& connection)
{
    if (!connection.isNull()) {
        monitorPresence(connection);
    }
}

void ContactRequestHandler::monitorPresence(const Tp::ConnectionPtr &connection)
{
    kDebug();
    connect(connection->contactManager().data(), SIGNAL(presencePublicationRequested(Tp::Contacts)),
            this, SLOT(onPresencePublicationRequested(Tp::Contacts)));

    connect(connection->contactManager().data(),
            SIGNAL(stateChanged(Tp::ContactListState)),
            this, SLOT(onContactManagerStateChanged(Tp::ContactListState)));

    onContactManagerStateChanged(connection->contactManager(),
                                 connection->contactManager()->state());
}

void ContactRequestHandler::onContactManagerStateChanged(Tp::ContactListState state)
{
    onContactManagerStateChanged(Tp::ContactManagerPtr(qobject_cast< Tp::ContactManager* >(sender())), state);
}

void ContactRequestHandler::onContactManagerStateChanged(const Tp::ContactManagerPtr &contactManager,
                                                         Tp::ContactListState state)
{
    if (state == Tp::ContactListStateSuccess) {
        QFutureWatcher< Tp::ContactPtr > *watcher = new QFutureWatcher< Tp::ContactPtr >(this);
        connect(watcher, SIGNAL(finished()), this, SLOT(onAccountsPresenceStatusFiltered()));
        watcher->setFuture(QtConcurrent::filtered(contactManager->allKnownContacts(),
                                                  kde_tp_filter_contacts_by_publication_status));

        kDebug() << "Watcher is on";
    } else {
        kDebug() << "Watcher still off, state is" << state << "contactManager is" << contactManager.isNull();
    }
}

void ContactRequestHandler::onAccountsPresenceStatusFiltered()
{
    kDebug() << "Watcher is here";
    QFutureWatcher< Tp::ContactPtr > *watcher = dynamic_cast< QFutureWatcher< Tp::ContactPtr > * >(sender());
    kDebug() << "Watcher is casted";
    Tp::Contacts contacts = watcher->future().results().toSet();
    kDebug() << "Watcher is used";
    if (!contacts.isEmpty()) {
        onPresencePublicationRequested(contacts);
    }
    watcher->deleteLater();
}

void ContactRequestHandler::onPresencePublicationRequested(const Tp::Contacts& contacts)
{
    kDebug() << "New contact requested";

    Q_FOREACH (const Tp::ContactPtr &contact, contacts) {
        Tp::ContactManagerPtr manager = contact->manager();

        if (contact->subscriptionState() == Tp::Contact::PresenceStateYes) {
            Tp::PendingOperation *op = manager->authorizePresencePublication(QList< Tp::ContactPtr >() << contact);
            op->setProperty("__contact", QVariant::fromValue(contact));

            connect(op, SIGNAL(finished(Tp::PendingOperation*)),
                    this, SLOT(onFinalizeSubscriptionFinished(Tp::PendingOperation*)));
        } else {
            m_pendingContacts.insert(contact->id(), contact);

            updateMenus();

            m_notifierItem.data()->showMessage(i18n("New contact request"),
                                               i18n("The contact %1 added you to its contact list",
                                                    contact->id()),
                                               QLatin1String("list-add-user"));
        }
    }
}

void ContactRequestHandler::onFinalizeSubscriptionFinished(Tp::PendingOperation *op)
{
    Tp::ContactPtr contact = op->property("__contact").value< Tp::ContactPtr >();

    if (op->isError()) {
        // ARGH
        m_notifierItem.data()->showMessage(i18n("Error adding contact"),
                                           i18n("%1 has been added successfully to your contact list, "
                                                "but might be unable to see your presence. Error details: %2",
                                                contact->alias(), KTp::ErrorDictionary::displayVerboseErrorMessage(op->errorName())),
                                                QLatin1String("dialog-error"));
    } else {
        // Yeah. All fine, so don't notify
    }
}

void ContactRequestHandler::updateNotifierItemTooltip()
{
    if (!m_menuItems.size()) {
        // Set passive
        m_notifierItem.data()->setStatus(KStatusNotifierItem::Passive);
        // Add the usual "nothing" action, if needed
        if (!m_notifierMenu->actions().contains(m_noContactsAction)) {
            m_notifierMenu->addAction(m_noContactsAction);
        }

        m_notifierItem.data()->setToolTip(QLatin1String("list-add-user"),
                                          i18n("No incoming contact requests"),
                                          QString());
    } else {
        // Set active
        m_notifierItem.data()->setStatus(KStatusNotifierItem::Active);
        // Remove the "nothing" action, if needed
        if (m_notifierMenu->actions().contains(m_noContactsAction)) {
            m_notifierMenu->removeAction(m_noContactsAction);
        }

        m_notifierItem.data()->setToolTip(QLatin1String("list-add-user"),
                                          i18np("You have 1 incoming contact request",
                                                "You have %1 incoming contact requests",
                                                m_menuItems.size()),
                                          QString());
    }
}

void ContactRequestHandler::onContactRequestApproved()
{
    QString contactId = qobject_cast<KAction*>(sender())->data().toString();

    if (!contactId.isEmpty()) {
        Tp::ContactPtr contact = m_pendingContacts.value(contactId);
        if (!contact.isNull()) {
            Tp::PendingOperation *op = contact->manager()->authorizePresencePublication(QList< Tp::ContactPtr >() << contact);
            op->setProperty("__contact", QVariant::fromValue(contact));

            connect (op, SIGNAL(finished(Tp::PendingOperation*)),
                     this, SLOT(onAuthorizePresencePublicationFinished(Tp::PendingOperation*)));
        }
    }
}

void ContactRequestHandler::onAuthorizePresencePublicationFinished(Tp::PendingOperation *op)
{
    Tp::ContactPtr contact = op->property("__contact").value< Tp::ContactPtr >();

    if (op->isError()) {
        // ARGH
        m_notifierItem.data()->showMessage(i18n("Error accepting contact request"),
                                           i18n("There was an error while accepting the request: %1",
                                                KTp::ErrorDictionary::displayVerboseErrorMessage(op->errorName())),
                                           QLatin1String("dialog-error"));

        // Re-enable the action
        m_menuItems.value(contact->id())->setEnabled(true);
    } else {
        // Yeah
        m_notifierItem.data()->showMessage(i18n("Contact request accepted"),
                                           i18n("%1 will now be able to see your presence",
                                                contact->alias()), QLatin1String("dialog-ok-apply"));

        // If needed, reiterate the request on the other end
        if (contact->manager()->canRequestPresenceSubscription() &&
            contact->subscriptionState() == Tp::Contact::PresenceStateNo) {
            connect(contact->manager()->requestPresenceSubscription(QList< Tp::ContactPtr >() << contact),
                    SIGNAL(finished(Tp::PendingOperation*)),
                    this, SLOT(onFinalizeSubscriptionFinished(Tp::PendingOperation*)));
        }

        // Update the menu
        m_pendingContacts.remove(contact->id());
        updateMenus();
    }
}

void ContactRequestHandler::onContactRequestDenied()
{
    QString contactId = qobject_cast<KAction*>(sender())->data().toString();

    // Disable the action in the meanwhile
    m_menuItems.value(contactId)->setEnabled(false);

    if (!contactId.isEmpty()) {
        Tp::ContactPtr contact = m_pendingContacts.value(contactId);
        if (!contact.isNull()) {
            Tp::PendingOperation *op = contact->manager()->removePresencePublication(QList< Tp::ContactPtr >() << contact);
            op->setProperty("__contact", QVariant::fromValue(contact));

            connect(op, SIGNAL(finished(Tp::PendingOperation*)),
                    this, SLOT(onRemovePresencePublicationFinished(Tp::PendingOperation*)));
        }
    }
}

void ContactRequestHandler::onRemovePresencePublicationFinished(Tp::PendingOperation *op)
{
    Tp::ContactPtr contact = op->property("__contact").value< Tp::ContactPtr >();

    if (op->isError()) {
        // ARGH<commit_msg>Handle multi-account requests with the same contact Id as if they were one, and authorize them altogether<commit_after>/*
    Copyright (C) 2011  <NAME> <<EMAIL>>
    Copyright (C) 2011  <NAME> <<EMAIL>>

    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 Street, Fifth Floor, Boston, MA  02110-1301  USA
*/


#include "contact-request-handler.h"

#include <TelepathyQt4/Connection>
#include <TelepathyQt4/ContactManager>
#include <TelepathyQt4/PendingOperation>
#include <TelepathyQt4/PendingComposite>
#include <TelepathyQt4/Account>

#include <KTelepathy/error-dictionary.h>

#include <QtCore/QFutureWatcher>

#include <KDebug>
#include <KGlobal>
#include <KAboutData>
#include <KMenu>
#include <KAction>
#include <KStatusNotifierItem>

Q_DECLARE_METATYPE(Tp::ContactPtr)

bool kde_tp_filter_contacts_by_publication_status(const Tp::ContactPtr &contact)
{
    return contact->publishState() == Tp::Contact::PresenceStateAsk;
}

ContactRequestHandler::ContactRequestHandler(const Tp::AccountManagerPtr& am, QObject *parent)
    : QObject(parent)
{
    m_accountManager = am;
    connect(m_accountManager.data(), SIGNAL(newAccount(Tp::AccountPtr)),
            this, SLOT(onNewAccountAdded(Tp::AccountPtr)));

    QList<Tp::AccountPtr> accounts = m_accountManager->allAccounts();

    Q_FOREACH(const Tp::AccountPtr &account, accounts) {
        onNewAccountAdded(account);
    }

    m_noContactsAction = new KAction(i18n("No pending contact requests at the moment"), this);
    m_noContactsAction->setEnabled(false);
}

ContactRequestHandler::~ContactRequestHandler()
{

}

void ContactRequestHandler::onNewAccountAdded(const Tp::AccountPtr& account)
{
    kWarning();
    Q_ASSERT(account->isReady(Tp::Account::FeatureCore));

    if (account->connection()) {
        monitorPresence(account->connection());
    }

    connect(account.data(),
            SIGNAL(connectionChanged(Tp::ConnectionPtr)),
            this, SLOT(onConnectionChanged(Tp::ConnectionPtr)));
}

void ContactRequestHandler::onConnectionChanged(const Tp::ConnectionPtr& connection)
{
    if (!connection.isNull()) {
        monitorPresence(connection);
    }
}

void ContactRequestHandler::monitorPresence(const Tp::ConnectionPtr &connection)
{
    kDebug();
    connect(connection->contactManager().data(), SIGNAL(presencePublicationRequested(Tp::Contacts)),
            this, SLOT(onPresencePublicationRequested(Tp::Contacts)));

    connect(connection->contactManager().data(),
            SIGNAL(stateChanged(Tp::ContactListState)),
            this, SLOT(onContactManagerStateChanged(Tp::ContactListState)));

    onContactManagerStateChanged(connection->contactManager(),
                                 connection->contactManager()->state());
}

void ContactRequestHandler::onContactManagerStateChanged(Tp::ContactListState state)
{
    onContactManagerStateChanged(Tp::ContactManagerPtr(qobject_cast< Tp::ContactManager* >(sender())), state);
}

void ContactRequestHandler::onContactManagerStateChanged(const Tp::ContactManagerPtr &contactManager,
                                                         Tp::ContactListState state)
{
    if (state == Tp::ContactListStateSuccess) {
        QFutureWatcher< Tp::ContactPtr > *watcher = new QFutureWatcher< Tp::ContactPtr >(this);
        connect(watcher, SIGNAL(finished()), this, SLOT(onAccountsPresenceStatusFiltered()));
        watcher->setFuture(QtConcurrent::filtered(contactManager->allKnownContacts(),
                                                  kde_tp_filter_contacts_by_publication_status));

        kDebug() << "Watcher is on";
    } else {
        kDebug() << "Watcher still off, state is" << state << "contactManager is" << contactManager.isNull();
    }
}

void ContactRequestHandler::onAccountsPresenceStatusFiltered()
{
    kDebug() << "Watcher is here";
    QFutureWatcher< Tp::ContactPtr > *watcher = dynamic_cast< QFutureWatcher< Tp::ContactPtr > * >(sender());
    kDebug() << "Watcher is casted";
    Tp::Contacts contacts = watcher->future().results().toSet();
    kDebug() << "Watcher is used";
    if (!contacts.isEmpty()) {
        onPresencePublicationRequested(contacts);
    }
    watcher->deleteLater();
}

void ContactRequestHandler::onPresencePublicationRequested(const Tp::Contacts& contacts)
{
    kDebug() << "New contact requested";

    Q_FOREACH (const Tp::ContactPtr &contact, contacts) {
        Tp::ContactManagerPtr manager = contact->manager();

        if (contact->subscriptionState() == Tp::Contact::PresenceStateYes) {
            Tp::PendingOperation *op = manager->authorizePresencePublication(QList< Tp::ContactPtr >() << contact);
            op->setProperty("__contact", QVariant::fromValue(contact));

            connect(op, SIGNAL(finished(Tp::PendingOperation*)),
                    this, SLOT(onFinalizeSubscriptionFinished(Tp::PendingOperation*)));
        } else {
            // Handle multiaccount requests properly
            if (m_pendingContacts.contains(contact->id())) {
                // It's likely we have a simultaneous request
                bool newReq = true;
                QHash<QString, Tp::ContactPtr>::const_iterator i = m_pendingContacts.find(contact->id());
                while (i != m_pendingContacts.constEnd() && i.key() == contact->id()) {
                    if (i.value().data() == contact.data()) {
                        newReq = false;
                        break;
                    }
                    ++i;
                }

                if (newReq) {
                    // Insert multi
                    m_pendingContacts.insertMulti(contact->id(), contact);
                }
            } else {
                // Simple insertion
                m_pendingContacts.insert(contact->id(), contact);
            }

            updateMenus();

            m_notifierItem.data()->showMessage(i18n("New contact request"),
                                               i18n("The contact %1 added you to its contact list",
                                                    contact->id()),
                                               QLatin1String("list-add-user"));
        }
    }
}

void ContactRequestHandler::onFinalizeSubscriptionFinished(Tp::PendingOperation *op)
{
    Tp::ContactPtr contact = op->property("__contact").value< Tp::ContactPtr >();

    if (op->isError()) {
        // ARGH
        m_notifierItem.data()->showMessage(i18n("Error adding contact"),
                                           i18n("%1 has been added successfully to your contact list, "
                                                "but might be unable to see your presence. Error details: %2",
                                                contact->alias(), KTp::ErrorDictionary::displayVerboseErrorMessage(op->errorName())),
                                                QLatin1String("dialog-error"));
    } else {
        // Yeah. All fine, so don't notify
    }
}

void ContactRequestHandler::updateNotifierItemTooltip()
{
    if (!m_menuItems.size()) {
        // Set passive
        m_notifierItem.data()->setStatus(KStatusNotifierItem::Passive);
        // Add the usual "nothing" action, if needed
        if (!m_notifierMenu->actions().contains(m_noContactsAction)) {
            m_notifierMenu->addAction(m_noContactsAction);
        }

        m_notifierItem.data()->setToolTip(QLatin1String("list-add-user"),
                                          i18n("No incoming contact requests"),
                                          QString());
    } else {
        // Set active
        m_notifierItem.data()->setStatus(KStatusNotifierItem::Active);
        // Remove the "nothing" action, if needed
        if (m_notifierMenu->actions().contains(m_noContactsAction)) {
            m_notifierMenu->removeAction(m_noContactsAction);
        }

        m_notifierItem.data()->setToolTip(QLatin1String("list-add-user"),
                                          i18np("You have 1 incoming contact request",
                                                "You have %1 incoming contact requests",
                                                m_menuItems.size()),
                                          QString());
    }
}

void ContactRequestHandler::onContactRequestApproved()
{
    QString contactId = qobject_cast<KAction*>(sender())->data().toString();

    // Disable the action in the meanwhile
    m_menuItems.value(contactId)->setEnabled(false);

    if (!contactId.isEmpty()) {
        QList<Tp::PendingOperation*> operations;
        QHash<QString, Tp::ContactPtr>::const_iterator i = m_pendingContacts.find(contactId);
        while (i != m_pendingContacts.constEnd() && i.key() == contactId) {
            Tp::PendingOperation *op = i.value()->manager()->authorizePresencePublication(QList< Tp::ContactPtr >() << i.value());
            op->setProperty("__contact", QVariant::fromValue(i.value()));
            operations.append(op);
            ++i;
        }

        // Take the first value, if any
        if (!operations.isEmpty()) {
            Tp::ContactPtr contact = m_pendingContacts.find(contactId).value();

            Tp::PendingComposite *op = new Tp::PendingComposite(operations, true, contact);
            op->setProperty("__contact", QVariant::fromValue(contact));

            connect(op, SIGNAL(finished(Tp::PendingOperation*)),
                    this, SLOT(onAuthorizePresencePublicationFinished(Tp::PendingOperation*)));
        }
    }

}

void ContactRequestHandler::onAuthorizePresencePublicationFinished(Tp::PendingOperation *op)
{
    Tp::ContactPtr contact = op->property("__contact").value< Tp::ContactPtr >();

    if (op->isError()) {
        // ARGH
        m_notifierItem.data()->showMessage(i18n("Error accepting contact request"),
                                           i18n("There was an error while accepting the request: %1",
                                                KTp::ErrorDictionary::displayVerboseErrorMessage(op->errorName())),
                                           QLatin1String("dialog-error"));

        // Re-enable the action
        m_menuItems.value(contact->id())->setEnabled(true);
    } else {
        // Yeah
        m_notifierItem.data()->showMessage(i18n("Contact request accepted"),
                                           i18n("%1 will now be able to see your presence",
                                                contact->alias()), QLatin1String("dialog-ok-apply"));

        // If needed, reiterate the request on the other end
        if (contact->manager()->canRequestPresenceSubscription() &&
            contact->subscriptionState() == Tp::Contact::PresenceStateNo) {
            connect(contact->manager()->requestPresenceSubscription(QList< Tp::ContactPtr >() << contact),
                    SIGNAL(finished(Tp::PendingOperation*)),
                    this, SLOT(onFinalizeSubscriptionFinished(Tp::PendingOperation*)));
        }

        // Update the menu
        m_pendingContacts.remove(contact->id());
        updateMenus();
    }
}

void ContactRequestHandler::onContactRequestDenied()
{
    QString contactId = qobject_cast<KAction*>(sender())->data().toString();

    // Disable the action in the meanwhile
    m_menuItems.value(contactId)->setEnabled(false);

    if (!contactId.isEmpty()) {
        QList<Tp::PendingOperation*> operations;
        QHash<QString, Tp::ContactPtr>::const_iterator i = m_pendingContacts.find(contactId);
        while (i != m_pendingContacts.constEnd() && i.key() == contactId) {
            Tp::PendingOperation *op = i.value()->manager()->removePresencePublication(QList< Tp::ContactPtr >() << i.value());
            op->setProperty("__contact", QVariant::fromValue(i.value()));
            operations.append(op);
            ++i;
        }

        // Take the first value, if any
        if (!operations.isEmpty()) {
            Tp::ContactPtr contact = m_pendingContacts.find(contactId).value();

            Tp::PendingComposite *op = new Tp::PendingComposite(operations, true, contact);
            op->setProperty("__contact", QVariant::fromValue(contact));

            connect(op, SIGNAL(finished(Tp::PendingOperation*)),
                    this, SLOT(onRemovePresencePublicationFinished(Tp::PendingOperation*)));
        }
    }
}

void ContactRequestHandler::onRemovePresencePublicationFinished(Tp::PendingOperation *op)
{
    Tp::ContactPtr contact = op->property("__contact").value< Tp::ContactPtr >();

    if (op->isError()) {
        // ARGH
{'fraction_non_alphanumeric': 0.12215320910973085, 'fraction_numerical': 0.005010351966873706, 'mean_word_length': 2.6928134556574923, 'pattern_counts': {'":': 0, '<': 129, '<?xml version=': 0, '>': 204, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 82, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6583268', 'n_tokens_mistral': 6479, 'n_tokens_neox': 6252, 'n_words': 1268}