// 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 "vcardhandler.h" #include "core/account.h" Core::VCardHandler::VCardHandler(Account* account): QObject(), acc(account), avatarHash(), avatarType() { QString path(QStandardPaths::writableLocation(QStandardPaths::CacheLocation)); path += "/" + acc->name; QDir dir(path); if (!dir.exists()) { bool res = dir.mkpath(path); if (!res) { qDebug() << "Couldn't create a cache directory for account" << acc->name; throw 22; } } QFile* avatar = new QFile(path + "/avatar.png"); QString type = "png"; if (!avatar->exists()) { delete avatar; avatar = new QFile(path + "/avatar.jpg"); type = "jpg"; if (!avatar->exists()) { delete avatar; avatar = new QFile(path + "/avatar.jpeg"); type = "jpeg"; if (!avatar->exists()) { delete avatar; avatar = new QFile(path + "/avatar.gif"); type = "gif"; } } } if (avatar->exists()) { if (avatar->open(QFile::ReadOnly)) { QCryptographicHash sha1(QCryptographicHash::Sha1); sha1.addData(avatar); avatarHash = sha1.result(); avatarType = type; } } } Core::VCardHandler::~VCardHandler() {} void Core::VCardHandler::initialize() { connect(acc->vm, &QXmppVCardManager::vCardReceived, this, &VCardHandler::onVCardReceived); //for some reason it doesn't work, launching from common handler //connect(acc->vm, &QXmppVCardManager::clientVCardReceived, this, &VCardHandler::onOwnVCardReceived); if (avatarType.size() != 0) { acc->presence.setVCardUpdateType(QXmppPresence::VCardUpdateValidPhoto); acc->presence.setPhotoHash(avatarHash.toUtf8()); } else { acc->presence.setVCardUpdateType(QXmppPresence::VCardUpdateNotReady); } } void Core::VCardHandler::onVCardReceived(const QXmppVCardIq& card) { QString id = card.from(); QStringList comps = id.split("/"); QString jid = comps.front().toLower(); QString resource(""); if (comps.size() > 1) { resource = comps.back(); } RosterItem* item = acc->rh->getRosterItem(jid); if (item == nullptr) { if (jid == acc->getBareJid()) onOwnVCardReceived(card); else qDebug() << "received vCard" << jid << "doesn't belong to any of known contacts or conferences, skipping"; return; } Shared::VCard vCard; item->handleResponseVCard(card, resource, vCard); acc->delay->receivedVCard(id, vCard); } void Core::VCardHandler::onOwnVCardReceived(const QXmppVCardIq& card) { QByteArray ava = card.photo(); bool avaChanged = false; QString path = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/" + acc->name + "/"; if (ava.size() > 0) { QCryptographicHash sha1(QCryptographicHash::Sha1); sha1.addData(ava); QString newHash(sha1.result()); QMimeDatabase db; QMimeType newType = db.mimeTypeForData(ava); if (avatarType.size() > 0) { if (avatarHash != newHash) { QString oldPath = path + "avatar." + avatarType; QFile oldAvatar(oldPath); bool oldToRemove = false; if (oldAvatar.exists()) { if (oldAvatar.rename(oldPath + ".bak")) { oldToRemove = true; } else { qDebug() << "Received new avatar for account" << acc->name << "but can't get rid of the old one, doing nothing"; } } QFile newAvatar(path + "avatar." + newType.preferredSuffix()); if (newAvatar.open(QFile::WriteOnly)) { newAvatar.write(ava); newAvatar.close(); avatarHash = newHash; avatarType = newType.preferredSuffix(); avaChanged = true; } else { qDebug() << "Received new avatar for account" << acc->name << "but can't save it"; if (oldToRemove) { qDebug() << "rolling back to the old avatar"; if (!oldAvatar.rename(oldPath)) { qDebug() << "Couldn't roll back to the old avatar in account" << acc->name; } } } } } else { QFile newAvatar(path + "avatar." + newType.preferredSuffix()); if (newAvatar.open(QFile::WriteOnly)) { newAvatar.write(ava); newAvatar.close(); avatarHash = newHash; avatarType = newType.preferredSuffix(); avaChanged = true; } else { qDebug() << "Received new avatar for account" << acc->name << "but can't save it"; } } } else { if (avatarType.size() > 0) { QFile oldAvatar(path + "avatar." + avatarType); if (!oldAvatar.remove()) { qDebug() << "Received vCard for account" << acc->name << "without avatar, but can't get rid of the file, doing nothing"; } else { avatarType = ""; avatarHash = ""; avaChanged = true; } } } if (avaChanged) { QMap change; if (avatarType.size() > 0) { acc->presence.setPhotoHash(avatarHash.toUtf8()); acc->presence.setVCardUpdateType(QXmppPresence::VCardUpdateValidPhoto); change.insert("avatarPath", path + "avatar." + avatarType); } else { acc->presence.setPhotoHash(""); acc->presence.setVCardUpdateType(QXmppPresence::VCardUpdateNoPhoto); change.insert("avatarPath", ""); } acc->client.setClientPresence(acc->presence); emit acc->changed(change); } Shared::VCard vCard; initializeVCard(vCard, card); if (avatarType.size() > 0) { vCard.setAvatarType(Shared::Avatar::valid); vCard.setAvatarPath(path + "avatar." + avatarType); } else { vCard.setAvatarType(Shared::Avatar::empty); } if (acc->delay->isOwnVCardPending()) acc->delay->receivedOwnVCard(vCard); } void Core::VCardHandler::handlePresenceOfMyAccountChange(const QXmppPresence& p_presence) { if (!acc->delay->isOwnVCardPending()) { switch (p_presence.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 (avatarType.size() > 0) acc->delay->getOwnVCard(); break; case QXmppPresence::VCardUpdateValidPhoto: //there is a photo, need to load if (avatarHash != p_presence.photoHash()) acc->delay->getOwnVCard(); break; } } } void Core::VCardHandler::uploadVCard(const Shared::VCard& card) { QXmppVCardIq iq; initializeQXmppVCard(iq, card); if (card.getAvatarType() != Shared::Avatar::empty) { QString newPath = card.getAvatarPath(); QString oldPath = getAvatarPath(); QByteArray data; QString type; if (newPath != oldPath) { QFile avatar(newPath); if (!avatar.open(QFile::ReadOnly)) { qDebug() << "An attempt to upload new vCard to account" << acc->name << "but it wasn't possible to read file" << newPath << "which was supposed to be new avatar, uploading old avatar"; if (avatarType.size() > 0) { QFile oA(oldPath); if (!oA.open(QFile::ReadOnly)) { qDebug() << "Couldn't read old avatar of account" << acc->name << ", uploading empty avatar"; } else { data = oA.readAll(); } } } else { data = avatar.readAll(); } } else { if (avatarType.size() > 0) { QFile oA(oldPath); if (!oA.open(QFile::ReadOnly)) qDebug() << "Couldn't read old avatar of account" << acc->name << ", uploading empty avatar"; else data = oA.readAll(); } } if (data.size() > 0) { QMimeDatabase db; type = db.mimeTypeForData(data).name(); iq.setPhoto(data); iq.setPhotoType(type); } } acc->vm->setClientVCard(iq); onOwnVCardReceived(iq); } QString Core::VCardHandler::getAvatarPath() const { if (avatarType.size() == 0) return ""; else return QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/" + acc->name + "/" + "avatar." + avatarType; }