- Update
This commit is contained in:
1 parent
d281d765ea
commit
5b074a5176
1261 files changed
+199158
-7303
No files matched your search
+603
@@ -0,0 +1,603 @@
|
||||
#import "XMPPJID.h"
|
||||
#import "XMPPStringPrep.h"
|
||||
|
||||
#if ! __has_feature(objc_arc)
|
||||
#warning This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC).
|
||||
#endif
|
||||
|
||||
|
||||
@implementation XMPPJID
|
||||
|
||||
+ (BOOL)validateDomain:(NSString *)domain
|
||||
{
|
||||
// Domain is the only required part of a JID
|
||||
if ((domain == nil) || ([domain length] == 0))
|
||||
return NO;
|
||||
|
||||
// If there's an @ symbol in the domain it probably means user put @ in their username
|
||||
NSRange invalidAtRange = [domain rangeOfString:@"@"];
|
||||
if (invalidAtRange.location != NSNotFound)
|
||||
return NO;
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
+ (BOOL)validateResource:(NSString *)resource
|
||||
{
|
||||
// Can't use an empty string resource name
|
||||
if ((resource != nil) && ([resource length] == 0))
|
||||
return NO;
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
+ (BOOL)validateUser:(NSString *)user domain:(NSString *)domain resource:(NSString *)resource
|
||||
{
|
||||
if (![self validateDomain:domain])
|
||||
return NO;
|
||||
|
||||
if (![self validateResource:resource])
|
||||
return NO;
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
+ (BOOL)parse:(NSString *)jidStr
|
||||
outUser:(NSString **)user
|
||||
outDomain:(NSString **)domain
|
||||
outResource:(NSString **)resource
|
||||
{
|
||||
if(user) *user = nil;
|
||||
if(domain) *domain = nil;
|
||||
if(resource) *resource = nil;
|
||||
|
||||
if(jidStr == nil) return NO;
|
||||
|
||||
NSString *rawUser = nil;
|
||||
NSString *rawDomain = nil;
|
||||
NSString *rawResource = nil;
|
||||
|
||||
NSRange atRange = [jidStr rangeOfString:@"@"];
|
||||
|
||||
if (atRange.location != NSNotFound)
|
||||
{
|
||||
rawUser = [jidStr substringToIndex:atRange.location];
|
||||
|
||||
NSString *minusUser = [jidStr substringFromIndex:atRange.location+1];
|
||||
|
||||
NSRange slashRange = [minusUser rangeOfString:@"/"];
|
||||
|
||||
if (slashRange.location != NSNotFound)
|
||||
{
|
||||
rawDomain = [minusUser substringToIndex:slashRange.location];
|
||||
rawResource = [minusUser substringFromIndex:slashRange.location+1];
|
||||
}
|
||||
else
|
||||
{
|
||||
rawDomain = minusUser;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
NSRange slashRange = [jidStr rangeOfString:@"/"];
|
||||
|
||||
if (slashRange.location != NSNotFound)
|
||||
{
|
||||
rawDomain = [jidStr substringToIndex:slashRange.location];
|
||||
rawResource = [jidStr substringFromIndex:slashRange.location+1];
|
||||
}
|
||||
else
|
||||
{
|
||||
rawDomain = jidStr;
|
||||
}
|
||||
}
|
||||
|
||||
NSString *prepUser = [XMPPStringPrep prepNode:rawUser];
|
||||
NSString *prepDomain = [XMPPStringPrep prepDomain:rawDomain];
|
||||
NSString *prepResource = [XMPPStringPrep prepResource:rawResource];
|
||||
|
||||
if ([XMPPJID validateUser:prepUser domain:prepDomain resource:prepResource])
|
||||
{
|
||||
if(user) *user = prepUser;
|
||||
if(domain) *domain = prepDomain;
|
||||
if(resource) *resource = prepResource;
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
+ (XMPPJID *)jidWithString:(NSString *)jidStr
|
||||
{
|
||||
NSString *user;
|
||||
NSString *domain;
|
||||
NSString *resource;
|
||||
|
||||
if ([XMPPJID parse:jidStr outUser:&user outDomain:&domain outResource:&resource])
|
||||
{
|
||||
XMPPJID *jid = [[XMPPJID alloc] init];
|
||||
jid->user = [user copy];
|
||||
jid->domain = [domain copy];
|
||||
jid->resource = [resource copy];
|
||||
|
||||
return jid;
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
+ (XMPPJID *)jidWithString:(NSString *)jidStr resource:(NSString *)resource
|
||||
{
|
||||
NSString *prepResource = [XMPPStringPrep prepResource:resource];
|
||||
if (![self validateResource:prepResource]) return nil;
|
||||
|
||||
NSString *user;
|
||||
NSString *domain;
|
||||
|
||||
if ([XMPPJID parse:jidStr outUser:&user outDomain:&domain outResource:nil])
|
||||
{
|
||||
XMPPJID *jid = [[XMPPJID alloc] init];
|
||||
jid->user = [user copy];
|
||||
jid->domain = [domain copy];
|
||||
jid->resource = [prepResource copy];
|
||||
|
||||
return jid;
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
+ (XMPPJID *)jidWithUser:(NSString *)user domain:(NSString *)domain resource:(NSString *)resource
|
||||
{
|
||||
NSString *prepUser = [XMPPStringPrep prepNode:user];
|
||||
NSString *prepDomain = [XMPPStringPrep prepDomain:domain];
|
||||
NSString *prepResource = [XMPPStringPrep prepResource:resource];
|
||||
|
||||
if ([XMPPJID validateUser:prepUser domain:prepDomain resource:prepResource])
|
||||
{
|
||||
XMPPJID *jid = [[XMPPJID alloc] init];
|
||||
jid->user = [prepUser copy];
|
||||
jid->domain = [prepDomain copy];
|
||||
jid->resource = [prepResource copy];
|
||||
|
||||
return jid;
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
+ (XMPPJID *)jidWithPrevalidatedUser:(NSString *)user
|
||||
prevalidatedDomain:(NSString *)domain
|
||||
prevalidatedResource:(NSString *)resource
|
||||
{
|
||||
XMPPJID *jid = [[XMPPJID alloc] init];
|
||||
jid->user = [user copy];
|
||||
jid->domain = [domain copy];
|
||||
jid->resource = [resource copy];
|
||||
|
||||
return jid;
|
||||
}
|
||||
|
||||
+ (XMPPJID *)jidWithPrevalidatedUser:(NSString *)user
|
||||
prevalidatedDomain:(NSString *)domain
|
||||
resource:(NSString *)resource
|
||||
{
|
||||
NSString *prepResource = [XMPPStringPrep prepResource:resource];
|
||||
if (![self validateResource:prepResource]) return nil;
|
||||
|
||||
XMPPJID *jid = [[XMPPJID alloc] init];
|
||||
jid->user = [user copy];
|
||||
jid->domain = [domain copy];
|
||||
jid->resource = [prepResource copy];
|
||||
|
||||
return jid;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#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])
|
||||
{
|
||||
user = [[coder decodeObjectOfClass:[NSString class] forKey:@"user"] copy];
|
||||
domain = [[coder decodeObjectOfClass:[NSString class] forKey:@"domain"] copy];
|
||||
resource = [[coder decodeObjectOfClass:[NSString class] forKey:@"resource"] copy];
|
||||
}
|
||||
else
|
||||
{
|
||||
user = [[coder decodeObjectForKey:@"user"] copy];
|
||||
domain = [[coder decodeObjectForKey:@"domain"] copy];
|
||||
resource = [[coder decodeObjectForKey:@"resource"] copy];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
user = [[coder decodeObject] copy];
|
||||
domain = [[coder decodeObject] copy];
|
||||
resource = [[coder decodeObject] copy];
|
||||
}
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(NSCoder *)coder
|
||||
{
|
||||
if ([coder allowsKeyedCoding])
|
||||
{
|
||||
[coder encodeObject:user forKey:@"user"];
|
||||
[coder encodeObject:domain forKey:@"domain"];
|
||||
[coder encodeObject:resource forKey:@"resource"];
|
||||
}
|
||||
else
|
||||
{
|
||||
[coder encodeObject:user];
|
||||
[coder encodeObject:domain];
|
||||
[coder encodeObject:resource];
|
||||
}
|
||||
}
|
||||
|
||||
+ (BOOL) supportsSecureCoding
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark Copying:
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
- (id)copyWithZone:(NSZone *)zone
|
||||
{
|
||||
// This class is immutable
|
||||
return self;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark Normal Methods:
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Why didn't we just synthesize these properties?
|
||||
//
|
||||
// Since these variables are readonly within the class,
|
||||
// we want the synthesized methods to work like a nonatomic property.
|
||||
// In order to do this, we have to mark the properties as nonatomic in the header.
|
||||
// However we don't like marking the property as nonatomic in the header because
|
||||
// then people might think it's not thread-safe when in fact it is.
|
||||
|
||||
- (NSString *)user
|
||||
{
|
||||
return user; // Why didn't we just synthesize this? See comment above.
|
||||
}
|
||||
|
||||
- (NSString *)domain
|
||||
{
|
||||
return domain; // Why didn't we just synthesize this? See comment above.
|
||||
}
|
||||
|
||||
- (NSString *)resource
|
||||
{
|
||||
return resource; // Why didn't we just synthesize this? See comment above.
|
||||
}
|
||||
|
||||
- (XMPPJID *)bareJID
|
||||
{
|
||||
if (resource == nil)
|
||||
{
|
||||
return self;
|
||||
}
|
||||
else
|
||||
{
|
||||
return [XMPPJID jidWithPrevalidatedUser:user prevalidatedDomain:domain prevalidatedResource:nil];
|
||||
}
|
||||
}
|
||||
|
||||
- (XMPPJID *)domainJID
|
||||
{
|
||||
if (user == nil && resource == nil)
|
||||
{
|
||||
return self;
|
||||
}
|
||||
else
|
||||
{
|
||||
return [XMPPJID jidWithPrevalidatedUser:nil prevalidatedDomain:domain prevalidatedResource:nil];
|
||||
}
|
||||
}
|
||||
|
||||
- (NSString *)bare
|
||||
{
|
||||
if (user)
|
||||
return [NSString stringWithFormat:@"%@@%@", user, domain];
|
||||
else
|
||||
return domain;
|
||||
}
|
||||
|
||||
- (NSString *)full
|
||||
{
|
||||
if (user)
|
||||
{
|
||||
if (resource)
|
||||
return [NSString stringWithFormat:@"%@@%@/%@", user, domain, resource];
|
||||
else
|
||||
return [NSString stringWithFormat:@"%@@%@", user, domain];
|
||||
}
|
||||
else
|
||||
{
|
||||
if (resource)
|
||||
return [NSString stringWithFormat:@"%@/%@", domain, resource];
|
||||
else
|
||||
return domain;
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)isBare
|
||||
{
|
||||
// From RFC 6120 Terminology:
|
||||
//
|
||||
// The term "bare JID" refers to an XMPP address of the form <localpart@domainpart> (for an account at a server)
|
||||
// or of the form <domainpart> (for a server).
|
||||
|
||||
return (resource == nil);
|
||||
}
|
||||
|
||||
- (BOOL)isBareWithUser
|
||||
{
|
||||
return (user != nil && resource == nil);
|
||||
}
|
||||
|
||||
- (BOOL)isFull
|
||||
{
|
||||
// From RFC 6120 Terminology:
|
||||
//
|
||||
// The term "full JID" refers to an XMPP address of the form
|
||||
// <localpart@domainpart/resourcepart> (for a particular authorized client or device associated with an account)
|
||||
// or of the form <domainpart/resourcepart> (for a particular resource or script associated with a server).
|
||||
|
||||
return (resource != nil);
|
||||
}
|
||||
|
||||
- (BOOL)isFullWithUser
|
||||
{
|
||||
return (user != nil && resource != nil);
|
||||
}
|
||||
|
||||
- (BOOL)isServer
|
||||
{
|
||||
return (user == nil);
|
||||
}
|
||||
|
||||
- (XMPPJID *)jidWithNewResource:(NSString *)newResource
|
||||
{
|
||||
return [XMPPJID jidWithPrevalidatedUser:user prevalidatedDomain:domain resource:newResource];
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark NSObject Methods:
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
- (NSUInteger)hash
|
||||
{
|
||||
// We used to do this:
|
||||
// return [[self full] hash];
|
||||
//
|
||||
// It was functional but less than optimal because it required the creation of a new NSString everytime.
|
||||
// Now the hashing of a string itself is extremely fast,
|
||||
// so combining 3 hashes is much faster than creating a new string.
|
||||
// To accomplish this we use the murmur hashing algorithm.
|
||||
//
|
||||
// MurmurHash2 was written by Austin Appleby, and is placed in the public domain.
|
||||
// http://code.google.com/p/smhasher
|
||||
|
||||
NSUInteger uhash = [user hash];
|
||||
NSUInteger dhash = [domain hash];
|
||||
NSUInteger rhash = [resource hash];
|
||||
|
||||
if (NSUIntegerMax == UINT32_MAX) // Should be optimized out via compiler since these are constants
|
||||
{
|
||||
// MurmurHash2 (32-bit)
|
||||
//
|
||||
// uint32_t MurmurHash2 ( const void * key, int len, uint32_t seed )
|
||||
//
|
||||
// Normally one would pass a chunk of data ('key') and associated data chunk length ('len').
|
||||
// Instead we're going to use our 3 hashes.
|
||||
// And we're going to randomly make up a 'seed'.
|
||||
|
||||
const uint32_t seed = 0xa2f1b6f; // Some random value I made up
|
||||
const uint32_t len = 12; // 3 hashes, each 4 bytes = 12 bytes
|
||||
|
||||
// 'm' and 'r' are mixing constants generated offline.
|
||||
// They're not really 'magic', they just happen to work well.
|
||||
|
||||
const uint32_t m = 0x5bd1e995;
|
||||
const int r = 24;
|
||||
|
||||
// Initialize the hash to a 'random' value
|
||||
|
||||
uint32_t h = seed ^ len;
|
||||
uint32_t k;
|
||||
|
||||
// Mix uhash
|
||||
|
||||
k = uhash;
|
||||
|
||||
k *= m;
|
||||
k ^= k >> r;
|
||||
k *= m;
|
||||
|
||||
h *= m;
|
||||
h ^= k;
|
||||
|
||||
// Mix dhash
|
||||
|
||||
k = dhash;
|
||||
|
||||
k *= m;
|
||||
k ^= k >> r;
|
||||
k *= m;
|
||||
|
||||
h *= m;
|
||||
h ^= k;
|
||||
|
||||
// Mix rhash
|
||||
|
||||
k = rhash;
|
||||
|
||||
k *= m;
|
||||
k ^= k >> r;
|
||||
k *= m;
|
||||
|
||||
h *= m;
|
||||
h ^= k;
|
||||
|
||||
// Do a few final mixes of the hash to ensure the last few
|
||||
// bytes are well-incorporated.
|
||||
|
||||
h ^= h >> 13;
|
||||
h *= m;
|
||||
h ^= h >> 15;
|
||||
|
||||
return (NSUInteger)h;
|
||||
}
|
||||
else
|
||||
{
|
||||
// MurmurHash2 (64-bit)
|
||||
//
|
||||
// uint64_t MurmurHash64A ( const void * key, int len, uint64_t seed )
|
||||
//
|
||||
// Normally one would pass a chunk of data ('key') and associated data chunk length ('len').
|
||||
// Instead we're going to use our 3 hashes.
|
||||
// And we're going to randomly make up a 'seed'.
|
||||
|
||||
const uint32_t seed = 0xa2f1b6f; // Some random value I made up
|
||||
const uint32_t len = 24; // 3 hashes, each 8 bytes = 24 bytes
|
||||
|
||||
// 'm' and 'r' are mixing constants generated offline.
|
||||
// They're not really 'magic', they just happen to work well.
|
||||
|
||||
const uint64_t m = 0xc6a4a7935bd1e995LLU;
|
||||
const int r = 47;
|
||||
|
||||
// Initialize the hash to a 'random' value
|
||||
|
||||
uint64_t h = seed ^ (len * m);
|
||||
uint64_t k;
|
||||
|
||||
// Mix uhash
|
||||
|
||||
k = uhash;
|
||||
|
||||
k *= m;
|
||||
k ^= k >> r;
|
||||
k *= m;
|
||||
|
||||
h ^= k;
|
||||
h *= m;
|
||||
|
||||
// Mix dhash
|
||||
|
||||
k = dhash;
|
||||
|
||||
k *= m;
|
||||
k ^= k >> r;
|
||||
k *= m;
|
||||
|
||||
h ^= k;
|
||||
h *= m;
|
||||
|
||||
// Mix rhash
|
||||
|
||||
k = rhash;
|
||||
|
||||
k *= m;
|
||||
k ^= k >> r;
|
||||
k *= m;
|
||||
|
||||
h ^= k;
|
||||
h *= m;
|
||||
|
||||
// Do a few final mixes of the hash to ensure the last few
|
||||
// bytes are well-incorporated.
|
||||
|
||||
h ^= h >> r;
|
||||
h *= m;
|
||||
h ^= h >> r;
|
||||
|
||||
return (NSUInteger)h;
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)isEqual:(id)anObject
|
||||
{
|
||||
if ([anObject isMemberOfClass:[self class]])
|
||||
{
|
||||
return [self isEqualToJID:(XMPPJID *)anObject options:XMPPJIDCompareFull];
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (BOOL)isEqualToJID:(XMPPJID *)aJID
|
||||
{
|
||||
return [self isEqualToJID:aJID options:XMPPJIDCompareFull];
|
||||
}
|
||||
|
||||
- (BOOL)isEqualToJID:(XMPPJID *)aJID options:(XMPPJIDCompareOptions)mask
|
||||
{
|
||||
if (aJID == nil) return NO;
|
||||
|
||||
if (mask & XMPPJIDCompareUser)
|
||||
{
|
||||
if (user) {
|
||||
if (![user isEqualToString:aJID->user]) return NO;
|
||||
}
|
||||
else {
|
||||
if (aJID->user) return NO;
|
||||
}
|
||||
}
|
||||
|
||||
if (mask & XMPPJIDCompareDomain)
|
||||
{
|
||||
if (domain) {
|
||||
if (![domain isEqualToString:aJID->domain]) return NO;
|
||||
}
|
||||
else {
|
||||
if (aJID->domain) return NO;
|
||||
}
|
||||
}
|
||||
|
||||
if (mask & XMPPJIDCompareResource)
|
||||
{
|
||||
if (resource) {
|
||||
if (![resource isEqualToString:aJID->resource]) return NO;
|
||||
}
|
||||
else {
|
||||
if (aJID->resource) return NO;
|
||||
}
|
||||
}
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (NSString *)description
|
||||
{
|
||||
return [self full];
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
Reference in new issue
Block a user