/* * 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() const { 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()}, {"client", QVariant::fromValue( Shared::ClientId( pres.capabilityNode(), pres.capabilityVer().toBase64(), pres.capabilityHash()) ) } }; careAboutAvatar(hasAvatar, itr->second, cData, resource, p_name); emit addParticipant(resource, cData); if (!hasAvatar) // because this way vCard is already requested, no need to handle possible avatar update return; } handlePossibleAvatarUpdate(pres, resource, hasAvatar, itr->second); } void Core::Conference::handlePossibleAvatarUpdate ( const QXmppPresence& pres, const QString& resource, bool hasAvatar, const Archive::AvatarInfo& info ) { 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 || !info.autogenerated) setAutoGeneratedAvatar(resource); break; case QXmppPresence::VCardUpdateValidPhoto: //there is a photo, need to load if (hasAvatar) { if (info.autogenerated || info.hash != pres.photoHash()) emit requestVCard(pres.from()); } else { emit requestVCard(pres.from()); } 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()}, {"client", QVariant::fromValue( Shared::ClientId( pres.capabilityNode(), pres.capabilityVer().toBase64(), pres.capabilityHash()) ) } }); } } 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(); Archive::AvatarInfo info; bool hasAvatar = readAvatarInfo(info, resource); handlePossibleAvatarUpdate(pres, resource, hasAvatar, info); } 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; } void Core::Conference::handleResponseVCard(const QXmppVCardIq& card, const QString &resource, Shared::VCard& out) { RosterItem::handleResponseVCard(card, resource, out); if (resource.size() > 0) emit changeParticipant(resource, { {"avatarState", static_cast(out.getAvatarType())}, {"avatarPath", out.getAvatarPath()} }); } 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; } QMap Core::Conference::getInfo() const { QMap data = RosterItem::getInfo(); data.insert("autoJoin", getAutoJoin()); data.insert("joined", getJoined()); data.insert("nick", getNick()); data.insert("avatars", getAllAvatars()); return data; }