/* * Squawk messenger. * Copyright (C) 2019 Yury Gubich * * 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 . */ #include "conference.h" #include Core::Conference::Conference(const QString& p_jid, const QString& p_account, bool p_autoJoin, const QString& p_name, const QString& p_nick, QXmppMucRoom* p_room): RosterItem(p_jid, p_account), nick(p_nick), room(p_room), joined(false), autoJoin(p_autoJoin), exParticipants() { muc = true; name = p_name; connect(room, &QXmppMucRoom::joined, this, &Conference::onRoomJoined); connect(room, &QXmppMucRoom::left, this, &Conference::onRoomLeft); connect(room, &QXmppMucRoom::nameChanged, this, &Conference::onRoomNameChanged); connect(room, &QXmppMucRoom::subjectChanged, this, &Conference::onRoomSubjectChanged); connect(room, &QXmppMucRoom::participantAdded, this, &Conference::onRoomParticipantAdded); connect(room, &QXmppMucRoom::participantChanged, this, &Conference::onRoomParticipantChanged); connect(room, &QXmppMucRoom::participantRemoved, this, &Conference::onRoomParticipantRemoved); connect(room, &QXmppMucRoom::nickNameChanged, this, &Conference::onRoomNickNameChanged); connect(room, &QXmppMucRoom::error, this, &Conference::onRoomError); room->setNickName(nick); if (autoJoin) { room->join(); } archive->readAllResourcesAvatars(exParticipants); } Core::Conference::~Conference() { if (joined) { room->leave(); } room->deleteLater(); } QString Core::Conference::getNick() const { return nick; } bool Core::Conference::getAutoJoin() { return autoJoin; } bool Core::Conference::getJoined() const { return joined; } void Core::Conference::setJoined(bool p_joined) { if (joined != p_joined) { if (p_joined) { room->join(); } else { room->leave(); } } } void Core::Conference::setAutoJoin(bool p_autoJoin) { if (autoJoin != p_autoJoin) { autoJoin = p_autoJoin; emit autoJoinChanged(autoJoin); } } void Core::Conference::setNick(const QString& p_nick) { if (nick != p_nick) { if (joined) { room->setNickName(p_nick); } else { nick = p_nick; emit nickChanged(nick); } } } void Core::Conference::onRoomJoined() { joined = true; emit joinedChanged(joined); } void Core::Conference::onRoomLeft() { joined = false; emit joinedChanged(joined); } void Core::Conference::onRoomNameChanged(const QString& p_name) { setName(p_name); } void Core::Conference::onRoomNickNameChanged(const QString& p_nick) { if (p_nick != nick) { nick = p_nick; emit nickChanged(nick); } } void Core::Conference::onRoomError(const QXmppStanza::Error& err) { qDebug() << "MUC" << jid << "error:" << err.text(); } void Core::Conference::onRoomParticipantAdded(const QString& p_name) { QStringList comps = p_name.split("/"); QString resource = comps.back(); QXmppPresence pres = room->participantPresence(p_name); QXmppMucItem mi = pres.mucItem(); if (resource == jid) { resource = ""; } std::map::const_iterator itr = exParticipants.find(resource); bool hasAvatar = itr != exParticipants.end(); if (resource.size() > 0) { QDateTime lastInteraction = pres.lastUserInteraction(); if (!lastInteraction.isValid()) { lastInteraction = QDateTime::currentDateTimeUtc(); } QMap cData = { {"lastActivity", lastInteraction}, {"availability", pres.availableStatusType()}, {"status", pres.statusText()}, {"affiliation", mi.affiliation()}, {"role", mi.role()} }; if (hasAvatar) { if (itr->second.autogenerated) { cData.insert("avatarState", static_cast(Shared::Avatar::valid)); } else { cData.insert("avatarState", static_cast(Shared::Avatar::autocreated)); } cData.insert("avatarPath", avatarPath(resource) + "." + itr->second.type); } else { cData.insert("avatarState", static_cast(Shared::Avatar::empty)); cData.insert("avatarPath", ""); requestVCard(p_name); } emit addParticipant(resource, cData); } switch (pres.vCardUpdateType()) { case QXmppPresence::VCardUpdateNone: //this presence has nothing to do with photo break; case QXmppPresence::VCardUpdateNotReady: //let's say the photo didn't change here break; case QXmppPresence::VCardUpdateNoPhoto: { //there is no photo, need to drop if any if (!hasAvatar || !itr->second.autogenerated) { setAutoGeneratedAvatar(resource); } } break; case QXmppPresence::VCardUpdateValidPhoto:{ //there is a photo, need to load if (hasAvatar) { if (itr->second.autogenerated || itr->second.hash != pres.photoHash()) { emit requestVCard(p_name); } } else { emit requestVCard(p_name); } break; } } } void Core::Conference::onRoomParticipantChanged(const QString& p_name) { QStringList comps = p_name.split("/"); QString resource = comps.back(); QXmppPresence pres = room->participantPresence(p_name); QXmppMucItem mi = pres.mucItem(); handlePresence(pres); if (resource != jid) { QDateTime lastInteraction = pres.lastUserInteraction(); if (!lastInteraction.isValid()) { lastInteraction = QDateTime::currentDateTimeUtc(); } emit changeParticipant(resource, { {"lastActivity", lastInteraction}, {"availability", pres.availableStatusType()}, {"status", pres.statusText()}, {"affiliation", mi.affiliation()}, {"role", mi.role()} }); } } void Core::Conference::onRoomParticipantRemoved(const QString& p_name) { QStringList comps = p_name.split("/"); QString resource = comps.back(); if (resource == jid) { qDebug() << "Room" << jid << "is reporting of removing his own presence from the list of participants. Not sure what to do with that yet, skipping"; } else { emit removeParticipant(resource); } } QString Core::Conference::getSubject() const { if (joined) { return room->subject(); } else { return ""; } } void Core::Conference::onRoomSubjectChanged(const QString& p_name) { emit subjectChanged(p_name); } void Core::Conference::handlePresence(const QXmppPresence& pres) { QString id = pres.from(); QStringList comps = id.split("/"); QString jid = comps.front(); QString resource(""); if (comps.size() > 1) { resource = comps.back(); } switch (pres.vCardUpdateType()) { case QXmppPresence::VCardUpdateNone: //this presence has nothing to do with photo break; case QXmppPresence::VCardUpdateNotReady: //let's say the photo didn't change here break; case QXmppPresence::VCardUpdateNoPhoto: { //there is no photo, need to drop if any Archive::AvatarInfo info; bool hasAvatar = readAvatarInfo(info, resource); if (!hasAvatar || !info.autogenerated) { setAutoGeneratedAvatar(resource); } } break; case QXmppPresence::VCardUpdateValidPhoto:{ //there is a photo, need to load Archive::AvatarInfo info; bool hasAvatar = readAvatarInfo(info, resource); if (hasAvatar) { if (info.autogenerated || info.hash != pres.photoHash()) { emit requestVCard(id); } } else { emit requestVCard(id); } break; } } } bool Core::Conference::setAutoGeneratedAvatar(const QString& resource) { Archive::AvatarInfo newInfo; bool result = RosterItem::setAutoGeneratedAvatar(newInfo, resource); if (result && resource.size() != 0) { std::map::iterator itr = exParticipants.find(resource); if (itr == exParticipants.end()) { exParticipants.insert(std::make_pair(resource, newInfo)); } else { itr->second = newInfo; } emit changeParticipant(resource, { {"avatarState", static_cast(Shared::Avatar::autocreated)}, {"avatarPath", avatarPath(resource) + "." + newInfo.type} }); } return result; } bool Core::Conference::setAvatar(const QByteArray& data, Archive::AvatarInfo& info, const QString& resource) { bool result = RosterItem::setAvatar(data, info, resource); if (result && resource.size() != 0) { if (data.size() > 0) { std::map::iterator itr = exParticipants.find(resource); if (itr == exParticipants.end()) { exParticipants.insert(std::make_pair(resource, info)); } else { itr->second = info; } emit changeParticipant(resource, { {"avatarState", static_cast(Shared::Avatar::autocreated)}, {"avatarPath", avatarPath(resource) + "." + info.type} }); } else { std::map::iterator itr = exParticipants.find(resource); if (itr != exParticipants.end()) { exParticipants.erase(itr); } emit changeParticipant(resource, { {"avatarState", static_cast(Shared::Avatar::empty)}, {"avatarPath", ""} }); } } return result; } Shared::VCard Core::Conference::handleResponseVCard(const QXmppVCardIq& card, const QString &resource) { Shared::VCard result = RosterItem::handleResponseVCard(card, resource); if (resource.size() > 0) { emit changeParticipant(resource, { {"avatarState", static_cast(result.getAvatarType())}, {"avatarPath", result.getAvatarPath()} }); } return result; } QMap Core::Conference::getAllAvatars() const { QMap result; for (const std::pair& pair : exParticipants) { result.insert(pair.first, avatarPath(pair.first) + "." + pair.second.type); } return result; }