- Update
This commit is contained in:
1 parent
d281d765ea
commit
5b074a5176
1261 files changed
+199158
-7303
No files matched your search
@@ -0,0 +1,512 @@
|
||||
#import "XMPP.h"
|
||||
#import "XMPPRosterMemoryStoragePrivate.h"
|
||||
|
||||
#if ! __has_feature(objc_arc)
|
||||
#warning This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC).
|
||||
#endif
|
||||
|
||||
@interface XMPPUserMemoryStorageObject (PrivateAPI)
|
||||
- (void)recalculatePrimaryResource;
|
||||
@end
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark -
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@implementation XMPPUserMemoryStorageObject
|
||||
|
||||
- (void)commonInit
|
||||
{
|
||||
// This method is here to more easily support subclassing.
|
||||
// That way subclasses can optionally override just commonInit, instead of each individual init method.
|
||||
//
|
||||
// If you override this method, don't forget to invoke [super commonInit];
|
||||
|
||||
resources = [[NSMutableDictionary alloc] initWithCapacity:1];
|
||||
}
|
||||
|
||||
- (id)initWithJID:(XMPPJID *)aJid
|
||||
{
|
||||
if ((self = [super init]))
|
||||
{
|
||||
jid = [aJid bareJID];
|
||||
|
||||
itemAttributes = [[NSMutableDictionary alloc] initWithCapacity:0];
|
||||
|
||||
groups = [[NSMutableArray alloc] initWithCapacity:0];
|
||||
|
||||
[self commonInit];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)initWithItem:(NSXMLElement *)item
|
||||
{
|
||||
if ((self = [super init]))
|
||||
{
|
||||
NSString *jidStr = [item attributeStringValueForName:@"jid"];
|
||||
jid = [[XMPPJID jidWithString:jidStr] bareJID];
|
||||
|
||||
itemAttributes = [item attributesAsDictionary];
|
||||
|
||||
groups = [[NSMutableArray alloc] initWithCapacity:0];
|
||||
|
||||
NSArray *groupElements = [item elementsForName:@"group"];
|
||||
|
||||
for (NSXMLElement *groupElement in groupElements) {
|
||||
NSString *groupName = [groupElement stringValue];
|
||||
|
||||
if ([groupName length])
|
||||
{
|
||||
[groups addObject:groupName];
|
||||
}
|
||||
}
|
||||
|
||||
[self commonInit];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark Copying
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
- (id)copyWithZone:(NSZone *)zone
|
||||
{
|
||||
// We use [self class] to support subclassing
|
||||
|
||||
XMPPUserMemoryStorageObject *deepCopy = (XMPPUserMemoryStorageObject *)[[[self class] alloc] init];
|
||||
|
||||
deepCopy->jid = [jid copy];
|
||||
deepCopy->itemAttributes = [itemAttributes mutableCopy];
|
||||
deepCopy->groups = [groups mutableCopy];
|
||||
|
||||
deepCopy->resources = [[NSMutableDictionary alloc] initWithCapacity:[resources count]];
|
||||
|
||||
for (XMPPJID *key in resources)
|
||||
{
|
||||
XMPPResourceMemoryStorageObject *resourceCopy = [resources[key] copy];
|
||||
|
||||
deepCopy->resources[key] = resourceCopy;
|
||||
}
|
||||
|
||||
[deepCopy recalculatePrimaryResource];
|
||||
|
||||
return deepCopy;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark Encoding, Decoding
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#if ! TARGET_OS_IPHONE
|
||||
- (id)replacementObjectForPortCoder:(NSPortCoder *)encoder
|
||||
{
|
||||
if ([encoder isBycopy])
|
||||
return self;
|
||||
else
|
||||
return [super replacementObjectForPortCoder:encoder];
|
||||
// return [NSDistantObject proxyWithLocal:self connection:[encoder connection]];
|
||||
}
|
||||
#endif
|
||||
|
||||
- (id)initWithCoder:(NSCoder *)coder
|
||||
{
|
||||
if ((self = [super init]))
|
||||
{
|
||||
if ([coder allowsKeyedCoding])
|
||||
{
|
||||
if([coder respondsToSelector:@selector(requiresSecureCoding)] &&
|
||||
[coder requiresSecureCoding])
|
||||
{
|
||||
jid = [coder decodeObjectOfClass:[XMPPJID class] forKey:@"jid"];
|
||||
itemAttributes = [[coder decodeObjectOfClass:[NSDictionary class] forKey:@"itemAttributes"] mutableCopy];
|
||||
groups = [[coder decodeObjectOfClass:[NSArray class] forKey:@"groups"] mutableCopy];
|
||||
#if TARGET_OS_IPHONE
|
||||
photo = [[UIImage alloc] initWithData:[coder decodeObjectOfClass:[NSData class] forKey:@"photo"]];
|
||||
#else
|
||||
photo = [[NSImage alloc] initWithData:[coder decodeObjectOfClass:[NSData class] forKey:@"photo"]];
|
||||
#endif
|
||||
resources = [[coder decodeObjectOfClass:[NSDictionary class] forKey:@"resources"] mutableCopy];
|
||||
primaryResource = [coder decodeObjectOfClass:[XMPPResourceMemoryStorageObject class] forKey:@"primaryResource"];
|
||||
}
|
||||
else
|
||||
{
|
||||
jid = [coder decodeObjectForKey:@"jid"];
|
||||
itemAttributes = [[coder decodeObjectForKey:@"itemAttributes"] mutableCopy];
|
||||
groups = [[coder decodeObjectForKey:@"groups"] mutableCopy];
|
||||
#if TARGET_OS_IPHONE
|
||||
photo = [[UIImage alloc] initWithData:[coder decodeObjectForKey:@"photo"]];
|
||||
#else
|
||||
photo = [[NSImage alloc] initWithData:[coder decodeObjectForKey:@"photo"]];
|
||||
#endif
|
||||
resources = [[coder decodeObjectForKey:@"resources"] mutableCopy];
|
||||
primaryResource = [coder decodeObjectForKey:@"primaryResource"];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
jid = [coder decodeObject];
|
||||
itemAttributes = [[coder decodeObject] mutableCopy];
|
||||
groups = [[coder decodeObject] mutableCopy];
|
||||
#if TARGET_OS_IPHONE
|
||||
photo = [[UIImage alloc] initWithData:[coder decodeObject]];
|
||||
#else
|
||||
photo = [[NSImage alloc] initWithData:[coder decodeObject]];
|
||||
#endif
|
||||
resources = [[coder decodeObject] mutableCopy];
|
||||
primaryResource = [coder decodeObject];
|
||||
}
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(NSCoder *)coder
|
||||
{
|
||||
if ([coder allowsKeyedCoding])
|
||||
{
|
||||
[coder encodeObject:jid forKey:@"jid"];
|
||||
[coder encodeObject:itemAttributes forKey:@"itemAttributes"];
|
||||
[coder encodeObject:groups forKey:@"groups"];
|
||||
#if TARGET_OS_IPHONE
|
||||
[coder encodeObject:UIImagePNGRepresentation(photo) forKey:@"photo"];
|
||||
#else
|
||||
[coder encodeObject:[photo TIFFRepresentation] forKey:@"photo"];
|
||||
#endif
|
||||
[coder encodeObject:resources forKey:@"resources"];
|
||||
[coder encodeObject:primaryResource forKey:@"primaryResource"];
|
||||
}
|
||||
else
|
||||
{
|
||||
[coder encodeObject:jid];
|
||||
[coder encodeObject:itemAttributes];
|
||||
[coder encodeObject:groups];
|
||||
#if TARGET_OS_IPHONE
|
||||
[coder encodeObject:UIImagePNGRepresentation(photo)];
|
||||
#else
|
||||
[coder encodeObject:[photo TIFFRepresentation]];
|
||||
#endif
|
||||
[coder encodeObject:resources];
|
||||
[coder encodeObject:primaryResource];
|
||||
}
|
||||
}
|
||||
|
||||
+ (BOOL) supportsSecureCoding
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark Standard Methods
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@synthesize photo;
|
||||
|
||||
- (XMPPJID *)jid
|
||||
{
|
||||
return jid;
|
||||
}
|
||||
|
||||
- (NSString *)nickname
|
||||
{
|
||||
return (NSString *) itemAttributes[@"name"];
|
||||
}
|
||||
|
||||
- (NSString *)subscription
|
||||
{
|
||||
return (NSString *) itemAttributes[@"subscription"];
|
||||
}
|
||||
|
||||
- (NSString *)ask
|
||||
{
|
||||
return (NSString *) itemAttributes[@"ask"];
|
||||
}
|
||||
|
||||
- (NSString *)displayName
|
||||
{
|
||||
NSString *nickname = [self nickname];
|
||||
if (nickname)
|
||||
return nickname;
|
||||
else
|
||||
return [jid bare];
|
||||
}
|
||||
|
||||
- (NSArray *)groups
|
||||
{
|
||||
return [groups copy];
|
||||
}
|
||||
|
||||
- (BOOL)isOnline
|
||||
{
|
||||
return (primaryResource != nil);
|
||||
}
|
||||
|
||||
- (BOOL)isPendingApproval
|
||||
{
|
||||
// Either of the following mean we're waiting to have our presence subscription approved:
|
||||
// <item ask='subscribe' subscription='none' jid='robbiehanson@deusty.com'/>
|
||||
// <item ask='subscribe' subscription='from' jid='robbiehanson@deusty.com'/>
|
||||
|
||||
NSString *subscription = itemAttributes[@"subscription"];
|
||||
NSString *ask = itemAttributes[@"ask"];
|
||||
|
||||
if ([subscription isEqualToString:@"none"] || [subscription isEqualToString:@"from"])
|
||||
{
|
||||
if([ask isEqualToString:@"subscribe"])
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (id <XMPPResource>)primaryResource
|
||||
{
|
||||
return primaryResource;
|
||||
}
|
||||
|
||||
- (id <XMPPResource>)resourceForJID:(XMPPJID *)aJid
|
||||
{
|
||||
return resources[aJid];
|
||||
}
|
||||
|
||||
- (NSArray *)allResources
|
||||
{
|
||||
return [resources allValues];
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark Hooks
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
- (void)didAddResource:(XMPPResourceMemoryStorageObject *)resource withPresence:(XMPPPresence *)presence
|
||||
{
|
||||
// Override / customization hook
|
||||
}
|
||||
|
||||
- (void)willUpdateResource:(XMPPResourceMemoryStorageObject *)resource withPresence:(XMPPPresence *)presence
|
||||
{
|
||||
// Override / customization hook
|
||||
}
|
||||
|
||||
- (void)didUpdateResource:(XMPPResourceMemoryStorageObject *)resource withPresence:(XMPPPresence *)presence
|
||||
{
|
||||
// Override / customization hook
|
||||
}
|
||||
|
||||
- (void)didRemoveResource:(XMPPResourceMemoryStorageObject *)resource withPresence:(XMPPPresence *)presence
|
||||
{
|
||||
// Override / customization hook
|
||||
}
|
||||
|
||||
- (void)recalculatePrimaryResource
|
||||
{
|
||||
// Override me to customize how the primary resource is chosen.
|
||||
//
|
||||
// This method uses the [XMPPResourceMemoryStorage compare:] method to sort the resources,
|
||||
// and properly supports negative (bot) priorities.
|
||||
|
||||
primaryResource = nil;
|
||||
|
||||
NSArray *sortedResources = [[self allResources] sortedArrayUsingSelector:@selector(compare:)];
|
||||
if ([sortedResources count] > 0)
|
||||
{
|
||||
XMPPResourceMemoryStorageObject *possiblePrimary = sortedResources[0];
|
||||
|
||||
// Primary resource must have a non-negative priority
|
||||
if ([[possiblePrimary presence] priority] >= 0)
|
||||
{
|
||||
primaryResource = possiblePrimary;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark Update Methods
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
- (void)clearAllResources
|
||||
{
|
||||
[resources removeAllObjects];
|
||||
|
||||
primaryResource = nil;
|
||||
}
|
||||
|
||||
- (void)updateWithItem:(NSXMLElement *)item
|
||||
{
|
||||
[itemAttributes removeAllObjects];
|
||||
|
||||
for (NSXMLNode *node in [item attributes])
|
||||
{
|
||||
NSString *key = [node name];
|
||||
NSString *value = [node stringValue];
|
||||
|
||||
itemAttributes[key] = value;
|
||||
}
|
||||
|
||||
[groups removeAllObjects];
|
||||
|
||||
NSArray *groupElements = [item elementsForName:@"group"];
|
||||
|
||||
for (NSXMLElement *groupElement in groupElements) {
|
||||
NSString *groupName = [groupElement stringValue];
|
||||
|
||||
if ([groupName length])
|
||||
{
|
||||
[groups addObject:groupName];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (int)updateWithPresence:(XMPPPresence *)presence
|
||||
resourceClass:(Class)resourceClass
|
||||
andGetResource:(XMPPResourceMemoryStorageObject **)resourcePtr
|
||||
{
|
||||
int result = XMPP_USER_NO_CHANGE;
|
||||
XMPPResourceMemoryStorageObject *resource;
|
||||
|
||||
XMPPJID *key = [presence from];
|
||||
NSString *presenceType = [presence type];
|
||||
|
||||
if ([presenceType isEqualToString:@"unavailable"] || [presenceType isEqualToString:@"error"])
|
||||
{
|
||||
resource = resources[key];
|
||||
if (resource)
|
||||
{
|
||||
[resources removeObjectForKey:key];
|
||||
[self didRemoveResource:resource withPresence:presence];
|
||||
|
||||
result = XMPP_USER_REMOVED_RESOURCE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
resource = resources[key];
|
||||
if (resource)
|
||||
{
|
||||
[self willUpdateResource:resource withPresence:presence];
|
||||
[resource updateWithPresence:presence];
|
||||
[self didUpdateResource:resource withPresence:presence];
|
||||
|
||||
result = XMPP_USER_UPDATED_RESOURCE;
|
||||
}
|
||||
else
|
||||
{
|
||||
resource = (XMPPResourceMemoryStorageObject *)[[resourceClass alloc] initWithPresence:presence];
|
||||
|
||||
resources[key] = resource;
|
||||
[self didAddResource:resource withPresence:presence];
|
||||
|
||||
result = XMPP_USER_ADDED_RESOURCE;
|
||||
}
|
||||
}
|
||||
|
||||
[self recalculatePrimaryResource];
|
||||
|
||||
if (resourcePtr)
|
||||
*resourcePtr = resource;
|
||||
return result;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark Comparisons
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* Returns the result of invoking compareByName:options: with no options.
|
||||
**/
|
||||
- (NSComparisonResult)compareByName:(XMPPUserMemoryStorageObject *)another
|
||||
{
|
||||
return [self compareByName:another options:0];
|
||||
}
|
||||
|
||||
/**
|
||||
* This method compares the two users according to their display name.
|
||||
*
|
||||
* Options for the search — you can combine any of the following using a C bitwise OR operator:
|
||||
* NSCaseInsensitiveSearch, NSLiteralSearch, NSNumericSearch.
|
||||
* See "String Programming Guide for Cocoa" for details on these options.
|
||||
**/
|
||||
- (NSComparisonResult)compareByName:(XMPPUserMemoryStorageObject *)another options:(NSStringCompareOptions)mask
|
||||
{
|
||||
NSString *myName = [self displayName];
|
||||
NSString *theirName = [another displayName];
|
||||
|
||||
return [myName compare:theirName options:mask];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the result of invoking compareByAvailabilityName:options: with no options.
|
||||
**/
|
||||
- (NSComparisonResult)compareByAvailabilityName:(XMPPUserMemoryStorageObject *)another
|
||||
{
|
||||
return [self compareByAvailabilityName:another options:0];
|
||||
}
|
||||
|
||||
/**
|
||||
* This method compares the two users according to availability first, and then display name.
|
||||
* Thus available users come before unavailable users.
|
||||
* If both users are available, or both users are not available,
|
||||
* this method follows the same functionality as the compareByName:options: as documented above.
|
||||
**/
|
||||
- (NSComparisonResult)compareByAvailabilityName:(XMPPUserMemoryStorageObject *)another
|
||||
options:(NSStringCompareOptions)mask
|
||||
{
|
||||
if ([self isOnline])
|
||||
{
|
||||
if ([another isOnline])
|
||||
return [self compareByName:another options:mask];
|
||||
else
|
||||
return NSOrderedAscending;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ([another isOnline])
|
||||
return NSOrderedDescending;
|
||||
else
|
||||
return [self compareByName:another options:mask];
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark NSObject Methods
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
- (NSUInteger)hash
|
||||
{
|
||||
return [jid hash];
|
||||
}
|
||||
|
||||
- (BOOL)isEqual:(id)anObject
|
||||
{
|
||||
if ([anObject isMemberOfClass:[self class]])
|
||||
{
|
||||
XMPPUserMemoryStorageObject *another = (XMPPUserMemoryStorageObject *)anObject;
|
||||
|
||||
return [jid isEqualToJID:[another jid]];
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (NSString *)description
|
||||
{
|
||||
return [NSString stringWithFormat:@"<XMPPUser[%p]: %@>", self, [jid bare]];
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark KVO Compliance methods
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
+ (NSSet *)keyPathsForValuesAffectingIsOnline
|
||||
{
|
||||
return [NSSet setWithObject:@"primaryResource"];
|
||||
}
|
||||
|
||||
+ (NSSet *)keyPathsForValuesAffectingAllResources {
|
||||
return [NSSet setWithObject:@"resources"];
|
||||
}
|
||||
|
||||
@end
|
||||
Reference in new issue
Block a user