313 lines
11 KiB
C++
313 lines
11 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 "vcardhandler.h"
|
|
#include "core/account.h"
|
|
|
|
Core::VCardHandler::VCardHandler(Account* account):
|
|
QObject(),
|
|
acc(account),
|
|
ownVCardRequestInProgress(false),
|
|
pendingVCardRequests(),
|
|
avatarHash(),
|
|
avatarType()
|
|
{
|
|
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);
|
|
|
|
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;
|
|
}
|
|
}
|
|
if (avatarType.size() != 0) {
|
|
acc->presence.setVCardUpdateType(QXmppPresence::VCardUpdateValidPhoto);
|
|
acc->presence.setPhotoHash(avatarHash.toUtf8());
|
|
} else {
|
|
acc->presence.setVCardUpdateType(QXmppPresence::VCardUpdateNotReady);
|
|
}
|
|
}
|
|
|
|
Core::VCardHandler::~VCardHandler()
|
|
{
|
|
|
|
}
|
|
|
|
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();
|
|
}
|
|
pendingVCardRequests.erase(id);
|
|
RosterItem* item = acc->rh->getRosterItem(jid);
|
|
|
|
if (item == 0) {
|
|
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);
|
|
|
|
emit acc->receivedVCard(jid, 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<QString, QVariant> 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);
|
|
}
|
|
|
|
ownVCardRequestInProgress = false;
|
|
|
|
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);
|
|
}
|
|
|
|
emit acc->receivedVCard(acc->getBareJid(), vCard);
|
|
}
|
|
|
|
void Core::VCardHandler::handleOffline()
|
|
{
|
|
pendingVCardRequests.clear();
|
|
Shared::VCard vCard; //just to show, that there is now more pending request
|
|
for (const QString& jid : pendingVCardRequests) {
|
|
emit acc->receivedVCard(jid, vCard); //need to show it better in the future, like with an error
|
|
}
|
|
pendingVCardRequests.clear();
|
|
ownVCardRequestInProgress = false;
|
|
}
|
|
|
|
void Core::VCardHandler::requestVCard(const QString& jid)
|
|
{
|
|
if (pendingVCardRequests.find(jid) == pendingVCardRequests.end()) {
|
|
qDebug() << "requesting vCard" << jid;
|
|
if (jid == acc->getBareJid()) {
|
|
if (!ownVCardRequestInProgress) {
|
|
acc->vm->requestClientVCard();
|
|
ownVCardRequestInProgress = true;
|
|
}
|
|
} else {
|
|
acc->vm->requestVCard(jid);
|
|
pendingVCardRequests.insert(jid);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Core::VCardHandler::handleOtherPresenceOfMyAccountChange(const QXmppPresence& p_presence)
|
|
{
|
|
if (!ownVCardRequestInProgress) {
|
|
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->vm->requestClientVCard();
|
|
ownVCardRequestInProgress = true;
|
|
}
|
|
break;
|
|
case QXmppPresence::VCardUpdateValidPhoto: //there is a photo, need to load
|
|
if (avatarHash != p_presence.photoHash()) {
|
|
acc->vm->requestClientVCard();
|
|
ownVCardRequestInProgress = true;
|
|
}
|
|
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;
|
|
}
|
|
}
|