forked from blue/squawk
muc participant avatars
This commit is contained in:
parent
efc90e18c3
commit
55703c2007
15 changed files with 506 additions and 221 deletions
234
core/archive.cpp
234
core/archive.cpp
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue