muc participant avatars

This commit is contained in:
Blue 2019-12-30 23:22:04 +03:00
parent efc90e18c3
commit 55703c2007
15 changed files with 506 additions and 221 deletions

View file

@ -33,10 +33,7 @@ Core::Archive::Archive(const QString& p_jid, QObject* parent):
main(),
order(),
stats(),
hasAvatar(false),
avatarAutoGenerated(false),
avatarHash(),
avatarType()
avatars()
{
}
@ -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_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, "order", MDB_CREATE | MDB_INTEGERKEY, &order);
mdb_dbi_open(txn, "stats", MDB_CREATE, &stats);
mdb_dbi_open(txn, "avatars", MDB_CREATE, &avatars);
mdb_txn_commit(txn);
mdb_txn_begin(environment, NULL, 0, &txn);
mdb_txn_begin(environment, NULL, MDB_RDONLY, &txn);
try {
fromTheBeginning = getStatBoolValue("beginning", txn);
} catch (const NotFound& e) {
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();
if (avatarAutoGenerated) {
avatarHash = "";
} else {
avatarHash = getStatStringValue("avatarHash", txn).c_str();
}
} else {
avatarAutoGenerated = false;
avatarHash = "";
avatarType = "";
}
std::string sJid = jid.toStdString();
AvatarInfo info;
bool hasAvatar = readAvatarInfo(info, sJid, txn);
mdb_txn_abort(txn);
if (hasAvatar) {
QFile ava(path + "/avatar." + avatarType);
QFile ava(path + "/" + sJid.c_str() + "." + info.type);
if (!ava.exists()) {
bool success = dropAvatar();
bool success = dropAvatar(sJid);
if (!success) {
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";
@ -120,6 +99,7 @@ void Core::Archive::open(const QString& account)
void Core::Archive::close()
{
if (opened) {
mdb_dbi_close(environment, avatars);
mdb_dbi_close(environment, stats);
mdb_dbi_close(environment, order);
mdb_dbi_close(environment, main);
@ -518,8 +498,9 @@ bool Core::Archive::getStatBoolValue(const std::string& id, MDB_txn* txn)
if (rc == MDB_NOTFOUND) {
throw NotFound(id, jid.toStdString());
} else if (rc) {
qDebug() << "error retrieving" << id.c_str() << "from stats db of" << jid << mdb_strerror(rc);
throw 15; //TODO proper exception
std::string err(mdb_strerror(rc));
qDebug() << "error retrieving" << id.c_str() << "from stats db of" << jid << err.c_str();
throw Unknown(jid.toStdString(), err);
} else {
uint8_t value = *(uint8_t*)(lmdbData.mv_data);
bool is;
@ -546,8 +527,9 @@ std::string Core::Archive::getStatStringValue(const std::string& id, MDB_txn* tx
if (rc == MDB_NOTFOUND) {
throw NotFound(id, jid.toStdString());
} else if (rc) {
qDebug() << "error retrieving" << id.c_str() << "from stats db of" << jid << mdb_strerror(rc);
throw 15; //TODO proper exception
std::string err(mdb_strerror(rc));
qDebug() << "error retrieving" << id.c_str() << "from stats db of" << jid << err.c_str();
throw Unknown(jid.toStdString(), err);
} else {
std::string value((char*)lmdbData.mv_data, lmdbData.mv_size);
return value;
@ -588,74 +570,38 @@ bool Core::Archive::setStatValue(const std::string& id, const std::string& value
return true;
}
bool Core::Archive::getHasAvatar() const
{
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()
bool Core::Archive::dropAvatar(const std::string& resource)
{
MDB_txn *txn;
MDB_val lmdbKey;
mdb_txn_begin(environment, NULL, 0, &txn);
bool success = setStatValue("hasAvatar", false, txn);
success = success && setStatValue("avatarAutoGenerated", false, txn);
success = success && setStatValue("avatarHash", "", txn);
success = success && setStatValue("avatarType", "", txn);
if (!success) {
lmdbKey.mv_size = resource.size();
lmdbKey.mv_data = (char*)resource.c_str();
int rc = mdb_del(txn, avatars, &lmdbKey, NULL);
if (rc != 0) {
mdb_txn_abort(txn);
return false;
} else {
hasAvatar = false;
avatarAutoGenerated = false;
avatarHash = "";
avatarType = "";
mdb_txn_commit(txn);
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) {
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 (!hasAvatar) {
return false;
} else {
return dropAvatar();
return dropAvatar(res);
}
} else {
const char* cep;
@ -664,14 +610,14 @@ bool Core::Archive::setAvatar(const QByteArray& data, bool generated)
bool needToRemoveOld = false;
QCryptographicHash hash(QCryptographicHash::Sha1);
hash.addData(data);
QString newHash(hash.result());
QByteArray newHash(hash.result());
if (hasAvatar) {
if (!generated && !avatarAutoGenerated && avatarHash == newHash) {
if (!generated && !oldInfo.autogenerated && oldInfo.hash == newHash) {
return false;
}
QFile oldAvatar(currentPath + "/avatar." + avatarType);
QFile oldAvatar(currentPath + "/" + res.c_str() + "." + oldInfo.type);
if (oldAvatar.exists()) {
if (oldAvatar.rename(currentPath + "/avatar." + avatarType + ".bak")) {
if (oldAvatar.rename(currentPath + "/" + res.c_str() + "." + oldInfo.type + ".bak")) {
needToRemoveOld = true;
} else {
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;
QMimeType type = db.mimeTypeForData(data);
QString ext = type.preferredSuffix();
QFile newAvatar(currentPath + "/avatar." + ext);
QFile newAvatar(currentPath + "/" + res.c_str() + "." + ext);
if (newAvatar.open(QFile::WriteOnly)) {
newAvatar.write(data);
newAvatar.close();
MDB_txn *txn;
mdb_txn_begin(environment, NULL, 0, &txn);
bool success = setStatValue("hasAvatar", true, txn);
success = success && setStatValue("avatarAutoGenerated", generated, txn);
success = success && setStatValue("avatarHash", newHash.toStdString(), txn);
success = success && setStatValue("avatarType", ext.toStdString(), txn);
if (!success) {
MDB_val lmdbKey, lmdbData;
QByteArray value;
AvatarInfo newInfo(ext, newHash, generated);
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";
if (needToRemoveOld) {
QFile oldAvatar(currentPath + "/avatar." + avatarType + ".bak");
oldAvatar.rename(currentPath + "/avatar." + avatarType);
QFile oldAvatar(currentPath + "/" + res.c_str() + "." + oldInfo.type + ".bak");
oldAvatar.rename(currentPath + "/" + res.c_str() + "." + oldInfo.type);
}
mdb_txn_abort(txn);
return false;
} else {
hasAvatar = true;
avatarAutoGenerated = generated;
avatarHash = newHash;
avatarType = ext;
mdb_txn_commit(txn);
if (needToRemoveOld) {
QFile oldAvatar(currentPath + "/avatar." + avatarType + ".bak");
QFile oldAvatar(currentPath + "/" + res.c_str() + "." + oldInfo.type + ".bak");
oldAvatar.remove();
}
return true;
@ -716,10 +665,91 @@ bool Core::Archive::setAvatar(const QByteArray& data, bool generated)
} else {
qDebug() << "Can't change avatar: cant open file to write" << newAvatar.fileName() << "rolling back to the previous state";
if (needToRemoveOld) {
QFile oldAvatar(currentPath + "/avatar." + avatarType + ".bak");
oldAvatar.rename(currentPath + "/avatar." + avatarType);
QFile oldAvatar(currentPath + "/" + res.c_str() + "." + oldInfo.type + ".bak");
oldAvatar.rename(currentPath + "/" + res.c_str() + "." + oldInfo.type);
}
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;
}