1
0
Fork 0
forked from blue/squawk

receiving account owner vCard, displaying avatars in roster

This commit is contained in:
Blue 2019-10-16 22:38:35 +03:00
parent 64e33b6139
commit dc1ec1c9d4
13 changed files with 358 additions and 64 deletions

View file

@ -41,7 +41,10 @@ Account::Account(const QString& p_login, const QString& p_server, const QString&
maxReconnectTimes(0),
reconnectTimes(0),
queuedContacts(),
outOfRosterContacts()
outOfRosterContacts(),
avatarHash(),
avatarType(),
ownVCardRequestInProgress(false)
{
config.setUser(p_login);
config.setDomain(p_server);
@ -81,6 +84,52 @@ Account::Account(const QString& p_login, const QString& p_server, const QString&
QXmppVCardManager& vm = client.vCardManager();
QObject::connect(&vm, &QXmppVCardManager::vCardReceived, this, &Account::onVCardReceived);
//QObject::connect(&vm, &QXmppVCardManager::clientVCardReceived, this, &Account::onOwnVCardReceived); //for some reason it doesn't work, launching from common handler
QString path(QStandardPaths::writableLocation(QStandardPaths::CacheLocation));
path += "/" + name;
QDir dir(path);
if (!dir.exists()) {
bool res = dir.mkpath(path);
if (!res) {
qDebug() << "Couldn't create a cache directory for account" << name;
throw 22;
}
}
QFile* avatar = new QFile(path + "/avatar.png");
QString type = "png";
if (!avatar->exists()) {
delete avatar;
avatar = new QFile(path + "/avatar.jpg");
QString type = "jpg";
if (!avatar->exists()) {
delete avatar;
avatar = new QFile(path + "/avatar.jpeg");
QString type = "jpeg";
if (!avatar->exists()) {
delete avatar;
avatar = new QFile(path + "/avatar.gif");
QString 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) {
presence.setVCardUpdateType(QXmppPresence::VCardUpdateValidPhoto);
presence.setPhotoHash(avatarHash.toUtf8());
} else {
presence.setVCardUpdateType(QXmppPresence::VCardUpdateNotReady);
}
}
Account::~Account()
@ -191,6 +240,9 @@ QString Core::Account::getServer() const
void Core::Account::onRosterReceived()
{
client.vCardManager().requestClientVCard(); //TODO need to make sure server actually supports vCards
ownVCardRequestInProgress = true;
QXmppRosterManager& rm = client.rosterManager();
QStringList bj = rm.getRosterBareJids();
for (int i = 0; i < bj.size(); ++i) {
@ -283,14 +335,15 @@ void Core::Account::addedAccount(const QString& jid)
});
if (contact->hasAvatar()) {
if (contact->isAvatarAutoGenerated()) {
cData.insert("avatarType", static_cast<uint>(Shared::Avatar::valid));
if (!contact->isAvatarAutoGenerated()) {
cData.insert("avatarState", static_cast<uint>(Shared::Avatar::valid));
} else {
cData.insert("avatarType", static_cast<uint>(Shared::Avatar::autocreated));
cData.insert("avatarState", static_cast<uint>(Shared::Avatar::autocreated));
}
cData.insert("avatarPath", contact->avatarPath());
} else {
cData.insert("avatarType", static_cast<uint>(Shared::Avatar::empty));
cData.insert("avatarState", static_cast<uint>(Shared::Avatar::empty));
cData.insert("avatarPath", "");
client.vCardManager().requestVCard(jid);
pendingVCardRequests.insert(jid);
}
@ -337,9 +390,9 @@ void Core::Account::handleNewConference(Core::Conference* contact)
QObject::connect(contact, &Conference::removeParticipant, this, &Account::onMucRemoveParticipant);
}
void Core::Account::onPresenceReceived(const QXmppPresence& presence)
void Core::Account::onPresenceReceived(const QXmppPresence& p_presence)
{
QString id = presence.from();
QString id = p_presence.from();
QStringList comps = id.split("/");
QString jid = comps.front();
QString resource = comps.back();
@ -348,16 +401,35 @@ void Core::Account::onPresenceReceived(const QXmppPresence& presence)
if (jid == myJid) {
if (resource == getResource()) {
emit availabilityChanged(presence.availableStatusType());
emit availabilityChanged(p_presence.availableStatusType());
} else {
qDebug() << "Received a presence for another resource of my " << name << " account, skipping";
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) {
client.vCardManager().requestClientVCard();
ownVCardRequestInProgress = true;
}
break;
case QXmppPresence::VCardUpdateValidPhoto: //there is a photo, need to load
if (avatarHash != p_presence.photoHash()) {
client.vCardManager().requestClientVCard();
ownVCardRequestInProgress = true;
}
break;
}
}
}
} else {
if (pendingVCardRequests.find(jid) == pendingVCardRequests.end()) {
std::map<QString, Contact*>::const_iterator itr = contacts.find(jid);
if (itr != contacts.end()) {
Contact* cnt = itr->second;
switch (presence.vCardUpdateType()) {
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
@ -373,7 +445,7 @@ void Core::Account::onPresenceReceived(const QXmppPresence& presence)
client.vCardManager().requestVCard(jid);
pendingVCardRequests.insert(jid);
} else {
if (cnt->avatarHash() != presence.photoHash()) {
if (cnt->avatarHash() != p_presence.photoHash()) {
client.vCardManager().requestVCard(jid);
pendingVCardRequests.insert(jid);
}
@ -388,19 +460,19 @@ void Core::Account::onPresenceReceived(const QXmppPresence& presence)
}
}
switch (presence.type()) {
switch (p_presence.type()) {
case QXmppPresence::Error:
qDebug() << "An error reported by presence from" << id << presence.error().text();
qDebug() << "An error reported by presence from" << id << p_presence.error().text();
break;
case QXmppPresence::Available:{
QDateTime lastInteraction = presence.lastUserInteraction();
QDateTime lastInteraction = p_presence.lastUserInteraction();
if (!lastInteraction.isValid()) {
lastInteraction = QDateTime::currentDateTime();
}
emit addPresence(jid, resource, {
{"lastActivity", lastInteraction},
{"availability", presence.availableStatusType()}, //TODO check and handle invisible
{"status", presence.statusText()}
{"availability", p_presence.availableStatusType()}, //TODO check and handle invisible
{"status", p_presence.statusText()}
});
}
break;
@ -1267,7 +1339,11 @@ void Core::Account::onVCardReceived(const QXmppVCardIq& card)
if (contItr == contacts.end()) {
std::map<QString, Conference*>::const_iterator confItr = conferences.find(jid);
if (confItr == conferences.end()) {
qDebug() << "received vCard" << jid << "doesn't belong to any of known contacts or conferences, skipping";
if (jid == getLogin() + "@" + getServer()) {
onOwnVCardReceived(card);
} else {
qDebug() << "received vCard" << jid << "doesn't belong to any of known contacts or conferences, skipping";
}
return;
} else {
item = confItr->second;
@ -1284,15 +1360,99 @@ void Core::Account::onVCardReceived(const QXmppVCardIq& card)
}
}
void Core::Account::onOwnVCardReceived(const QXmppVCardIq& card)
{
QByteArray ava = card.photo();
bool changed = false;
QString path = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/" + 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" << 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();
changed = true;
} else {
qDebug() << "Received new avatar for account" << 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" << name;
}
}
}
}
} else {
QFile newAvatar(path + "avatar." + newType.preferredSuffix());
if (newAvatar.open(QFile::WriteOnly)) {
newAvatar.write(ava);
newAvatar.close();
avatarHash = newHash;
avatarType = newType.preferredSuffix();
changed = true;
} else {
qDebug() << "Received new avatar for account" << name << "but can't save it";
}
}
} else {
if (avatarType.size() > 0) {
QFile oldAvatar(path + "avatar." + avatarType);
if (!oldAvatar.remove()) {
qDebug() << "Received vCard for account" << name << "without avatar, but can't get rid of the file, doing nothing";
} else {
changed = true;
}
}
}
if (changed) {
QMap<QString, QVariant> change;
if (avatarType.size() > 0) {
presence.setPhotoHash(avatarHash.toUtf8());
presence.setVCardUpdateType(QXmppPresence::VCardUpdateValidPhoto);
change.insert("avatarPath", path + "avatar." + avatarType);
} else {
presence.setPhotoHash("");
presence.setVCardUpdateType(QXmppPresence::VCardUpdateNoPhoto);
change.insert("avatarPath", "");
}
client.setClientPresence(presence);
}
ownVCardRequestInProgress = false;
}
QString Core::Account::getAvatarPath() const
{
return QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/" + name + "/" + "avatar." + avatarType;
}
void Core::Account::onContactAvatarChanged(Shared::Avatar type, const QString& path)
{
RosterItem* item = static_cast<RosterItem*>(sender());
QMap<QString, QVariant> cData({
{"avatarType", static_cast<uint>(type)}
{"avatarState", static_cast<uint>(type)},
{"avatarPath", path}
});
if (type != Shared::Avatar::empty) {
cData.insert("avatarPath", path);
}
emit changeContact(item->jid, cData);
}