diff -Naur psi-0.10-test1-orig/psics.pri psi-0.10-test1/psics.pri --- psi-0.10-test1-orig/psics.pri 2005-08-21 17:44:35.000000000 +0000 +++ psi-0.10-test1/psics.pri 2005-08-27 19:16:20.000000000 +0000 @@ -6,6 +6,7 @@ HEADERS += \ $$IRISCS_CPP/util/base64.h \ + $$IRISCS_CPP/util/sha1.h \ $$IRISCS_CPP/util/bytestream.h \ $$IRISCS_CPP/util/safedelete.h \ $$IRISCS_CPP/network/ndns.h \ @@ -18,6 +19,7 @@ SOURCES += \ $$IRISCS_CPP/util/base64.cpp \ + $$IRISCS_CPP/util/sha1.cpp \ $$IRISCS_CPP/util/bytestream.cpp \ $$IRISCS_CPP/util/safedelete.cpp \ $$IRISCS_CPP/network/ndns.cpp \ diff -Naur psi-0.10-test1-orig/src/avatars.cpp psi-0.10-test1/src/avatars.cpp --- psi-0.10-test1-orig/src/avatars.cpp 2005-08-27 19:16:48.000000000 +0000 +++ psi-0.10-test1/src/avatars.cpp 2005-08-27 19:16:54.000000000 +0000 @@ -12,9 +12,228 @@ #include "psiaccount.h" #include "profiles.h" #include "vcardfactory.h" +#include "sha1.h" +#include "base64.h" +#include "xmpp_xmlcommon.h" +#include "xmpp_tasks.h" #define MAX_AVATAR_SIZE 64 + +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +// CachedAvatar: Base class for avatars which are requested and are to be cached +//------------------------------------------------------------------------------ + +class CachedAvatar : public Avatar +{ +public: + CachedAvatar() { }; + virtual void updateHash(const QString& h); + +protected: + virtual const QString& hash() const { return hash_; } + virtual void requestAvatar() { } + virtual void avatarUpdated() { } + + virtual bool isCached(const QString& hash); + virtual void loadFromCache(const QString& hash); + virtual void saveToCache(const QString& hash, const QByteArray& data); + +private: + QString hash_; +}; + + +void CachedAvatar::updateHash(const QString& h) +{ + if (hash_ != h) { + hash_ = h; + if (h.isEmpty()) { + hash_ = ""; + resetImage(); + avatarUpdated(); + } + else if (isCached(h)) { + loadFromCache(h); + avatarUpdated(); + } + else { + resetImage(); + avatarUpdated(); + requestAvatar(); + } + } +} + +bool CachedAvatar::isCached(const QString& h) +{ + return QDir(AvatarFactory::getCacheDir()).exists(h); +} + +void CachedAvatar::loadFromCache(const QString& h) +{ + // printf("Loading avatar from cache\n"); + hash_ = h; + setImage(QImage(QDir(AvatarFactory::getCacheDir()).filePath(h))); + if (pixmap().isNull()) { + printf("WARNING: Null pixmap. Unsupported format ?\n"); + } +} + +void CachedAvatar::saveToCache(const QString& hash, const QByteArray& data) +{ + // Check if the cache dir exists + if (!QDir(AvatarFactory::getAvatarsDir()).exists()) + QDir("").mkdir(AvatarFactory::getAvatarsDir()); + if (!QDir(AvatarFactory::getCacheDir()).exists()) + QDir("").mkdir(AvatarFactory::getCacheDir()); + + // Write file to cache + // printf("Saving %s to cache.\n",hash.latin1()); + QString fn = QDir(AvatarFactory::getCacheDir()).filePath(hash); + QFile f(fn); + if (f.open(IO_WriteOnly)) { + f.writeBlock(data); + f.close(); + } + else + printf("Error opening %s for writing.\n",f.name().latin1()); + +} + + +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +// JT_JEP8Avatar: Task for retrieving JEP-8 avatars +//------------------------------------------------------------------------------ + +class JT_JEP8Avatar : public Task +{ + Q_OBJECT + +public: + JT_JEP8Avatar(Task*, const Jid& jid); + + void onGo(); + const QString& data() + { return data_; } + const Jid& jid() + { return jid_; } + bool take(const QDomElement&); + +private: + Jid jid_; + QDomElement iq_; + QString data_; +}; + + +JT_JEP8Avatar::JT_JEP8Avatar(Task* parent, const Jid& jid) : Task(parent), jid_(jid) +{ + iq_ = createIQ(doc(), "get", jid_.full(), id()); + QDomElement query = doc()->createElement("query"); + query.setAttribute("xmlns","jabber:iq:avatar"); + iq_.appendChild(query); +} + + +void JT_JEP8Avatar::onGo() +{ + send(iq_); +} + + +bool JT_JEP8Avatar::take(const QDomElement &x) +{ + if (!iqVerify(x,jid_,id())) + return false; + + if (x.attribute("type") == "result") { + bool found; + QDomElement q = queryTag(x); + QDomElement tag = findSubTag(q, "data", &found); + if (found) { + data_ = tagContent(tag); + } + setSuccess(); + } + else { + setError(x); + } + + return true; +} + + +//------------------------------------------------------------------------------ +// JEP8Avatar: JEP-8 Avatars +//------------------------------------------------------------------------------ + +class JEP8Avatar : public QObject, public CachedAvatar +{ + Q_OBJECT + +public: + JEP8Avatar(AvatarFactory* factory, const Jid& jid); + +public slots: + void receiveAvatar(); + +signals: + void avatarChanged(const Jid&); + +protected: + void requestAvatar(); + void avatarUpdated() + { emit avatarChanged(jid_); } + +private: + Jid jid_; + AvatarFactory* factory_; +}; + + + +JEP8Avatar::JEP8Avatar(AvatarFactory* factory, const Jid& jid) : + jid_(jid), factory_(factory) +{ +} + + +void JEP8Avatar::requestAvatar() +{ + //blabla requestAvatar(factory_->account()->client()->rootTask()); + printf("Requesting avatar for %s\n",jid_.full().latin1()); + JT_JEP8Avatar *jav = new JT_JEP8Avatar(factory_->account()->client()->rootTask(), jid_); + connect(jav, SIGNAL(finished()), SLOT(receiveAvatar())); + jav->go(true); +} + +void JEP8Avatar::receiveAvatar() +{ + JT_JEP8Avatar *j = (JT_JEP8Avatar*) sender(); + if (j->success()) { + printf("Avatar data arrived from %s\n",j->jid().full().latin1()); + + // Decode data + QByteArray ba = Base64::stringToArray(j->data());; + if (!ba.isEmpty()) { + saveToCache(hash(),ba); + setImage(ba); + if (pixmap().isNull()) { + printf("WARNING: Null pixmap. Unsupported format ?\n"); + } + emit avatarChanged(j->jid()); + } + else + printf("WARNING: Received data is empty. Bad encoding ?\n"); + } +} + + //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ @@ -69,7 +288,7 @@ // VCardAvatar: Avatars coming from VCards of contacts. //------------------------------------------------------------------------------ -class VCardAvatar : public QObject, public Avatar +class VCardAvatar : public QObject, public CachedAvatar { Q_OBJECT @@ -80,8 +299,17 @@ signals: void avatarChanged(const Jid&); +public slots: + void receivedVCard(); + +protected: + void requestAvatar(); + void avatarUpdated() + { emit avatarChanged(jid_); } + private: Jid jid_; + QString hash_; AvatarFactory* factory_; const VCard* lastVCard_; // Never dereference this, might point to dead object }; @@ -117,6 +345,19 @@ return pixmap(); } +void VCardAvatar::requestAvatar() +{ + //blabla requestAvatar(factory_->account()->client()->rootTask()); + printf("Requesting VCard avatar for %s\n",jid_.full().latin1()); + VCardFactory::getVCard(jid_,factory_->account()->client()->rootTask(), this, SLOT(receivedVCard())); +} + +void VCardAvatar::receivedVCard() +{ + printf("VCard avatar arrived\n"); + // FIXME: Should probably save this data, but we're too lazy right now + emit avatarChanged(jid_); +} //------------------------------------------------------------------------------ @@ -325,6 +566,17 @@ if (!file_avatars_[jid.bare()]->isEmpty()) return file_avatars_[jid.bare()]; + // Try finding a JEP-8 avatar. + if (!jep8_avatars_.contains(jid.full())) { + // printf("JEP 8 avatar not yet loaded\n"); + jep8_avatars_[jid.full()] = new JEP8Avatar(this, jid); + connect(jep8_avatars_[jid.full()],SIGNAL(avatarChanged(const Jid&)), + this, SLOT(updateAvatar(const Jid&))); + } + // printf("Trying JEP8 avatar\n"); + if (!jep8_avatars_[jid.full()]->isEmpty()) + return jep8_avatars_[jid.full()]; + // Try finding an avatar in the VCard //printf("VCard avatar\n"); if (!vcard_avatars_.contains(jid.bare())) { @@ -400,6 +652,32 @@ return getAvatarsDir() + "/manual"; } +QString AvatarFactory::getCacheDir() +{ + return getAvatarsDir() + "/cache"; +} + +void AvatarFactory::setHash(const Jid& jid, const QString& hash) +{ + if (!jep8_avatars_.contains(jid.full())) { + jep8_avatars_[jid.full()] = new JEP8Avatar(this, jid); + connect(jep8_avatars_[jid.full()],SIGNAL(avatarChanged(const Jid&)), + this, SLOT(updateAvatar(const Jid&))); + } + + jep8_avatars_[jid.full()]->updateHash(hash); +} + +void AvatarFactory::setPhotoHash(const Jid& jid, const QString& hash) +{ + if (!vcard_avatars_.contains(jid.bare())) { + vcard_avatars_[jid.bare()] = new VCardAvatar(this, jid); + connect(vcard_avatars_[jid.bare()],SIGNAL(avatarChanged(const Jid&)),this,SLOT(updateAvatar(const Jid&))); + } + + vcard_avatars_[jid.bare()]->updateHash(hash); +} + //------------------------------------------------------------------------------ #include "avatars.moc" diff -Naur psi-0.10-test1-orig/src/avatars.h psi-0.10-test1/src/avatars.h --- psi-0.10-test1-orig/src/avatars.h 2005-08-27 19:16:48.000000000 +0000 +++ psi-0.10-test1/src/avatars.h 2005-08-27 19:16:54.000000000 +0000 @@ -15,6 +15,7 @@ class VCardAvatar; class FileAvatar; class ClientAvatar; +class JEP8Avatar; //------------------------------------------------------------------------------ @@ -36,6 +37,10 @@ static QString getAvatarsDir(); static QString getManualDir(); + static QString getCacheDir(); + + void setHash(const Jid& jid, const QString& hash); + void setPhotoHash(const Jid& jid, const QString& hash); signals: void avatarChanged(const Jid&); @@ -51,6 +56,7 @@ QMap file_avatars_; QMap client_avatars_; QMap vcard_avatars_; + QMap jep8_avatars_; PsiAccount* pa_; }; diff -Naur psi-0.10-test1-orig/src/psiaccount.cpp psi-0.10-test1/src/psiaccount.cpp --- psi-0.10-test1-orig/src/psiaccount.cpp 2005-08-27 19:16:48.000000000 +0000 +++ psi-0.10-test1/src/psiaccount.cpp 2005-08-27 19:16:54.000000000 +0000 @@ -451,6 +451,7 @@ connect(d->client, SIGNAL(rosterItemUpdated(const RosterItem &)), SLOT(client_rosterItemUpdated(const RosterItem &))); connect(d->client, SIGNAL(rosterItemRemoved(const RosterItem &)), SLOT(client_rosterItemRemoved(const RosterItem &))); connect(d->client, SIGNAL(resourceAvailable(const Jid &, const Resource &)), SLOT(client_resourceAvailable(const Jid &, const Resource &))); + connect(d->client, SIGNAL(resourceAvailable(const Jid &, const Resource &)), SLOT(updateAvatarHash(const Jid &, const Resource &))); connect(d->client, SIGNAL(resourceUnavailable(const Jid &, const Resource &)), SLOT(client_resourceUnavailable(const Jid &, const Resource &))); connect(d->client, SIGNAL(presenceError(const Jid &, int, const QString &)), SLOT(client_presenceError(const Jid &, int, const QString &))); connect(d->client, SIGNAL(messageReceived(const Message &)), SLOT(client_messageReceived(const Message &))); @@ -3872,6 +3873,16 @@ actionSendFile(j); } +void PsiAccount::updateAvatarHash(const Jid& j, const Resource& r) +{ + if (r.status().hasAvatarHash()) { + d->avatarFactory->setHash(j,r.status().avatarHash()); + } + if (r.status().hasPhotoHash()) { + d->avatarFactory->setPhotoHash(j,r.status().photoHash()); + } +} + void PsiAccount::toggleSecurity(const Jid &j, bool b) { UserListItem *u = findFirstRelavent(j); diff -Naur psi-0.10-test1-orig/src/psiaccount.h psi-0.10-test1/src/psiaccount.h --- psi-0.10-test1-orig/src/psiaccount.h 2005-08-27 19:16:48.000000000 +0000 +++ psi-0.10-test1/src/psiaccount.h 2005-08-27 19:16:54.000000000 +0000 @@ -277,6 +277,8 @@ void invokeGCInfo(const Jid &); void invokeGCFile(const Jid &); + void updateAvatarHash(const Jid& j, const Resource& r); + private slots: void tls_handshaken(); void cs_connected();