]> cgit.babelmonkeys.de Git - jubjub.git/blobdiff - src/core/JubAvatarManager.m
Add User Avatar support
[jubjub.git] / src / core / JubAvatarManager.m
diff --git a/src/core/JubAvatarManager.m b/src/core/JubAvatarManager.m
new file mode 100644 (file)
index 0000000..7d2fe8f
--- /dev/null
@@ -0,0 +1,180 @@
+#import "JubAvatarManager.h"
+#import "JubChatClient.h"
+
+#define JUB_NS_PUBSUB @"http://jabber.org/protocol/pubsub"
+#define JUB_NS_PUBSUB_EVENT @"http://jabber.org/protocol/pubsub#event"
+#define JUB_NS_AVATAR_DATA @"urn:xmpp:avatar:data"
+#define JUB_NS_AVATAR_METADATA @"urn:xmpp:avatar:metadata"
+
+@implementation JubAvatarManager
+@synthesize delegate = _delegate;
+
+- initWithClient: (JubChatClient*)client
+{
+       self = [super init];
+
+       @try {
+               _client = client;
+               [_client.discoEntity
+                   addFeature: JUB_NS_AVATAR_METADATA @"+notify"];
+               [_client.connection addDelegate: self];
+
+               // Determine cache path
+               OFDictionary *env = [OFApplication environment];
+               OFString * xdgCache = env[@"XDG_CACHE_HOME"];
+
+               cachePath = [OFMutableString new];
+               if (xdgCache == nil) {
+                       OFString *home = env[@"HOME"];
+                       if (home == nil)
+                               [cachePath appendString: @"/tmp"];
+                       else
+                               [cachePath appendString: home];
+                       [cachePath appendString: @"/.cache"];
+               } else
+                       [cachePath appendString: xdgCache];
+
+               [cachePath appendString: @"/jubjub"];
+       } @catch (id e) {
+               [self release];
+               @throw e;
+       }
+
+       return self;
+}
+
+- (void)dealloc
+{
+       // TODO: Remove feature
+       [cachePath release];
+       [_client.connection removeDelegate: self];
+       [super dealloc];
+}
+
+- (void)connection: (XMPPConnection*)connection
+      didReceiveMessage: (XMPPMessage*)message
+{
+       OFXMLElement *event = [message elementForName: @"event"
+                                           namespace: JUB_NS_PUBSUB_EVENT];
+       if (event == nil)
+               return;
+
+       OFXMLElement *items = [event elementForName: @"items"
+                                         namespace: JUB_NS_PUBSUB_EVENT];
+       if (items == nil ||
+           ![[[items attributeForName: @"node"] stringValue]
+             isEqual: JUB_NS_AVATAR_METADATA])
+               return;
+
+       OFXMLElement *item = [items elementForName: @"item"
+                                        namespace: JUB_NS_PUBSUB_EVENT];
+       if (item == nil)
+               return;
+
+       OFString *ID = [[item attributeForName: @"id"] stringValue];
+       if (ID == nil)
+               return;
+
+       OFXMLElement *metadata = [item elementForName: @"metadata"
+                                           namespace: JUB_NS_AVATAR_METADATA];
+       if (metadata == nil)
+               return;
+
+       OFXMLElement *info = [metadata elementForName: @"info"
+                                           namespace: JUB_NS_AVATAR_METADATA];
+       if (info == nil)
+               return;
+
+       OFString *avatarPath =
+           [cachePath stringByAppendingFormat: @"/%@.png", ID];
+       if ([OFFile fileExistsAtPath: avatarPath]) {
+               OFString *contactJID = [message.from bareJID];
+               XMPPContact *contact =
+                   [_client.contactManager.contacts objectForKey: contactJID];
+               if (contact == nil) // Avatar for unknown contact
+                       return;
+               [_delegate contact: contact
+                     didSetAvatar: avatarPath];
+               return;
+       }
+
+       XMPPIQ *queryIQ =
+           [XMPPIQ IQWithType: @"get"
+                           ID: [_client.connection generateStanzaID]];
+       queryIQ.to = message.from;
+
+       OFXMLElement *pubsub = [OFXMLElement elementWithName: @"pubsub"
+                                                  namespace: JUB_NS_PUBSUB];
+       [queryIQ addChild: pubsub];
+
+       OFXMLElement *queryItems =
+           [OFXMLElement elementWithName: @"items"
+                               namespace: JUB_NS_PUBSUB];
+       [queryItems addAttributeWithName: @"node"
+                            stringValue: JUB_NS_AVATAR_DATA];
+       [pubsub addChild: queryItems];
+
+       OFXMLElement *queryItem =
+           [OFXMLElement elementWithName: @"item"
+                               namespace: JUB_NS_PUBSUB];
+       [queryItem addAttributeWithName: @"id"
+                           stringValue: ID];
+       [queryItems addChild: queryItem];
+
+       [_client.connection sendIQ: queryIQ
+                   callbackTarget: self
+                         selector: @selector(Jub_connection:
+                                           receivedAvatarIQ:)];
+}
+
+- (void)Jub_connection: (XMPPConnection*)connection
+      receivedAvatarIQ: (XMPPIQ*)IQ
+{
+       OFXMLElement *pubsub = [IQ elementForName: @"pubsub"
+                                       namespace: JUB_NS_PUBSUB];
+       if (pubsub == nil)
+               return;
+
+       OFXMLElement *items = [pubsub elementForName: @"items"
+                                          namespace: JUB_NS_PUBSUB];
+       if (items == nil ||
+           ![[[items attributeForName: @"node"] stringValue]
+             isEqual: JUB_NS_AVATAR_DATA])
+               return;
+
+       OFXMLElement *item = [items elementForName: @"item"
+                                        namespace: JUB_NS_PUBSUB];
+       if (item == nil)
+               return;
+
+       OFString *ID = [[item attributeForName: @"id"] stringValue];
+       if (ID == nil)
+               return;
+
+       OFXMLElement *data = [item elementForName: @"data"
+                                       namespace: JUB_NS_AVATAR_DATA];
+       if (data == nil)
+               return;
+
+       OFDataArray *avatar =
+           [OFDataArray dataArrayWithBase64EncodedString: [data stringValue]];
+
+       if (![OFFile directoryExistsAtPath: cachePath])
+               [OFFile createDirectoryAtPath: cachePath
+                               createParents: true];
+
+       OFString *filename =
+           [cachePath stringByAppendingFormat: @"/%@.png", ID];
+       [avatar writeToFile: filename];
+
+       OFString *contactJID = [IQ.from bareJID];
+
+       XMPPContact *contact =
+           [_client.contactManager.contacts objectForKey: contactJID];
+       if (contact == nil) // Avatar for unknown contact
+               return;
+
+       [_delegate contact: contact
+             didSetAvatar: filename];
+}
+@end