squawk/core/delayManager/manager.cpp

438 lines
13 KiB
C++

// Squawk messenger.
// Copyright (C) 2019 Yury Gubich <blue@macaw.me>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "manager.h"
#include <QDebug>
#include "cardinternal.h"
#include "infoforuser.h"
#include "owncardinternal.h"
#include "owninfoforuser.h"
Core::DelayManager::Manager::Manager(const QString& poj, Job::Id mpj, QObject* parent) :
QObject(parent),
maxParallelJobs(mpj),
nextJobId(1),
scheduledJobs(),
scheduledJobsById(scheduledJobs.get<id>()),
jobSequence(scheduledJobs.get<sequence>()),
runningJobs(),
ownVCardJobId(0),
ownInfoJobId(0),
scheduledVCards(),
requestedVCards(),
#ifdef WITH_OMEMO
requestedBundles(),
#endif
ownJid(poj)
{
}
Core::DelayManager::Manager::~Manager() {
for (const std::pair<const Job::Id, Job*>& pair : runningJobs) {
delete pair.second;
}
for (Job* job : jobSequence) {
delete job;
}
}
Core::DelayManager::Job::Id Core::DelayManager::Manager::getNextJobId() {
Job::Id id = nextJobId++;
if (id == 0)
id = nextJobId++;
return id;
}
void Core::DelayManager::Manager::getInfo(const QString& jid) {
if (jid == ownJid)
return getOwnInfo();
Job* job = nullptr;
#ifdef WITH_OMEMO
std::map<QString, Job::Id>::const_iterator bitr = requestedBundles.find(jid);
if (bitr != requestedBundles.end()) {
std::map<Job::Id, Job*>::const_iterator itr = runningJobs.find(bitr->second);
if (itr == runningJobs.end())
throw JobNotFound(bitr->second);
job = itr->second;
}
else
#endif
job = getVCardJob(jid);
if (job != nullptr) {
if (job->type == Job::Type::cardInternal)
replaceJob(new InfoForUser(job->id, jid));
} else
scheduleJob(new InfoForUser(getNextJobId(), jid));
}
void Core::DelayManager::Manager::getOwnInfo() {
if (ownInfoJobId == 0) {
if (ownVCardJobId != 0)
replaceJob(new OwnInfoForUser(ownVCardJobId));
else
scheduleJob(new OwnInfoForUser(getNextJobId()));
}
}
void Core::DelayManager::Manager::getVCard(const QString& jid) {
Job* job = getVCardJob(jid);
if (job == nullptr)
scheduleJob(new CardInternal(getNextJobId(), jid));
}
void Core::DelayManager::Manager::getOwnVCard() {
if (ownVCardJobId == 0)
scheduleJob(new OwnCardInternal(getNextJobId()));
}
bool Core::DelayManager::Manager::isOwnVCardPending() const {
return ownVCardJobId != 0;
}
Core::DelayManager::Job* Core::DelayManager::Manager::getVCardJob(const QString& jid) {
Job* job = nullptr;
std::map<QString, Job::Id>::const_iterator sitr = scheduledVCards.find(jid);
if (sitr == scheduledVCards.end()) {
std::map<QString, Job::Id>::const_iterator ritr = requestedVCards.find(jid);
if (ritr != requestedVCards.end()) {
std::map<Job::Id, Job*>::const_iterator itr = runningJobs.find(ritr->second);
if (itr == runningJobs.end())
throw JobNotFound(ritr->second, "getVCardJob:1");
job = itr->second;
}
} else {
StorageById::const_iterator itr = scheduledJobsById.find(sitr->second);
if (itr == scheduledJobsById.end())
throw JobNotFound(sitr->second, "getVCardJob:2");
job = *itr;
}
return job;
}
void Core::DelayManager::Manager::preScheduleJob(Job* job) {
switch (job->type) {
case Job::Type::cardInternal:
scheduledVCards.emplace(dynamic_cast<CardInternal*>(job)->jid, job->id);
break;
case Job::Type::ownCardInternal:
ownVCardJobId = job->id;
break;
case Job::Type::infoForUser:
scheduledVCards.emplace(dynamic_cast<InfoForUser*>(job)->jid, job->id);
break;
case Job::Type::ownInfoForUser:
ownVCardJobId = job->id;
ownInfoJobId = job->id;
break;
}
}
void Core::DelayManager::Manager::scheduleJob(Job* job) {
preScheduleJob(job);
if (runningJobs.size() < maxParallelJobs) {
executeJob(job);
} else {
scheduledJobs.push_back(job);
}
}
void Core::DelayManager::Manager::preExecuteJob(Job* job) {
switch (job->type) {
case Job::Type::cardInternal:
case Job::Type::infoForUser: {
Contact* cij = dynamic_cast<Contact*>(job);
requestedVCards.emplace(cij->jid, job->id);
scheduledVCards.erase(cij->jid);
}
break;
case Job::Type::ownInfoForUser:
case Job::Type::ownCardInternal:
break;
}
}
void Core::DelayManager::Manager::executeJob(Job* job) {
preExecuteJob(job);
runningJobs.emplace(job->id, job);
switch (job->type) {
case Job::Type::cardInternal:
case Job::Type::infoForUser:
emit requestVCard(dynamic_cast<Contact*>(job)->jid);
break;
case Job::Type::ownInfoForUser:
case Job::Type::ownCardInternal:
emit requestOwnVCard();
break;
}
}
void Core::DelayManager::Manager::jobIsDone(Job::Id jobId) {
std::map<Job::Id, Job*>::const_iterator itr = runningJobs.find(jobId);
if (itr == runningJobs.end()) {
throw JobNotFound(jobId, "jobIsDone");
}
Job* job = itr->second;
delete job;
runningJobs.erase(itr);
if (scheduledJobs.size() > 0) {
Job* job = scheduledJobs.front();
scheduledJobs.pop_front();
executeJob(job);
}
}
void Core::DelayManager::Manager::replaceJob(Job* job) {
preScheduleJob(job);
std::map<Job::Id, Job*>::iterator itr = runningJobs.find(job->id);
if (itr != runningJobs.end()) {
preExecuteJob(job);
delete itr->second;
itr->second = job;
} else {
StorageById::iterator sitr = scheduledJobsById.find(job->id);
if (sitr != scheduledJobsById.end()) {
delete *(sitr);
scheduledJobsById.replace(sitr, job);
} else
throw JobNotFound(job->id, "replaceJob");
}
}
void Core::DelayManager::Manager::jobIsCanceled(Job* job, bool wasRunning) {
switch (job->type) {
case Job::Type::cardInternal: {
CardInternal* jb = dynamic_cast<CardInternal*>(job);
if (wasRunning)
requestedVCards.erase(jb->jid);
else
scheduledVCards.erase(jb->jid);
emit gotVCard(jb->jid, Shared::VCard());
}
break;
case Job::Type::infoForUser: {
InfoForUser* jb = dynamic_cast<InfoForUser*>(job);
switch (jb->getStage()) {
case InfoForUser::Stage::waitingForVCard:
if (wasRunning)
requestedVCards.erase(jb->jid);
else
scheduledVCards.erase(jb->jid);
emit gotVCard(jb->jid, Shared::VCard());
break;
case InfoForUser::Stage::waitingForBundles:
requestedBundles.erase(jb->jid);
break;
default:
break;
}
emit gotInfo(Shared::Info(jb->jid));
}
break;
case Job::Type::ownInfoForUser: {
OwnInfoForUser* jb = dynamic_cast<OwnInfoForUser*>(job);
if (jb->getStage() == OwnInfoForUser::Stage::waitingForVCard) {
ownVCardJobId = 0;
emit gotOwnVCard(Shared::VCard());
}
ownInfoJobId = 0;
emit gotOwnInfo(Shared::Info (ownJid));
}
break;
case Job::Type::ownCardInternal:
ownVCardJobId = 0;
emit gotOwnVCard(Shared::VCard());
break;
}
delete job;
}
void Core::DelayManager::Manager::disconnected() {
for (const std::pair<const Job::Id, Job*> pair : runningJobs)
jobIsCanceled(pair.second, true);
for (Job* job : scheduledJobs)
jobIsCanceled(job, false);
runningJobs.clear();
scheduledJobs.clear();
}
void Core::DelayManager::Manager::receivedVCard(const QString& jid, const Shared::VCard& card) {
std::map<QString, Job::Id>::const_iterator cardItr = requestedVCards.find(jid);
if (cardItr == requestedVCards.end()) {
qDebug() << "received VCard for" << jid << "but it was never requested through manager, ignoring";
return;
}
Job::Id jobId = cardItr->second;
requestedVCards.erase(cardItr);
std::map<Job::Id, Job*>::const_iterator itr = runningJobs.find(jobId);
if (itr == runningJobs.end()) {
throw JobNotFound(jobId, "receivedVCard");
}
Job* job = itr->second;
switch (job->type) {
case Job::Type::cardInternal:
jobIsDone(jobId);
emit gotVCard(jid, card);
break;
case Job::Type::infoForUser: {
#ifdef WITH_OMEMO
requestedBundles.emplace(jid, jobId);
InfoForUser* jb = dynamic_cast<InfoForUser*>(job);
jb->receivedVCard(card);
emit requestBundles(jid);
#else
Shared::Info info(jid);
info.turnIntoContact(card);
emit gotInfo(info);
jobIsDone(jobId);
#endif
emit gotVCard(jid, card);
}
break;
default:
throw UnexpectedJobType(job->type, "receivedVCard");
}
}
void Core::DelayManager::Manager::receivedOwnVCard(const Shared::VCard& card) {
if (ownVCardJobId == 0) {
qDebug() << "received own VCard for" << ownJid << "but it was never requested through manager, ignoring";
return;
}
Job::Id jobId = ownVCardJobId;
ownVCardJobId = 0;
std::map<Job::Id, Job*>::const_iterator itr = runningJobs.find(jobId);
if (itr == runningJobs.end()) {
throw JobNotFound(jobId, "receivedOwnVCard");
}
Job* job = itr->second;
switch (job->type) {
case Job::Type::ownCardInternal:
jobIsDone(jobId);
emit gotOwnVCard(card);
break;
case Job::Type::ownInfoForUser: {
#ifdef WITH_OMEMO
OwnInfoForUser* jb = dynamic_cast<OwnInfoForUser*>(job);
jb->receivedVCard(card);
emit requestOwnBundles();
#else
Shared::Info info(ownJid);
info.turnIntoOwnAccount(card);
emit gotOwnInfo(info);
jobIsDone(jobId);
#endif
emit gotOwnVCard(card);
}
break;
default:
throw UnexpectedJobType(job->type, "receivedVCard");
}
}
void Core::DelayManager::Manager::receivedBundles(const QString& jid, const std::list<Shared::KeyInfo>& keys) {
std::map<QString, Job::Id>::const_iterator itr = requestedBundles.find(jid);
if (itr == requestedBundles.end()) {
qDebug() << "received bundles for" << jid << "but they were never requested through manager, ignoring";
return;
}
Job::Id jobId = itr->second;
requestedBundles.erase(itr);
std::map<Job::Id, Job*>::const_iterator jitr = runningJobs.find(jobId);
if (jitr == runningJobs.end())
throw JobNotFound(jobId, "receivedBundles");
Job* jb = jitr->second;
InfoForUser* job = dynamic_cast<InfoForUser*>(jb);
Shared::Info info(jid);
info.turnIntoContact(job->claim(), new std::list<Shared::KeyInfo>(keys));
emit gotInfo(info);
jobIsDone(jobId);
}
void Core::DelayManager::Manager::receivedOwnBundles(const std::list<Shared::KeyInfo>& keys) {
if (ownInfoJobId == 0) {
qDebug() << "received own bundles for" << ownJid << "but they were never requested through manager, ignoring";
return;
}
Job::Id jobId = ownInfoJobId;
ownInfoJobId = 0;
std::map<Job::Id, Job*>::const_iterator jitr = runningJobs.find(jobId);
if (jitr == runningJobs.end()) {
throw JobNotFound(jobId, "receivedOwnBundles");
}
Job* jb = jitr->second;
OwnInfoForUser* job = dynamic_cast<OwnInfoForUser*>(jb);
Shared::Info info(ownJid);
info.turnIntoOwnAccount(job->claim(), new std::list<Shared::KeyInfo>(keys));
emit gotOwnInfo(info);
jobIsDone(jobId);
}
void Core::DelayManager::Manager::setOwnJid(const QString& jid) {
ownJid = jid;
}
Core::DelayManager::Manager::UnexpectedJobType::UnexpectedJobType(Job::Type p_type, const std::string& p_method):
Exception(),
type(p_type),
method(p_method)
{}
std::string Core::DelayManager::Manager::UnexpectedJobType::getMessage() const{
std::string msg("Unexpected job type: ");
msg += Job::TypeString[static_cast<int>(type)];
if (method.size() > 0) {
msg += " in method " + method;
}
return msg;
}
Core::DelayManager::Manager::JobNotFound::JobNotFound(Job::Id p_id, const std::string& p_method) :
Exception(),
id(p_id),
method(p_method)
{}
std::string Core::DelayManager::Manager::JobNotFound::getMessage() const {
std::string msg("Job with id ");
msg += std::to_string(id);
msg += " was not found";
if (method.size() > 0) {
msg += " in method " + method;
}
return msg;
}