PNXMPPFramework/Extensions/XEP-0153/XMPPvCardAvatarModule.m
2016-02-24 16:56:39 +01:00

306 lines
8.9 KiB
Objective-C
Executable File

//
// XMPPvCardAvatarModule.h
// XEP-0153 vCard-Based Avatars
//
// Created by Eric Chamberlain on 3/9/11.
// Copyright 2011 RF.com. All rights reserved.
#import "XMPPvCardAvatarModule.h"
#import "NSData+XMPP.h"
#import "NSXMLElement+XMPP.h"
#import "XMPPLogging.h"
#import "XMPPPresence.h"
#import "XMPPStream.h"
#import "XMPPvCardTempModule.h"
#import "XMPPvCardTemp.h"
#if ! __has_feature(objc_arc)
#warning This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC).
#endif
// Log levels: off, error, warn, info, verbose
// Log flags: trace
#if DEBUG
static const int xmppLogLevel = XMPP_LOG_LEVEL_WARN; // | XMPP_LOG_FLAG_TRACE;
#else
static const int xmppLogLevel = XMPP_LOG_LEVEL_WARN;
#endif
NSString *const kXMPPvCardAvatarElement = @"x";
NSString *const kXMPPvCardAvatarNS = @"vcard-temp:x:update";
NSString *const kXMPPvCardAvatarPhotoElement = @"photo";
@implementation XMPPvCardAvatarModule
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark Init/dealloc
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (id)init
{
// This will cause a crash - it's designed to.
// Only the init methods listed in XMPPvCardAvatarModule.h are supported.
return [self initWithvCardTempModule:nil dispatchQueue:NULL];
}
- (id)initWithDispatchQueue:(dispatch_queue_t)queue
{
// This will cause a crash - it's designed to.
// Only the init methods listed in XMPPvCardAvatarModule.h are supported.
return [self initWithvCardTempModule:nil dispatchQueue:NULL];
}
- (id)initWithvCardTempModule:(XMPPvCardTempModule *)xmppvCardTempModule
{
return [self initWithvCardTempModule:xmppvCardTempModule dispatchQueue:NULL];
}
- (id)initWithvCardTempModule:(XMPPvCardTempModule *)xmppvCardTempModule dispatchQueue:(dispatch_queue_t)queue
{
NSParameterAssert(xmppvCardTempModule != nil);
if ((self = [super initWithDispatchQueue:queue])) {
_xmppvCardTempModule = xmppvCardTempModule;
// we don't need to call the storage configureWithParent:queue: method,
// because the vCardTempModule already did that.
_moduleStorage = (id <XMPPvCardAvatarStorage>)xmppvCardTempModule.xmppvCardTempModuleStorage;
[_xmppvCardTempModule addDelegate:self delegateQueue:moduleQueue];
_autoClearMyvcard = YES;
}
return self;
}
- (void)dealloc {
[_xmppvCardTempModule removeDelegate:self];
_moduleStorage = nil;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark Properties
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (BOOL)autoClearMyvcard
{
__block BOOL result = NO;
dispatch_block_t block = ^{
result = _autoClearMyvcard;
};
if (dispatch_get_specific(moduleQueueTag))
block();
else
dispatch_sync(moduleQueue, block);
return result;
}
- (void)setAutoClearMyvcard:(BOOL)flag
{
dispatch_block_t block = ^{
_autoClearMyvcard = flag;
};
if (dispatch_get_specific(moduleQueueTag))
block();
else
dispatch_async(moduleQueue, block);
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark Public
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (NSData *)photoDataForJID:(XMPPJID *)jid
{
// This is a public method, so it may be invoked on any thread/queue.
//
// The vCardTempModule is thread safe.
// The moduleStorage should be thread safe. (User may be using custom module storage class).
// The multicastDelegate is NOT thread safe.
__block NSData *photoData;
dispatch_block_t block = ^{ @autoreleasepool {
photoData = [_moduleStorage photoDataForJID:jid xmppStream:xmppStream];
if (photoData == nil)
{
[_xmppvCardTempModule vCardTempForJID:jid shouldFetch:YES];
}
}};
if (dispatch_get_specific(moduleQueueTag))
block();
else
dispatch_sync(moduleQueue, block);
return photoData;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark XMPPStreamDelegate
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (void)xmppStreamDidConnect:(XMPPStream *)sender {
XMPPLogTrace();
if(self.autoClearMyvcard)
{
/*
* XEP-0153 Section 4.2 rule 1
*
* A client MUST NOT advertise an avatar image without first downloading the current vCard.
* Once it has done this, it MAY advertise an image.
*/
[_moduleStorage clearvCardTempForJID:[sender myJID] xmppStream:sender];
}
}
- (void)xmppStreamDidAuthenticate:(XMPPStream *)sender {
XMPPLogTrace();
[_xmppvCardTempModule fetchvCardTempForJID:[sender myJID] ignoreStorage:YES];
}
- (XMPPPresence *)xmppStream:(XMPPStream *)sender willSendPresence:(XMPPPresence *)presence {
XMPPLogTrace();
NSXMLElement *currentXElement = [presence elementForName:kXMPPvCardAvatarElement xmlns:kXMPPvCardAvatarNS];
//If there is already a x element then remove it
if(currentXElement)
{
NSUInteger currentXElementIndex = [[presence children] indexOfObject:currentXElement];
if(currentXElementIndex != NSNotFound)
{
[presence removeChildAtIndex:currentXElementIndex];
}
}
// add our photo info to the presence stanza
NSXMLElement *photoElement = nil;
NSXMLElement *xElement = [NSXMLElement elementWithName:kXMPPvCardAvatarElement xmlns:kXMPPvCardAvatarNS];
NSString *photoHash = [_moduleStorage photoHashForJID:[sender myJID] xmppStream:sender];
if (photoHash != nil)
{
photoElement = [NSXMLElement elementWithName:kXMPPvCardAvatarPhotoElement stringValue:photoHash];
} else {
photoElement = [NSXMLElement elementWithName:kXMPPvCardAvatarPhotoElement];
}
[xElement addChild:photoElement];
[presence addChild:xElement];
// Question: If photoElement is nil, should we be adding xElement?
return presence;
}
- (void)xmppStream:(XMPPStream *)sender didReceivePresence:(XMPPPresence *)presence {
XMPPLogTrace();
NSXMLElement *xElement = [presence elementForName:kXMPPvCardAvatarElement xmlns:kXMPPvCardAvatarNS];
if (xElement == nil) {
return;
}
NSXMLElement *photoElement = [xElement elementForName:kXMPPvCardAvatarPhotoElement];
if (photoElement == nil) {
return;
}
NSString *photoHash = [photoElement stringValue];
XMPPJID *jid = [presence from];
NSString *savedPhotoHash = [_moduleStorage photoHashForJID:jid xmppStream:xmppStream];
// check the hash
if ([photoHash caseInsensitiveCompare:savedPhotoHash] != NSOrderedSame
&& !([photoHash length] == 0 && [savedPhotoHash length] == 0)) {
[_xmppvCardTempModule fetchvCardTempForJID:jid ignoreStorage:YES];
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark XMPPvCardTempModuleDelegate
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (void)xmppvCardTempModule:(XMPPvCardTempModule *)vCardTempModule
didReceivevCardTemp:(XMPPvCardTemp *)vCardTemp
forJID:(XMPPJID *)jid
{
XMPPLogTrace();
if (vCardTemp.photo != nil)
{
#if TARGET_OS_IPHONE
UIImage *photo = [UIImage imageWithData:vCardTemp.photo];
#else
NSImage *photo = [[NSImage alloc] initWithData:vCardTemp.photo];
#endif
if (photo != nil)
{
[multicastDelegate xmppvCardAvatarModule:self
didReceivePhoto:photo
forJID:jid];
}
}
/*
* XEP-0153 4.1.3
* If the client subsequently obtains an avatar image (e.g., by updating or retrieving the vCard),
* it SHOULD then publish a new <presence/> stanza with character data in the <photo/> element.
*/
if ([[xmppStream myJID] isEqualToJID:jid options:XMPPJIDCompareBare])
{
XMPPPresence *presence = xmppStream.myPresence;
if(presence)
{
[xmppStream sendElement:presence];
}
}
}
- (void)xmppvCardTempModuleDidUpdateMyvCard:(XMPPvCardTempModule *)vCardTempModule{
//The vCard has been updated on the server so we need to cache it
[_xmppvCardTempModule fetchvCardTempForJID:[xmppStream myJID] ignoreStorage:NO];
}
- (void)xmppvCardTempModule:(XMPPvCardTempModule *)vCardTempModule failedToUpdateMyvCard:(NSXMLElement *)error{
//The vCard failed to update so we fetch the current one from the server
[_xmppvCardTempModule fetchvCardTempForJID:[xmppStream myJID] ignoreStorage:YES];
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark Getter/setter
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@synthesize xmppvCardTempModule = _xmppvCardTempModule;
@end