forked from blue/squawk
muc participant avatars
This commit is contained in:
parent
efc90e18c3
commit
55703c2007
@ -349,13 +349,15 @@ void Core::Account::addedAccount(const QString& jid)
|
|||||||
{"state", state}
|
{"state", state}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (contact->hasAvatar()) {
|
Archive::AvatarInfo info;
|
||||||
if (!contact->isAvatarAutoGenerated()) {
|
bool hasAvatar = contact->readAvatarInfo(info);
|
||||||
|
if (hasAvatar) {
|
||||||
|
if (info.autogenerated) {
|
||||||
cData.insert("avatarState", static_cast<uint>(Shared::Avatar::valid));
|
cData.insert("avatarState", static_cast<uint>(Shared::Avatar::valid));
|
||||||
} else {
|
} else {
|
||||||
cData.insert("avatarState", static_cast<uint>(Shared::Avatar::autocreated));
|
cData.insert("avatarState", static_cast<uint>(Shared::Avatar::autocreated));
|
||||||
}
|
}
|
||||||
cData.insert("avatarPath", contact->avatarPath());
|
cData.insert("avatarPath", contact->avatarPath() + "." + info.type);
|
||||||
} else {
|
} else {
|
||||||
cData.insert("avatarState", static_cast<uint>(Shared::Avatar::empty));
|
cData.insert("avatarState", static_cast<uint>(Shared::Avatar::empty));
|
||||||
cData.insert("avatarPath", "");
|
cData.insert("avatarPath", "");
|
||||||
@ -382,6 +384,7 @@ void Core::Account::handleNewRosterItem(Core::RosterItem* contact)
|
|||||||
QObject::connect(contact, &RosterItem::historyResponse, this, &Account::onContactHistoryResponse);
|
QObject::connect(contact, &RosterItem::historyResponse, this, &Account::onContactHistoryResponse);
|
||||||
QObject::connect(contact, &RosterItem::nameChanged, this, &Account::onContactNameChanged);
|
QObject::connect(contact, &RosterItem::nameChanged, this, &Account::onContactNameChanged);
|
||||||
QObject::connect(contact, &RosterItem::avatarChanged, this, &Account::onContactAvatarChanged);
|
QObject::connect(contact, &RosterItem::avatarChanged, this, &Account::onContactAvatarChanged);
|
||||||
|
QObject::connect(contact, &RosterItem::requestVCard, this, &Account::requestVCard);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Account::handleNewContact(Core::Contact* contact)
|
void Core::Account::handleNewContact(Core::Contact* contact)
|
||||||
@ -440,42 +443,9 @@ void Core::Account::onPresenceReceived(const QXmppPresence& p_presence)
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (pendingVCardRequests.find(jid) == pendingVCardRequests.end()) {
|
if (pendingVCardRequests.find(jid) == pendingVCardRequests.end()) {
|
||||||
RosterItem* item = 0;
|
|
||||||
std::map<QString, Contact*>::const_iterator itr = contacts.find(jid);
|
std::map<QString, Contact*>::const_iterator itr = contacts.find(jid);
|
||||||
if (itr != contacts.end()) {
|
if (itr != contacts.end()) {
|
||||||
item = itr->second;
|
itr->second->handlePresence(p_presence);
|
||||||
} else {
|
|
||||||
std::map<QString, Conference*>::const_iterator citr = conferences.find(jid);
|
|
||||||
if (citr != conferences.end()) {
|
|
||||||
item = citr->second;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (item != 0) {
|
|
||||||
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 (!item->hasAvatar() || (item->hasAvatar() && !item->isAvatarAutoGenerated())) {
|
|
||||||
item->setAutoGeneratedAvatar();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case QXmppPresence::VCardUpdateValidPhoto: //there is a photo, need to load
|
|
||||||
if (item->hasAvatar()) {
|
|
||||||
if (item->isAvatarAutoGenerated()) {
|
|
||||||
requestVCard(jid);
|
|
||||||
} else {
|
|
||||||
if (item->avatarHash() != p_presence.photoHash()) {
|
|
||||||
requestVCard(jid);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
requestVCard(jid);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1333,13 +1303,15 @@ void Core::Account::addNewRoom(const QString& jid, const QString& nick, const QS
|
|||||||
{"name", conf->getName()}
|
{"name", conf->getName()}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (conf->hasAvatar()) {
|
Archive::AvatarInfo info;
|
||||||
if (!conf->isAvatarAutoGenerated()) {
|
bool hasAvatar = conf->readAvatarInfo(info);
|
||||||
|
if (hasAvatar) {
|
||||||
|
if (info.autogenerated) {
|
||||||
cData.insert("avatarState", static_cast<uint>(Shared::Avatar::valid));
|
cData.insert("avatarState", static_cast<uint>(Shared::Avatar::valid));
|
||||||
} else {
|
} else {
|
||||||
cData.insert("avatarState", static_cast<uint>(Shared::Avatar::autocreated));
|
cData.insert("avatarState", static_cast<uint>(Shared::Avatar::autocreated));
|
||||||
}
|
}
|
||||||
cData.insert("avatarPath", conf->avatarPath());
|
cData.insert("avatarPath", conf->avatarPath() + "." + info.type);
|
||||||
} else {
|
} else {
|
||||||
cData.insert("avatarState", static_cast<uint>(Shared::Avatar::empty));
|
cData.insert("avatarState", static_cast<uint>(Shared::Avatar::empty));
|
||||||
cData.insert("avatarPath", "");
|
cData.insert("avatarPath", "");
|
||||||
@ -1406,8 +1378,14 @@ void Core::Account::renameContactRequest(const QString& jid, const QString& newN
|
|||||||
|
|
||||||
void Core::Account::onVCardReceived(const QXmppVCardIq& card)
|
void Core::Account::onVCardReceived(const QXmppVCardIq& card)
|
||||||
{
|
{
|
||||||
QString jid = card.from();
|
QString id = card.from();
|
||||||
pendingVCardRequests.erase(jid);
|
QStringList comps = id.split("/");
|
||||||
|
QString jid = comps.front();
|
||||||
|
QString resource("");
|
||||||
|
if (comps.size() > 1) {
|
||||||
|
resource = comps.back();
|
||||||
|
}
|
||||||
|
pendingVCardRequests.erase(id);
|
||||||
RosterItem* item = 0;
|
RosterItem* item = 0;
|
||||||
|
|
||||||
std::map<QString, Contact*>::const_iterator contItr = contacts.find(jid);
|
std::map<QString, Contact*>::const_iterator contItr = contacts.find(jid);
|
||||||
@ -1427,35 +1405,8 @@ void Core::Account::onVCardReceived(const QXmppVCardIq& card)
|
|||||||
item = contItr->second;
|
item = contItr->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArray ava = card.photo();
|
Shared::VCard vCard = item->handleResponseVCard(card, resource);
|
||||||
|
|
||||||
if (ava.size() > 0) {
|
|
||||||
item->setAvatar(ava);
|
|
||||||
} else {
|
|
||||||
if (!item->hasAvatar() || !item->isAvatarAutoGenerated()) {
|
|
||||||
item->setAutoGeneratedAvatar();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Shared::VCard vCard;
|
|
||||||
initializeVCard(vCard, card);
|
|
||||||
|
|
||||||
if (item->hasAvatar()) {
|
|
||||||
if (!item->isAvatarAutoGenerated()) {
|
|
||||||
vCard.setAvatarType(Shared::Avatar::valid);
|
|
||||||
} else {
|
|
||||||
vCard.setAvatarType(Shared::Avatar::autocreated);
|
|
||||||
}
|
|
||||||
vCard.setAvatarPath(item->avatarPath());
|
|
||||||
} else {
|
|
||||||
vCard.setAvatarType(Shared::Avatar::empty);
|
|
||||||
}
|
|
||||||
|
|
||||||
QMap<QString, QVariant> cd = {
|
|
||||||
{"avatarState", static_cast<quint8>(vCard.getAvatarType())},
|
|
||||||
{"avatarPath", vCard.getAvatarPath()}
|
|
||||||
};
|
|
||||||
emit changeContact(jid, cd);
|
|
||||||
emit receivedVCard(jid, vCard);
|
emit receivedVCard(jid, vCard);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1577,6 +1528,7 @@ void Core::Account::onContactAvatarChanged(Shared::Avatar type, const QString& p
|
|||||||
void Core::Account::requestVCard(const QString& jid)
|
void Core::Account::requestVCard(const QString& jid)
|
||||||
{
|
{
|
||||||
if (pendingVCardRequests.find(jid) == pendingVCardRequests.end()) {
|
if (pendingVCardRequests.find(jid) == pendingVCardRequests.end()) {
|
||||||
|
qDebug() << "requesting vCard" << jid;
|
||||||
if (jid == getLogin() + "@" + getServer()) {
|
if (jid == getLogin() + "@" + getServer()) {
|
||||||
if (!ownVCardRequestInProgress) {
|
if (!ownVCardRequestInProgress) {
|
||||||
vm->requestClientVCard();
|
vm->requestClientVCard();
|
||||||
|
@ -92,9 +92,11 @@ public:
|
|||||||
void setRoomAutoJoin(const QString& jid, bool joined);
|
void setRoomAutoJoin(const QString& jid, bool joined);
|
||||||
void removeRoomRequest(const QString& jid);
|
void removeRoomRequest(const QString& jid);
|
||||||
void addRoomRequest(const QString& jid, const QString& nick, const QString& password, bool autoJoin);
|
void addRoomRequest(const QString& jid, const QString& nick, const QString& password, bool autoJoin);
|
||||||
void requestVCard(const QString& jid);
|
|
||||||
void uploadVCard(const Shared::VCard& card);
|
void uploadVCard(const Shared::VCard& card);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void requestVCard(const QString& jid);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void changed(const QMap<QString, QVariant>& data);
|
void changed(const QMap<QString, QVariant>& data);
|
||||||
void connectionStateChanged(int);
|
void connectionStateChanged(int);
|
||||||
|
232
core/archive.cpp
232
core/archive.cpp
@ -33,10 +33,7 @@ Core::Archive::Archive(const QString& p_jid, QObject* parent):
|
|||||||
main(),
|
main(),
|
||||||
order(),
|
order(),
|
||||||
stats(),
|
stats(),
|
||||||
hasAvatar(false),
|
avatars()
|
||||||
avatarAutoGenerated(false),
|
|
||||||
avatarHash(),
|
|
||||||
avatarType()
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -60,7 +57,7 @@ void Core::Archive::open(const QString& account)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mdb_env_set_maxdbs(environment, 4);
|
mdb_env_set_maxdbs(environment, 5);
|
||||||
mdb_env_set_mapsize(environment, 512UL * 1024UL * 1024UL);
|
mdb_env_set_mapsize(environment, 512UL * 1024UL * 1024UL);
|
||||||
mdb_env_open(environment, path.toStdString().c_str(), 0, 0664);
|
mdb_env_open(environment, path.toStdString().c_str(), 0, 0664);
|
||||||
|
|
||||||
@ -69,43 +66,25 @@ void Core::Archive::open(const QString& account)
|
|||||||
mdb_dbi_open(txn, "main", MDB_CREATE, &main);
|
mdb_dbi_open(txn, "main", MDB_CREATE, &main);
|
||||||
mdb_dbi_open(txn, "order", MDB_CREATE | MDB_INTEGERKEY, &order);
|
mdb_dbi_open(txn, "order", MDB_CREATE | MDB_INTEGERKEY, &order);
|
||||||
mdb_dbi_open(txn, "stats", MDB_CREATE, &stats);
|
mdb_dbi_open(txn, "stats", MDB_CREATE, &stats);
|
||||||
|
mdb_dbi_open(txn, "avatars", MDB_CREATE, &avatars);
|
||||||
mdb_txn_commit(txn);
|
mdb_txn_commit(txn);
|
||||||
|
|
||||||
mdb_txn_begin(environment, NULL, 0, &txn);
|
mdb_txn_begin(environment, NULL, MDB_RDONLY, &txn);
|
||||||
try {
|
try {
|
||||||
fromTheBeginning = getStatBoolValue("beginning", txn);
|
fromTheBeginning = getStatBoolValue("beginning", txn);
|
||||||
} catch (const NotFound& e) {
|
} catch (const NotFound& e) {
|
||||||
fromTheBeginning = false;
|
fromTheBeginning = false;
|
||||||
}
|
}
|
||||||
try {
|
|
||||||
hasAvatar = getStatBoolValue("hasAvatar", txn);
|
|
||||||
} catch (const NotFound& e) {
|
|
||||||
hasAvatar = false;
|
|
||||||
}
|
|
||||||
if (hasAvatar) {
|
|
||||||
try {
|
|
||||||
avatarAutoGenerated = getStatBoolValue("avatarAutoGenerated", txn);
|
|
||||||
} catch (const NotFound& e) {
|
|
||||||
avatarAutoGenerated = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
avatarType = getStatStringValue("avatarType", txn).c_str();
|
std::string sJid = jid.toStdString();
|
||||||
if (avatarAutoGenerated) {
|
AvatarInfo info;
|
||||||
avatarHash = "";
|
bool hasAvatar = readAvatarInfo(info, sJid, txn);
|
||||||
} else {
|
|
||||||
avatarHash = getStatStringValue("avatarHash", txn).c_str();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
avatarAutoGenerated = false;
|
|
||||||
avatarHash = "";
|
|
||||||
avatarType = "";
|
|
||||||
}
|
|
||||||
mdb_txn_abort(txn);
|
mdb_txn_abort(txn);
|
||||||
|
|
||||||
if (hasAvatar) {
|
if (hasAvatar) {
|
||||||
QFile ava(path + "/avatar." + avatarType);
|
QFile ava(path + "/" + sJid.c_str() + "." + info.type);
|
||||||
if (!ava.exists()) {
|
if (!ava.exists()) {
|
||||||
bool success = dropAvatar();
|
bool success = dropAvatar(sJid);
|
||||||
if (!success) {
|
if (!success) {
|
||||||
qDebug() << "error opening archive" << jid << "for account" << account
|
qDebug() << "error opening archive" << jid << "for account" << account
|
||||||
<< ". There is supposed to be avatar but the file doesn't exist, couldn't even drop it, it surely will lead to an error";
|
<< ". There is supposed to be avatar but the file doesn't exist, couldn't even drop it, it surely will lead to an error";
|
||||||
@ -120,6 +99,7 @@ void Core::Archive::open(const QString& account)
|
|||||||
void Core::Archive::close()
|
void Core::Archive::close()
|
||||||
{
|
{
|
||||||
if (opened) {
|
if (opened) {
|
||||||
|
mdb_dbi_close(environment, avatars);
|
||||||
mdb_dbi_close(environment, stats);
|
mdb_dbi_close(environment, stats);
|
||||||
mdb_dbi_close(environment, order);
|
mdb_dbi_close(environment, order);
|
||||||
mdb_dbi_close(environment, main);
|
mdb_dbi_close(environment, main);
|
||||||
@ -518,8 +498,9 @@ bool Core::Archive::getStatBoolValue(const std::string& id, MDB_txn* txn)
|
|||||||
if (rc == MDB_NOTFOUND) {
|
if (rc == MDB_NOTFOUND) {
|
||||||
throw NotFound(id, jid.toStdString());
|
throw NotFound(id, jid.toStdString());
|
||||||
} else if (rc) {
|
} else if (rc) {
|
||||||
qDebug() << "error retrieving" << id.c_str() << "from stats db of" << jid << mdb_strerror(rc);
|
std::string err(mdb_strerror(rc));
|
||||||
throw 15; //TODO proper exception
|
qDebug() << "error retrieving" << id.c_str() << "from stats db of" << jid << err.c_str();
|
||||||
|
throw Unknown(jid.toStdString(), err);
|
||||||
} else {
|
} else {
|
||||||
uint8_t value = *(uint8_t*)(lmdbData.mv_data);
|
uint8_t value = *(uint8_t*)(lmdbData.mv_data);
|
||||||
bool is;
|
bool is;
|
||||||
@ -546,8 +527,9 @@ std::string Core::Archive::getStatStringValue(const std::string& id, MDB_txn* tx
|
|||||||
if (rc == MDB_NOTFOUND) {
|
if (rc == MDB_NOTFOUND) {
|
||||||
throw NotFound(id, jid.toStdString());
|
throw NotFound(id, jid.toStdString());
|
||||||
} else if (rc) {
|
} else if (rc) {
|
||||||
qDebug() << "error retrieving" << id.c_str() << "from stats db of" << jid << mdb_strerror(rc);
|
std::string err(mdb_strerror(rc));
|
||||||
throw 15; //TODO proper exception
|
qDebug() << "error retrieving" << id.c_str() << "from stats db of" << jid << err.c_str();
|
||||||
|
throw Unknown(jid.toStdString(), err);
|
||||||
} else {
|
} else {
|
||||||
std::string value((char*)lmdbData.mv_data, lmdbData.mv_size);
|
std::string value((char*)lmdbData.mv_data, lmdbData.mv_size);
|
||||||
return value;
|
return value;
|
||||||
@ -588,74 +570,38 @@ bool Core::Archive::setStatValue(const std::string& id, const std::string& value
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Core::Archive::getHasAvatar() const
|
bool Core::Archive::dropAvatar(const std::string& resource)
|
||||||
{
|
|
||||||
if (!opened) {
|
|
||||||
throw Closed("getHasAvatar", jid.toStdString());
|
|
||||||
}
|
|
||||||
|
|
||||||
return hasAvatar;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Core::Archive::getAutoAvatar() const
|
|
||||||
{
|
|
||||||
if (!opened) {
|
|
||||||
throw Closed("getAutoAvatar", jid.toStdString());
|
|
||||||
}
|
|
||||||
|
|
||||||
return avatarAutoGenerated;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString Core::Archive::getAvatarHash() const
|
|
||||||
{
|
|
||||||
if (!opened) {
|
|
||||||
throw Closed("getAvatarHash", jid.toStdString());
|
|
||||||
}
|
|
||||||
|
|
||||||
return avatarHash;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString Core::Archive::getAvatarType() const
|
|
||||||
{
|
|
||||||
if (!opened) {
|
|
||||||
throw Closed("getAvatarType", jid.toStdString());
|
|
||||||
}
|
|
||||||
|
|
||||||
return avatarType;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Core::Archive::dropAvatar()
|
|
||||||
{
|
{
|
||||||
MDB_txn *txn;
|
MDB_txn *txn;
|
||||||
|
MDB_val lmdbKey;
|
||||||
mdb_txn_begin(environment, NULL, 0, &txn);
|
mdb_txn_begin(environment, NULL, 0, &txn);
|
||||||
bool success = setStatValue("hasAvatar", false, txn);
|
lmdbKey.mv_size = resource.size();
|
||||||
success = success && setStatValue("avatarAutoGenerated", false, txn);
|
lmdbKey.mv_data = (char*)resource.c_str();
|
||||||
success = success && setStatValue("avatarHash", "", txn);
|
int rc = mdb_del(txn, avatars, &lmdbKey, NULL);
|
||||||
success = success && setStatValue("avatarType", "", txn);
|
if (rc != 0) {
|
||||||
if (!success) {
|
|
||||||
mdb_txn_abort(txn);
|
mdb_txn_abort(txn);
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
hasAvatar = false;
|
|
||||||
avatarAutoGenerated = false;
|
|
||||||
avatarHash = "";
|
|
||||||
avatarType = "";
|
|
||||||
mdb_txn_commit(txn);
|
mdb_txn_commit(txn);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Core::Archive::setAvatar(const QByteArray& data, bool generated)
|
bool Core::Archive::setAvatar(const QByteArray& data, bool generated, const QString& resource)
|
||||||
{
|
{
|
||||||
if (!opened) {
|
if (!opened) {
|
||||||
throw Closed("setAvatar", jid.toStdString());
|
throw Closed("setAvatar", jid.toStdString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AvatarInfo oldInfo;
|
||||||
|
bool hasAvatar = readAvatarInfo(oldInfo, resource);
|
||||||
|
std::string res = resource.size() == 0 ? jid.toStdString() : resource.toStdString();
|
||||||
|
|
||||||
if (data.size() == 0) {
|
if (data.size() == 0) {
|
||||||
if (!hasAvatar) {
|
if (!hasAvatar) {
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
return dropAvatar();
|
return dropAvatar(res);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const char* cep;
|
const char* cep;
|
||||||
@ -664,14 +610,14 @@ bool Core::Archive::setAvatar(const QByteArray& data, bool generated)
|
|||||||
bool needToRemoveOld = false;
|
bool needToRemoveOld = false;
|
||||||
QCryptographicHash hash(QCryptographicHash::Sha1);
|
QCryptographicHash hash(QCryptographicHash::Sha1);
|
||||||
hash.addData(data);
|
hash.addData(data);
|
||||||
QString newHash(hash.result());
|
QByteArray newHash(hash.result());
|
||||||
if (hasAvatar) {
|
if (hasAvatar) {
|
||||||
if (!generated && !avatarAutoGenerated && avatarHash == newHash) {
|
if (!generated && !oldInfo.autogenerated && oldInfo.hash == newHash) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
QFile oldAvatar(currentPath + "/avatar." + avatarType);
|
QFile oldAvatar(currentPath + "/" + res.c_str() + "." + oldInfo.type);
|
||||||
if (oldAvatar.exists()) {
|
if (oldAvatar.exists()) {
|
||||||
if (oldAvatar.rename(currentPath + "/avatar." + avatarType + ".bak")) {
|
if (oldAvatar.rename(currentPath + "/" + res.c_str() + "." + oldInfo.type + ".bak")) {
|
||||||
needToRemoveOld = true;
|
needToRemoveOld = true;
|
||||||
} else {
|
} else {
|
||||||
qDebug() << "Can't change avatar: couldn't get rid of the old avatar" << oldAvatar.fileName();
|
qDebug() << "Can't change avatar: couldn't get rid of the old avatar" << oldAvatar.fileName();
|
||||||
@ -682,33 +628,36 @@ bool Core::Archive::setAvatar(const QByteArray& data, bool generated)
|
|||||||
QMimeDatabase db;
|
QMimeDatabase db;
|
||||||
QMimeType type = db.mimeTypeForData(data);
|
QMimeType type = db.mimeTypeForData(data);
|
||||||
QString ext = type.preferredSuffix();
|
QString ext = type.preferredSuffix();
|
||||||
QFile newAvatar(currentPath + "/avatar." + ext);
|
QFile newAvatar(currentPath + "/" + res.c_str() + "." + ext);
|
||||||
if (newAvatar.open(QFile::WriteOnly)) {
|
if (newAvatar.open(QFile::WriteOnly)) {
|
||||||
newAvatar.write(data);
|
newAvatar.write(data);
|
||||||
newAvatar.close();
|
newAvatar.close();
|
||||||
|
|
||||||
MDB_txn *txn;
|
MDB_txn *txn;
|
||||||
mdb_txn_begin(environment, NULL, 0, &txn);
|
mdb_txn_begin(environment, NULL, 0, &txn);
|
||||||
bool success = setStatValue("hasAvatar", true, txn);
|
|
||||||
success = success && setStatValue("avatarAutoGenerated", generated, txn);
|
MDB_val lmdbKey, lmdbData;
|
||||||
success = success && setStatValue("avatarHash", newHash.toStdString(), txn);
|
QByteArray value;
|
||||||
success = success && setStatValue("avatarType", ext.toStdString(), txn);
|
AvatarInfo newInfo(ext, newHash, generated);
|
||||||
if (!success) {
|
newInfo.serialize(&value);
|
||||||
|
lmdbKey.mv_size = res.size();
|
||||||
|
lmdbKey.mv_data = (char*)res.c_str();
|
||||||
|
lmdbData.mv_size = value.size();
|
||||||
|
lmdbData.mv_data = value.data();
|
||||||
|
int rc = mdb_put(txn, avatars, &lmdbKey, &lmdbData, 0);
|
||||||
|
|
||||||
|
if (rc != 0) {
|
||||||
qDebug() << "Can't change avatar: couldn't store changes to database for" << newAvatar.fileName() << "rolling back to the previous state";
|
qDebug() << "Can't change avatar: couldn't store changes to database for" << newAvatar.fileName() << "rolling back to the previous state";
|
||||||
if (needToRemoveOld) {
|
if (needToRemoveOld) {
|
||||||
QFile oldAvatar(currentPath + "/avatar." + avatarType + ".bak");
|
QFile oldAvatar(currentPath + "/" + res.c_str() + "." + oldInfo.type + ".bak");
|
||||||
oldAvatar.rename(currentPath + "/avatar." + avatarType);
|
oldAvatar.rename(currentPath + "/" + res.c_str() + "." + oldInfo.type);
|
||||||
}
|
}
|
||||||
mdb_txn_abort(txn);
|
mdb_txn_abort(txn);
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
hasAvatar = true;
|
|
||||||
avatarAutoGenerated = generated;
|
|
||||||
avatarHash = newHash;
|
|
||||||
avatarType = ext;
|
|
||||||
mdb_txn_commit(txn);
|
mdb_txn_commit(txn);
|
||||||
if (needToRemoveOld) {
|
if (needToRemoveOld) {
|
||||||
QFile oldAvatar(currentPath + "/avatar." + avatarType + ".bak");
|
QFile oldAvatar(currentPath + "/" + res.c_str() + "." + oldInfo.type + ".bak");
|
||||||
oldAvatar.remove();
|
oldAvatar.remove();
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@ -716,10 +665,91 @@ bool Core::Archive::setAvatar(const QByteArray& data, bool generated)
|
|||||||
} else {
|
} else {
|
||||||
qDebug() << "Can't change avatar: cant open file to write" << newAvatar.fileName() << "rolling back to the previous state";
|
qDebug() << "Can't change avatar: cant open file to write" << newAvatar.fileName() << "rolling back to the previous state";
|
||||||
if (needToRemoveOld) {
|
if (needToRemoveOld) {
|
||||||
QFile oldAvatar(currentPath + "/avatar." + avatarType + ".bak");
|
QFile oldAvatar(currentPath + "/" + res.c_str() + "." + oldInfo.type + ".bak");
|
||||||
oldAvatar.rename(currentPath + "/avatar." + avatarType);
|
oldAvatar.rename(currentPath + "/" + res.c_str() + "." + oldInfo.type);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Core::Archive::readAvatarInfo(Core::Archive::AvatarInfo& target, const QString& resource) const
|
||||||
|
{
|
||||||
|
if (!opened) {
|
||||||
|
throw Closed("readAvatarInfo", jid.toStdString());
|
||||||
|
}
|
||||||
|
std::string res = resource.size() == 0 ? jid.toStdString() : resource.toStdString();
|
||||||
|
|
||||||
|
MDB_txn *txn;
|
||||||
|
mdb_txn_begin(environment, NULL, MDB_RDONLY, &txn);
|
||||||
|
|
||||||
|
try {
|
||||||
|
bool success = readAvatarInfo(target, res, txn);
|
||||||
|
mdb_txn_abort(txn);
|
||||||
|
return success;
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
mdb_txn_abort(txn);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Core::Archive::readAvatarInfo(Core::Archive::AvatarInfo& target, const std::string& res, MDB_txn* txn) const
|
||||||
|
{
|
||||||
|
MDB_val lmdbKey, lmdbData;
|
||||||
|
lmdbKey.mv_size = res.size();
|
||||||
|
lmdbKey.mv_data = (char*)res.c_str();
|
||||||
|
|
||||||
|
int rc;
|
||||||
|
rc = mdb_get(txn, avatars, &lmdbKey, &lmdbData);
|
||||||
|
if (rc == MDB_NOTFOUND) {
|
||||||
|
return false;
|
||||||
|
} else if (rc) {
|
||||||
|
std::string err(mdb_strerror(rc));
|
||||||
|
qDebug() << "error reading avatar info for" << res.c_str() << "resource of" << jid << err.c_str();
|
||||||
|
throw Unknown(jid.toStdString(), err);
|
||||||
|
} else {
|
||||||
|
target.deserialize((char*)lmdbData.mv_data, lmdbData.mv_size);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Core::Archive::AvatarInfo Core::Archive::getAvatarInfo(const QString& resource) const
|
||||||
|
{
|
||||||
|
if (!opened) {
|
||||||
|
throw Closed("readAvatarInfo", jid.toStdString());
|
||||||
|
}
|
||||||
|
|
||||||
|
AvatarInfo info;
|
||||||
|
bool success = readAvatarInfo(info, resource);
|
||||||
|
if (success) {
|
||||||
|
return info;
|
||||||
|
} else {
|
||||||
|
throw NoAvatar(jid.toStdString(), resource.toStdString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Core::Archive::AvatarInfo::AvatarInfo():
|
||||||
|
type(),
|
||||||
|
hash(),
|
||||||
|
autogenerated(false) {}
|
||||||
|
|
||||||
|
Core::Archive::AvatarInfo::AvatarInfo(const QString& p_type, const QByteArray& p_hash, bool p_autogenerated):
|
||||||
|
type(p_type),
|
||||||
|
hash(p_hash),
|
||||||
|
autogenerated(p_autogenerated) {}
|
||||||
|
|
||||||
|
void Core::Archive::AvatarInfo::deserialize(char* pointer, uint32_t size)
|
||||||
|
{
|
||||||
|
QByteArray data = QByteArray::fromRawData(pointer, size);
|
||||||
|
QDataStream in(&data, QIODevice::ReadOnly);
|
||||||
|
|
||||||
|
in >> type >> hash >> autogenerated;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Core::Archive::AvatarInfo::serialize(QByteArray* ba) const
|
||||||
|
{
|
||||||
|
QDataStream out(ba, QIODevice::WriteOnly);
|
||||||
|
|
||||||
|
out << type << hash << autogenerated;
|
||||||
|
}
|
||||||
|
@ -35,6 +35,8 @@ class Archive : public QObject
|
|||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
|
class AvatarInfo;
|
||||||
|
|
||||||
Archive(const QString& jid, QObject* parent = 0);
|
Archive(const QString& jid, QObject* parent = 0);
|
||||||
~Archive();
|
~Archive();
|
||||||
|
|
||||||
@ -53,11 +55,9 @@ public:
|
|||||||
std::list<Shared::Message> getBefore(int count, const QString& id);
|
std::list<Shared::Message> getBefore(int count, const QString& id);
|
||||||
bool isFromTheBeginning();
|
bool isFromTheBeginning();
|
||||||
void setFromTheBeginning(bool is);
|
void setFromTheBeginning(bool is);
|
||||||
bool getHasAvatar() const;
|
bool setAvatar(const QByteArray& data, bool generated = false, const QString& resource = "");
|
||||||
bool getAutoAvatar() const;
|
AvatarInfo getAvatarInfo(const QString& resource = "") const;
|
||||||
QString getAvatarHash() const;
|
bool readAvatarInfo(AvatarInfo& target, const QString& resource = "") const;
|
||||||
QString getAvatarType() const;
|
|
||||||
bool setAvatar(const QByteArray& data, bool generated = false);
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
const QString jid;
|
const QString jid;
|
||||||
@ -121,6 +121,22 @@ public:
|
|||||||
std::string key;
|
std::string key;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class NoAvatar:
|
||||||
|
public Utils::Exception
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
NoAvatar(const std::string& el, const std::string& res):Exception(), element(el), resource(res){
|
||||||
|
if (resource.size() == 0) {
|
||||||
|
resource = "for himself";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string getMessage() const{return "Element " + element + " has no avatar for " + resource ;}
|
||||||
|
private:
|
||||||
|
std::string element;
|
||||||
|
std::string resource;
|
||||||
|
};
|
||||||
|
|
||||||
class Unknown:
|
class Unknown:
|
||||||
public Utils::Exception
|
public Utils::Exception
|
||||||
{
|
{
|
||||||
@ -133,6 +149,20 @@ public:
|
|||||||
std::string msg;
|
std::string msg;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class AvatarInfo {
|
||||||
|
public:
|
||||||
|
AvatarInfo();
|
||||||
|
AvatarInfo(const QString& type, const QByteArray& hash, bool autogenerated);
|
||||||
|
|
||||||
|
void deserialize(char* pointer, uint32_t size);
|
||||||
|
void serialize(QByteArray* ba) const;
|
||||||
|
|
||||||
|
QString type;
|
||||||
|
QByteArray hash;
|
||||||
|
bool autogenerated;
|
||||||
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool opened;
|
bool opened;
|
||||||
bool fromTheBeginning;
|
bool fromTheBeginning;
|
||||||
@ -140,19 +170,17 @@ private:
|
|||||||
MDB_dbi main;
|
MDB_dbi main;
|
||||||
MDB_dbi order;
|
MDB_dbi order;
|
||||||
MDB_dbi stats;
|
MDB_dbi stats;
|
||||||
bool hasAvatar;
|
MDB_dbi avatars;
|
||||||
bool avatarAutoGenerated;
|
|
||||||
QString avatarHash;
|
|
||||||
QString avatarType;
|
|
||||||
|
|
||||||
bool getStatBoolValue(const std::string& id, MDB_txn* txn);
|
bool getStatBoolValue(const std::string& id, MDB_txn* txn);
|
||||||
std::string getStatStringValue(const std::string& id, MDB_txn* txn);
|
std::string getStatStringValue(const std::string& id, MDB_txn* txn);
|
||||||
|
|
||||||
bool setStatValue(const std::string& id, bool value, MDB_txn* txn);
|
bool setStatValue(const std::string& id, bool value, MDB_txn* txn);
|
||||||
bool setStatValue(const std::string& id, const std::string& value, MDB_txn* txn);
|
bool setStatValue(const std::string& id, const std::string& value, MDB_txn* txn);
|
||||||
|
bool readAvatarInfo(AvatarInfo& target, const std::string& res, MDB_txn* txn) const;
|
||||||
void printOrder();
|
void printOrder();
|
||||||
void printKeys();
|
void printKeys();
|
||||||
bool dropAvatar();
|
bool dropAvatar(const std::string& resource);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -143,14 +143,31 @@ void Core::Conference::onRoomParticipantAdded(const QString& p_name)
|
|||||||
lastInteraction = QDateTime::currentDateTime();
|
lastInteraction = QDateTime::currentDateTime();
|
||||||
}
|
}
|
||||||
QXmppMucItem mi = pres.mucItem();
|
QXmppMucItem mi = pres.mucItem();
|
||||||
|
Archive::AvatarInfo info;
|
||||||
|
bool hasAvatar = readAvatarInfo(info, resource);
|
||||||
|
|
||||||
emit addParticipant(resource, {
|
QMap<QString, QVariant> cData = {
|
||||||
{"lastActivity", lastInteraction},
|
{"lastActivity", lastInteraction},
|
||||||
{"availability", pres.availableStatusType()},
|
{"availability", pres.availableStatusType()},
|
||||||
{"status", pres.statusText()},
|
{"status", pres.statusText()},
|
||||||
{"affiliation", mi.affiliation()},
|
{"affiliation", mi.affiliation()},
|
||||||
{"role", mi.role()}
|
{"role", mi.role()}
|
||||||
});
|
};
|
||||||
|
|
||||||
|
if (hasAvatar) {
|
||||||
|
if (info.autogenerated) {
|
||||||
|
cData.insert("avatarState", static_cast<uint>(Shared::Avatar::valid));
|
||||||
|
} else {
|
||||||
|
cData.insert("avatarState", static_cast<uint>(Shared::Avatar::autocreated));
|
||||||
|
}
|
||||||
|
cData.insert("avatarPath", avatarPath(resource) + "." + info.type);
|
||||||
|
} else {
|
||||||
|
cData.insert("avatarState", static_cast<uint>(Shared::Avatar::empty));
|
||||||
|
cData.insert("avatarPath", "");
|
||||||
|
requestVCard(p_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
emit addParticipant(resource, cData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -167,6 +184,7 @@ void Core::Conference::onRoomParticipantChanged(const QString& p_name)
|
|||||||
lastInteraction = QDateTime::currentDateTime();
|
lastInteraction = QDateTime::currentDateTime();
|
||||||
}
|
}
|
||||||
QXmppMucItem mi = pres.mucItem();
|
QXmppMucItem mi = pres.mucItem();
|
||||||
|
handlePresence(pres);
|
||||||
|
|
||||||
emit changeParticipant(resource, {
|
emit changeParticipant(resource, {
|
||||||
{"lastActivity", lastInteraction},
|
{"lastActivity", lastInteraction},
|
||||||
@ -202,3 +220,92 @@ void Core::Conference::onRoomSubjectChanged(const QString& p_name)
|
|||||||
{
|
{
|
||||||
emit subjectChanged(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)
|
||||||
|
{
|
||||||
|
bool result = RosterItem::setAutoGeneratedAvatar(resource);
|
||||||
|
if (result && resource.size() != 0) {
|
||||||
|
emit changeParticipant(resource, {
|
||||||
|
{"avatarState", static_cast<uint>(Shared::Avatar::autocreated)},
|
||||||
|
{"availability", avatarPath(resource) + ".png"}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Core::Conference::setAvatar(const QByteArray& data, const QString& resource)
|
||||||
|
{
|
||||||
|
bool result = RosterItem::setAvatar(data, resource);
|
||||||
|
if (result && resource.size() != 0) {
|
||||||
|
if (data.size() > 0) {
|
||||||
|
QMimeDatabase db;
|
||||||
|
QMimeType type = db.mimeTypeForData(data);
|
||||||
|
QString ext = type.preferredSuffix();
|
||||||
|
emit changeParticipant(resource, {
|
||||||
|
{"avatarState", static_cast<uint>(Shared::Avatar::autocreated)},
|
||||||
|
{"avatarPath", avatarPath(resource) + "." + ext}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
emit changeParticipant(resource, {
|
||||||
|
{"avatarState", static_cast<uint>(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<uint>(result.getAvatarType())},
|
||||||
|
{"avatarPath", result.getAvatarPath()}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
@ -44,6 +44,10 @@ public:
|
|||||||
|
|
||||||
bool getAutoJoin();
|
bool getAutoJoin();
|
||||||
void setAutoJoin(bool p_autoJoin);
|
void setAutoJoin(bool p_autoJoin);
|
||||||
|
void handlePresence(const QXmppPresence & pres) override;
|
||||||
|
bool setAutoGeneratedAvatar(const QString& resource = "") override;
|
||||||
|
bool setAvatar(const QByteArray &data, const QString &resource = "") override;
|
||||||
|
Shared::VCard handleResponseVCard(const QXmppVCardIq & card, const QString &resource) override;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void nickChanged(const QString& nick);
|
void nickChanged(const QString& nick);
|
||||||
|
@ -68,3 +68,33 @@ void Core::Contact::setSubscriptionState(Shared::SubscriptionState state)
|
|||||||
emit subscriptionStateChanged(subscriptionState);
|
emit subscriptionStateChanged(subscriptionState);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Core::Contact::handlePresence(const QXmppPresence& pres)
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
if (!hasAvatar || !info.autogenerated) {
|
||||||
|
setAutoGeneratedAvatar();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case QXmppPresence::VCardUpdateValidPhoto:{ //there is a photo, need to load
|
||||||
|
Archive::AvatarInfo info;
|
||||||
|
bool hasAvatar = readAvatarInfo(info);
|
||||||
|
if (hasAvatar) {
|
||||||
|
if (info.autogenerated || info.hash != pres.photoHash()) {
|
||||||
|
emit requestVCard(jid);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
emit requestVCard(jid);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -38,6 +38,7 @@ public:
|
|||||||
|
|
||||||
void setSubscriptionState(Shared::SubscriptionState state);
|
void setSubscriptionState(Shared::SubscriptionState state);
|
||||||
Shared::SubscriptionState getSubscriptionState() const;
|
Shared::SubscriptionState getSubscriptionState() const;
|
||||||
|
void handlePresence(const QXmppPresence & pres) override;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void groupAdded(const QString& name);
|
void groupAdded(const QString& name);
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "rosteritem.h"
|
#include "rosteritem.h"
|
||||||
|
#include "account.h"
|
||||||
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
|
||||||
@ -334,40 +335,30 @@ bool Core::RosterItem::isMuc() const
|
|||||||
return muc;
|
return muc;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Core::RosterItem::avatarHash() const
|
QString Core::RosterItem::avatarPath(const QString& resource) const
|
||||||
{
|
|
||||||
return archive->getAvatarHash();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Core::RosterItem::isAvatarAutoGenerated() const
|
|
||||||
{
|
|
||||||
return archive->getAutoAvatar();
|
|
||||||
}
|
|
||||||
|
|
||||||
QString Core::RosterItem::avatarPath() const
|
|
||||||
{
|
{
|
||||||
QString path(QStandardPaths::writableLocation(QStandardPaths::CacheLocation));
|
QString path(QStandardPaths::writableLocation(QStandardPaths::CacheLocation));
|
||||||
path += "/" + account + "/" + jid + "/avatar." + archive->getAvatarType();
|
path += "/" + account + "/" + jid + "/" + (resource.size() == 0 ? jid : resource);
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Core::RosterItem::hasAvatar() const
|
bool Core::RosterItem::setAvatar(const QByteArray& data, const QString& resource)
|
||||||
{
|
{
|
||||||
return archive->getHasAvatar();
|
bool result = archive->setAvatar(data, false, resource);
|
||||||
}
|
if (resource.size() == 0 && result) {
|
||||||
|
if (data.size() == 0) {
|
||||||
void Core::RosterItem::setAvatar(const QByteArray& data)
|
|
||||||
{
|
|
||||||
if (archive->setAvatar(data, false)) {
|
|
||||||
if (archive->getHasAvatar()) {
|
|
||||||
emit avatarChanged(Shared::Avatar::empty, "");
|
emit avatarChanged(Shared::Avatar::empty, "");
|
||||||
} else {
|
} else {
|
||||||
emit avatarChanged(Shared::Avatar::valid, avatarPath());
|
QMimeDatabase db;
|
||||||
|
QMimeType type = db.mimeTypeForData(data);
|
||||||
|
QString ext = type.preferredSuffix();
|
||||||
|
emit avatarChanged(Shared::Avatar::valid, avatarPath(resource) + "." + ext);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::RosterItem::setAutoGeneratedAvatar()
|
bool Core::RosterItem::setAutoGeneratedAvatar(const QString& resource)
|
||||||
{
|
{
|
||||||
QImage image(96, 96, QImage::Format_ARGB32_Premultiplied);
|
QImage image(96, 96, QImage::Format_ARGB32_Premultiplied);
|
||||||
QPainter painter(&image);
|
QPainter painter(&image);
|
||||||
@ -383,13 +374,68 @@ void Core::RosterItem::setAutoGeneratedAvatar()
|
|||||||
} else {
|
} else {
|
||||||
painter.setPen(Qt::white);
|
painter.setPen(Qt::white);
|
||||||
}
|
}
|
||||||
painter.drawText(image.rect(), Qt::AlignCenter | Qt::AlignVCenter, jid.at(0).toUpper());
|
painter.drawText(image.rect(), Qt::AlignCenter | Qt::AlignVCenter, resource.size() == 0 ? jid.at(0).toUpper() : resource.at(0).toUpper());
|
||||||
QByteArray arr;
|
QByteArray arr;
|
||||||
QBuffer stream(&arr);
|
QBuffer stream(&arr);
|
||||||
stream.open(QBuffer::WriteOnly);
|
stream.open(QBuffer::WriteOnly);
|
||||||
image.save(&stream, "PNG");
|
image.save(&stream, "PNG");
|
||||||
stream.close();
|
stream.close();
|
||||||
if (archive->setAvatar(arr, true)) {
|
bool result = archive->setAvatar(arr, true, resource);
|
||||||
emit avatarChanged(Shared::Avatar::autocreated, avatarPath());
|
if (resource.size() == 0 && result) {
|
||||||
|
emit avatarChanged(Shared::Avatar::autocreated, avatarPath(resource) + ".png");
|
||||||
}
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Core::RosterItem::readAvatarInfo(Archive::AvatarInfo& target, const QString& resource) const
|
||||||
|
{
|
||||||
|
return archive->readAvatarInfo(target, resource);
|
||||||
|
}
|
||||||
|
|
||||||
|
Shared::VCard Core::RosterItem::handleResponseVCard(const QXmppVCardIq& card, const QString& resource)
|
||||||
|
{
|
||||||
|
Archive::AvatarInfo info;
|
||||||
|
bool hasAvatar = readAvatarInfo(info, resource);
|
||||||
|
|
||||||
|
QByteArray ava = card.photo();
|
||||||
|
Shared::VCard vCard;
|
||||||
|
initializeVCard(vCard, card);
|
||||||
|
Shared::Avatar type = Shared::Avatar::empty;
|
||||||
|
QString path = "";
|
||||||
|
|
||||||
|
if (ava.size() > 0) {
|
||||||
|
bool changed = setAvatar(ava, resource);
|
||||||
|
if (changed) {
|
||||||
|
type = Shared::Avatar::valid;
|
||||||
|
QMimeDatabase db;
|
||||||
|
QMimeType type = db.mimeTypeForData(ava);
|
||||||
|
QString ext = type.preferredSuffix();
|
||||||
|
path = avatarPath(resource) + "." + ext;
|
||||||
|
} else if (hasAvatar) {
|
||||||
|
if (info.autogenerated) {
|
||||||
|
type = Shared::Avatar::autocreated;
|
||||||
|
path = avatarPath(resource) + ".png";
|
||||||
|
} else {
|
||||||
|
type = Shared::Avatar::valid;
|
||||||
|
path = avatarPath(resource) + "." + info.type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!hasAvatar || !info.autogenerated) {
|
||||||
|
setAutoGeneratedAvatar(resource);
|
||||||
|
type = Shared::Avatar::autocreated;
|
||||||
|
path = avatarPath(resource) + ".png";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
vCard.setAvatarType(type);
|
||||||
|
vCard.setAvatarPath(path);
|
||||||
|
|
||||||
|
if (resource.size() == 0) {
|
||||||
|
emit avatarChanged(vCard.getAvatarType(), vCard.getAvatarPath());
|
||||||
|
}
|
||||||
|
|
||||||
|
return vCard;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -28,6 +28,9 @@
|
|||||||
|
|
||||||
#include <list>
|
#include <list>
|
||||||
|
|
||||||
|
#include <QXmppVCardIq.h>
|
||||||
|
#include <QXmppPresence.h>
|
||||||
|
|
||||||
#include "../global.h"
|
#include "../global.h"
|
||||||
#include "archive.h"
|
#include "archive.h"
|
||||||
|
|
||||||
@ -62,12 +65,12 @@ public:
|
|||||||
void flushMessagesToArchive(bool finished, const QString& firstId, const QString& lastId);
|
void flushMessagesToArchive(bool finished, const QString& firstId, const QString& lastId);
|
||||||
void requestHistory(int count, const QString& before);
|
void requestHistory(int count, const QString& before);
|
||||||
void requestFromEmpty(int count, const QString& before);
|
void requestFromEmpty(int count, const QString& before);
|
||||||
bool hasAvatar() const;
|
QString avatarPath(const QString& resource = "") const;
|
||||||
bool isAvatarAutoGenerated() const;
|
bool readAvatarInfo(Archive::AvatarInfo& target, const QString& resource = "") const;
|
||||||
QString avatarHash() const;
|
virtual bool setAvatar(const QByteArray& data, const QString& resource = "");
|
||||||
QString avatarPath() const;
|
virtual bool setAutoGeneratedAvatar(const QString& resource = "");
|
||||||
void setAvatar(const QByteArray& data);
|
virtual Shared::VCard handleResponseVCard(const QXmppVCardIq& card, const QString& resource);
|
||||||
void setAutoGeneratedAvatar();
|
virtual void handlePresence(const QXmppPresence& pres) = 0;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void nameChanged(const QString& name);
|
void nameChanged(const QString& name);
|
||||||
@ -75,6 +78,7 @@ signals:
|
|||||||
void historyResponse(const std::list<Shared::Message>& messages);
|
void historyResponse(const std::list<Shared::Message>& messages);
|
||||||
void needHistory(const QString& before, const QString& after, const QDateTime& afterTime = QDateTime());
|
void needHistory(const QString& before, const QString& after, const QDateTime& afterTime = QDateTime());
|
||||||
void avatarChanged(Shared::Avatar, const QString& path);
|
void avatarChanged(Shared::Avatar, const QString& path);
|
||||||
|
void requestVCard(const QString& jid);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
const QString jid;
|
const QString jid;
|
||||||
|
@ -34,6 +34,15 @@ Models::Participant::Participant(const QMap<QString, QVariant>& data, Models::It
|
|||||||
if (itr != data.end()) {
|
if (itr != data.end()) {
|
||||||
setRole(itr.value().toUInt());
|
setRole(itr.value().toUInt());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
itr = data.find("avatarState");
|
||||||
|
if (itr != data.end()) {
|
||||||
|
setAvatarState(itr.value().toUInt());
|
||||||
|
}
|
||||||
|
itr = data.find("avatarPath");
|
||||||
|
if (itr != data.end()) {
|
||||||
|
setAvatarPath(itr.value().toString());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Models::Participant::~Participant()
|
Models::Participant::~Participant()
|
||||||
@ -42,7 +51,7 @@ Models::Participant::~Participant()
|
|||||||
|
|
||||||
int Models::Participant::columnCount() const
|
int Models::Participant::columnCount() const
|
||||||
{
|
{
|
||||||
return 6;
|
return 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariant Models::Participant::data(int column) const
|
QVariant Models::Participant::data(int column) const
|
||||||
@ -52,6 +61,10 @@ QVariant Models::Participant::data(int column) const
|
|||||||
return static_cast<uint8_t>(affiliation);
|
return static_cast<uint8_t>(affiliation);
|
||||||
case 5:
|
case 5:
|
||||||
return static_cast<uint8_t>(role);
|
return static_cast<uint8_t>(role);
|
||||||
|
case 6:
|
||||||
|
return static_cast<quint8>(getAvatarState());
|
||||||
|
case 7:
|
||||||
|
return getAvatarPath();
|
||||||
default:
|
default:
|
||||||
return AbstractParticipant::data(column);
|
return AbstractParticipant::data(column);
|
||||||
}
|
}
|
||||||
@ -63,6 +76,10 @@ void Models::Participant::update(const QString& key, const QVariant& value)
|
|||||||
setAffiliation(value.toUInt());
|
setAffiliation(value.toUInt());
|
||||||
} else if (key == "role") {
|
} else if (key == "role") {
|
||||||
setRole(value.toUInt());
|
setRole(value.toUInt());
|
||||||
|
} else if (key == "avatarState") {
|
||||||
|
setAvatarState(value.toUInt());
|
||||||
|
} else if (key == "avatarPath") {
|
||||||
|
setAvatarPath(value.toString());
|
||||||
} else {
|
} else {
|
||||||
AbstractParticipant::update(key, value);
|
AbstractParticipant::update(key, value);
|
||||||
}
|
}
|
||||||
@ -113,3 +130,39 @@ void Models::Participant::setRole(unsigned int p_role)
|
|||||||
qDebug() << "An attempt to set wrong role" << p_role << "to the room participant" << name;
|
qDebug() << "An attempt to set wrong role" << p_role << "to the room participant" << name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString Models::Participant::getAvatarPath() const
|
||||||
|
{
|
||||||
|
return avatarPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
Shared::Avatar Models::Participant::getAvatarState() const
|
||||||
|
{
|
||||||
|
return avatarState;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Models::Participant::setAvatarPath(const QString& path)
|
||||||
|
{
|
||||||
|
if (avatarPath != path) {
|
||||||
|
avatarPath = path;
|
||||||
|
changed(7);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Models::Participant::setAvatarState(Shared::Avatar p_state)
|
||||||
|
{
|
||||||
|
if (avatarState != p_state) {
|
||||||
|
avatarState = p_state;
|
||||||
|
changed(6);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Models::Participant::setAvatarState(unsigned int p_state)
|
||||||
|
{
|
||||||
|
if (p_state <= static_cast<quint8>(Shared::Avatar::valid)) {
|
||||||
|
Shared::Avatar state = static_cast<Shared::Avatar>(p_state);
|
||||||
|
setAvatarState(state);
|
||||||
|
} else {
|
||||||
|
qDebug() << "An attempt to set invalid avatar state" << p_state << "to the room participant" << name << ", skipping";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -42,9 +42,19 @@ public:
|
|||||||
void setRole(Shared::Role p_role);
|
void setRole(Shared::Role p_role);
|
||||||
void setRole(unsigned int role);
|
void setRole(unsigned int role);
|
||||||
|
|
||||||
|
Shared::Avatar getAvatarState() const;
|
||||||
|
QString getAvatarPath() const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void setAvatarState(Shared::Avatar p_state);
|
||||||
|
void setAvatarState(unsigned int p_state);
|
||||||
|
void setAvatarPath(const QString& path);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Shared::Affiliation affiliation;
|
Shared::Affiliation affiliation;
|
||||||
Shared::Role role;
|
Shared::Role role;
|
||||||
|
Shared::Avatar avatarState;
|
||||||
|
QString avatarPath;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -66,6 +66,7 @@ QVariant Models::Roster::data (const QModelIndex& index, int role) const
|
|||||||
case Qt::DisplayRole:
|
case Qt::DisplayRole:
|
||||||
{
|
{
|
||||||
if (index.column() != 0) {
|
if (index.column() != 0) {
|
||||||
|
result = "";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
switch (item->type) {
|
switch (item->type) {
|
||||||
@ -139,11 +140,20 @@ QVariant Models::Roster::data (const QModelIndex& index, int role) const
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case Item::participant: {
|
case Item::participant: {
|
||||||
|
quint8 col = index.column();
|
||||||
|
Participant* p = static_cast<Participant*>(item);
|
||||||
|
if (col == 0) {
|
||||||
|
result = p->getStatusIcon(false);
|
||||||
|
} else if (col == 1) {
|
||||||
|
QString path = p->getAvatarPath();
|
||||||
|
if (path.size() > 0) {
|
||||||
|
result = QIcon(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
if (index.column() != 0) {
|
if (index.column() != 0) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
Participant* p = static_cast<Participant*>(item);
|
|
||||||
result = p->getStatusIcon(false);
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -213,7 +213,6 @@ void Squawk::addContact(const QString& account, const QString& jid, const QStrin
|
|||||||
settings.beginGroup(account);
|
settings.beginGroup(account);
|
||||||
if (settings.value("expanded", false).toBool()) {
|
if (settings.value("expanded", false).toBool()) {
|
||||||
QModelIndex ind = rosterModel.getAccountIndex(account);
|
QModelIndex ind = rosterModel.getAccountIndex(account);
|
||||||
qDebug() << "expanding account " << ind.data();
|
|
||||||
m_ui->roster->expand(ind);
|
m_ui->roster->expand(ind);
|
||||||
}
|
}
|
||||||
settings.endGroup();
|
settings.endGroup();
|
||||||
|
@ -23,7 +23,16 @@
|
|||||||
#include <QRegularExpression>
|
#include <QRegularExpression>
|
||||||
#include "message.h"
|
#include "message.h"
|
||||||
|
|
||||||
const QRegularExpression urlReg("(?<!<a\\shref=['\"])(?<!<img\\ssrc=['\"])((?:https?|ftp)://\\S+)"); //finds all hypertext references which are not wrapped in a or img tags
|
const QRegularExpression urlReg("(?<!<a\\shref=['\"])(?<!<img\\ssrc=['\"])("
|
||||||
|
"(?:https?|ftp):\\/\\/"
|
||||||
|
"\\w+"
|
||||||
|
"(?:"
|
||||||
|
"[\\w\\.\\/\\:\\;\\?\\&\\=\\@\\%\\#\\+]?"
|
||||||
|
"(?:"
|
||||||
|
"\\([\\w\\.\\/\\:\\;\\?\\&\\=\\@\\%\\#\\+]+\\)"
|
||||||
|
")?"
|
||||||
|
")*"
|
||||||
|
")");
|
||||||
const QRegularExpression imgReg("((?:https?|ftp)://\\S+\\.(?:jpg|jpeg|png|svg|gif))");
|
const QRegularExpression imgReg("((?:https?|ftp)://\\S+\\.(?:jpg|jpeg|png|svg|gif))");
|
||||||
|
|
||||||
Message::Message(const Shared::Message& source, bool outgoing, const QString& p_sender, const QString& avatarPath, QWidget* parent):
|
Message::Message(const Shared::Message& source, bool outgoing, const QString& p_sender, const QString& avatarPath, QWidget* parent):
|
||||||
|
Loading…
Reference in New Issue
Block a user