This commit is contained in:
giuseppenuc committed 2016-02-24 16:56:39 +01:00
1 parent d281d765ea
commit 5b074a5176
1261 files changed
+199158 -7303

No files matched your search

@@ -0,0 +1,54 @@
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
#import "XMPPCapabilities.h"
#import "XMPPCoreDataStorage.h"
/**
* This class is an example implementation of XMPPCapabilitiesStorage using core data.
* You are free to substitute your own storage class.
**/
@interface XMPPCapabilitiesCoreDataStorage : XMPPCoreDataStorage <XMPPCapabilitiesStorage>
{
/* Inherited protected variables from XMPPCoreDataStorage
NSString *databaseFileName;
NSUInteger saveThreshold;
dispatch_queue_t storageQueue;
*/
}
/**
* XEP-0115 provides a mechanism for hashing a list of capabilities.
* Clients then broadcast this hash instead of the entire list to save bandwidth.
* Because the hashing is standardized, it is safe to persistently store the linked hash & capabilities.
*
* For this reason, it is recommended you use this sharedInstance across all your xmppStreams.
* This way all streams can shared a knowledgebase concerning known hashes.
*
* All other aspects of capabilities handling (such as JID's, lookup failures, etc) are kept separate between streams.
**/
+ (instancetype)sharedInstance;
/* Inherited from XMPPCoreDataStorage
* Please see the XMPPCoreDataStorage header file for extensive documentation.
- (id)initWithDatabaseFilename:(NSString *)databaseFileName storeOptions:(NSDictionary *)storeOptions;
- (id)initWithInMemoryStore;
@property (readonly) NSString *databaseFileName;
@property (readwrite) NSUInteger saveThreshold;
@property (readonly) NSManagedObjectModel *managedObjectModel;
@property (readonly) NSPersistentStoreCoordinator *persistentStoreCoordinator;
@property (readonly) NSManagedObjectContext *mainThreadManagedObjectContext;
*/
@end
@@ -0,0 +1,587 @@
#import "XMPPCapabilitiesCoreDataStorage.h"
#import "XMPPCapsCoreDataStorageObject.h"
#import "XMPPCapsResourceCoreDataStorageObject.h"
#import "XMPP.h"
#import "XMPPCoreDataStorageProtected.h"
#import "XMPPLogging.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
#if DEBUG
static const int xmppLogLevel = XMPP_LOG_LEVEL_WARN; // | XMPP_LOG_FLAG_TRACE;
#else
static const int xmppLogLevel = XMPP_LOG_LEVEL_WARN;
#endif
@implementation XMPPCapabilitiesCoreDataStorage
static XMPPCapabilitiesCoreDataStorage *sharedInstance;
+ (instancetype)sharedInstance
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[XMPPCapabilitiesCoreDataStorage alloc] initWithDatabaseFilename:nil storeOptions:nil];
});
return sharedInstance;
}
- (void)commonInit
{
XMPPLogTrace();
[super commonInit];
autoRecreateDatabaseFile = YES;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark Setup
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (BOOL)configureWithParent:(XMPPCapabilities *)aParent queue:(dispatch_queue_t)queue
{
return [super configureWithParent:aParent queue:queue];
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark Utilities
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (XMPPCapsResourceCoreDataStorageObject *)resourceForJID:(XMPPJID *)jid xmppStream:(XMPPStream *)stream
{
NSAssert(dispatch_get_specific(storageQueueTag), @"Invoked on incorrect queue");
XMPPLogTrace2(@"%@: %@ %@", THIS_FILE, THIS_METHOD, jid);
if (jid == nil) return nil;
NSEntityDescription *entity = [NSEntityDescription entityForName:@"XMPPCapsResourceCoreDataStorageObject"
inManagedObjectContext:[self managedObjectContext]];
NSPredicate *predicate;
if (stream == nil)
predicate = [NSPredicate predicateWithFormat:@"jidStr == %@", [jid full]];
else
predicate = [NSPredicate predicateWithFormat:@"jidStr == %@ AND streamBareJidStr == %@",
[jid full], [[self myJIDForXMPPStream:stream] bare]];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
[fetchRequest setEntity:entity];
[fetchRequest setPredicate:predicate];
[fetchRequest setFetchLimit:1];
NSArray *results = [[self managedObjectContext] executeFetchRequest:fetchRequest error:nil];
XMPPCapsResourceCoreDataStorageObject *resource = [results lastObject];
XMPPLogVerbose(@"%@: %@ - %@", THIS_FILE, THIS_METHOD, resource);
return resource;
}
- (XMPPCapsCoreDataStorageObject *)capsForHash:(NSString *)hash algorithm:(NSString *)hashAlg
{
NSAssert(dispatch_get_specific(storageQueueTag), @"Invoked on incorrect queue");
XMPPLogTrace2(@"%@: capsForHash:%@ algorithm:%@", THIS_FILE, hash, hashAlg);
if (hash == nil) return nil;
if (hashAlg == nil) return nil;
NSEntityDescription *entity = [NSEntityDescription entityForName:@"XMPPCapsCoreDataStorageObject"
inManagedObjectContext:[self managedObjectContext]];
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"hashStr == %@ AND hashAlgorithm == %@",
hash, hashAlg];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
[fetchRequest setEntity:entity];
[fetchRequest setPredicate:predicate];
[fetchRequest setFetchLimit:1];
NSArray *results = [[self managedObjectContext] executeFetchRequest:fetchRequest error:nil];
XMPPCapsCoreDataStorageObject *caps = [results lastObject];
XMPPLogVerbose(@"%@: %@ - %@", THIS_FILE, THIS_METHOD, caps);
return caps;
}
- (void)_clearAllNonPersistentCapabilitiesForXMPPStream:(XMPPStream *)stream
{
NSAssert(dispatch_get_specific(storageQueueTag), @"Invoked on incorrect queue");
NSEntityDescription *entity = [NSEntityDescription entityForName:@"XMPPCapsResourceCoreDataStorageObject"
inManagedObjectContext:[self managedObjectContext]];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
[fetchRequest setEntity:entity];
[fetchRequest setFetchBatchSize:saveThreshold];
if (stream)
{
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"streamBareJidStr == %@",
[[self myJIDForXMPPStream:stream] bare]];
[fetchRequest setPredicate:predicate];
}
NSArray *results = [[self managedObjectContext] executeFetchRequest:fetchRequest error:nil];
NSUInteger unsavedCount = [self numberOfUnsavedChanges];
for (XMPPCapsResourceCoreDataStorageObject *resource in results)
{
NSString *hash = resource.hashStr;
NSString *hashAlg = resource.hashAlgorithm;
BOOL nonPersistentCapabilities = ((hash == nil) || (hashAlg == nil));
if (nonPersistentCapabilities)
{
XMPPCapsCoreDataStorageObject *caps = resource.caps;
if (caps)
{
[[self managedObjectContext] deleteObject:caps];
}
}
[[self managedObjectContext] deleteObject:resource];
if (++unsavedCount >= saveThreshold)
{
[self save];
}
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark Overrides
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (void)didCreateManagedObjectContext
{
// This method is overriden from the XMPPCoreDataStore superclass.
[self _clearAllNonPersistentCapabilitiesForXMPPStream:nil];
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark Protocol Public API
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (BOOL)areCapabilitiesKnownForJID:(XMPPJID *)jid xmppStream:(XMPPStream *)stream
{
XMPPLogTrace();
__block BOOL result;
[self executeBlock:^{
XMPPCapsResourceCoreDataStorageObject *resource = [self resourceForJID:jid xmppStream:stream];
result = (resource.caps != nil);
}];
return result;
}
- (NSXMLElement *)capabilitiesForJID:(XMPPJID *)jid xmppStream:(XMPPStream *)stream
{
XMPPLogTrace();
return [self capabilitiesForJID:jid ext:nil xmppStream:stream];
}
- (NSXMLElement *)capabilitiesForJID:(XMPPJID *)jid ext:(NSString **)extPtr xmppStream:(XMPPStream *)stream
{
// By design this method should not be invoked from the storageQueue.
NSAssert(!dispatch_get_specific(storageQueueTag), @"Invoked on incorrect queue");
XMPPLogTrace();
__block NSXMLElement *result = nil;
__block NSString *ext = nil;
[self executeBlock:^{
XMPPCapsResourceCoreDataStorageObject *resource = [self resourceForJID:jid xmppStream:stream];
if (resource)
{
result = [[resource caps] capabilities];
ext = [resource ext];
}
}];
if (extPtr)
*extPtr = ext;
return result;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark Protocol Private API
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (BOOL)setCapabilitiesNode:(NSString *)node
ver:(NSString *)ver
ext:(NSString *)ext
hash:(NSString *)hash
algorithm:(NSString *)hashAlg
forJID:(XMPPJID *)jid
xmppStream:(XMPPStream *)stream
andGetNewCapabilities:(NSXMLElement **)newCapabilitiesPtr
{
XMPPLogTrace();
__block BOOL result = NO;
__block NSXMLElement *newCapabilities = nil;
[self executeBlock:^{
BOOL hashChange = NO;
XMPPCapsResourceCoreDataStorageObject *resource = [self resourceForJID:jid xmppStream:stream];
if (resource)
{
resource.node = node;
resource.ver = ver;
resource.ext = ext;
if (![hash isEqual:[resource hashStr]])
{
hashChange = YES;
resource.hashStr = hash;
}
if (![hashAlg isEqual:[resource hashAlgorithm]])
{
hashChange = YES;
resource.hashAlgorithm = hashAlg;
}
}
else
{
resource = [NSEntityDescription insertNewObjectForEntityForName:@"XMPPCapsResourceCoreDataStorageObject"
inManagedObjectContext:[self managedObjectContext]];
resource.jidStr = [jid full];
resource.streamBareJidStr = [[self myJIDForXMPPStream:stream] bare];
resource.node = node;
resource.ver = ver;
resource.ext = ext;
resource.hashStr = hash;
resource.hashAlgorithm = hashAlg;
hashChange = ((hash != nil) || (hashAlg != nil));
}
if (hashChange)
{
resource.caps = [self capsForHash:hash algorithm:hashAlg];
newCapabilities = resource.caps.capabilities;
}
// Return whether or not the capabilities are known for the given jid
result = (resource.caps != nil);
}];
if (newCapabilitiesPtr)
*newCapabilitiesPtr = newCapabilities;
return result;
}
- (BOOL)getCapabilitiesHash:(NSString **)hashPtr
algorithm:(NSString **)hashAlgPtr
forJID:(XMPPJID *)jid
xmppStream:(XMPPStream *)stream
{
// By design this method should not be invoked from the storageQueue.
NSAssert(!dispatch_get_specific(storageQueueTag), @"Invoked on incorrect queue");
XMPPLogTrace();
__block BOOL result = NO;
__block NSString *hash = nil;
__block NSString *hashAlg = nil;
[self executeBlock:^{
XMPPCapsResourceCoreDataStorageObject *resource = [self resourceForJID:jid xmppStream:stream];
if (resource)
{
hash = resource.hashStr;
hashAlg = resource.hashAlgorithm;
result = (hash && hashAlg);
}
else
{
hash = nil;
hashAlg = nil;
result = NO;
}
}];
if (hashPtr)
*hashPtr = hash;
if (hashAlgPtr)
*hashAlgPtr = hashAlg;
return result;
}
- (void)clearCapabilitiesHashAndAlgorithmForJID:(XMPPJID *)jid xmppStream:(XMPPStream *)stream
{
XMPPLogTrace();
[self scheduleBlock:^{
XMPPCapsResourceCoreDataStorageObject *resource = [self resourceForJID:jid xmppStream:stream];
if (resource)
{
BOOL clearCaps = NO;
NSString *hash = resource.hashStr;
NSString *hashAlg = resource.hashAlgorithm;
if (hash && hashAlg)
{
clearCaps = YES;
}
resource.hashStr = nil;
resource.hashAlgorithm = nil;
if (clearCaps)
{
resource.caps = nil;
}
}
}];
}
- (void)getCapabilitiesKnown:(BOOL *)areCapabilitiesKnownPtr
failed:(BOOL *)haveFailedFetchingBeforePtr
node:(NSString **)nodePtr
ver:(NSString **)verPtr
ext:(NSString **)extPtr
hash:(NSString **)hashPtr
algorithm:(NSString **)hashAlgPtr
forJID:(XMPPJID *)jid
xmppStream:(XMPPStream *)stream
{
// By design this method should not be invoked from the storageQueue.
NSAssert(!dispatch_get_specific(storageQueueTag), @"Invoked on incorrect queue");
XMPPLogTrace();
__block BOOL areCapabilitiesKnown = NO;
__block BOOL haveFailedFetchingBefore = NO;
__block NSString *node = nil;
__block NSString *ver = nil;
__block NSString *ext = nil;
__block NSString *hash = nil;
__block NSString *hashAlg = nil;
[self executeBlock:^{
XMPPCapsResourceCoreDataStorageObject *resource = [self resourceForJID:jid xmppStream:stream];
if (resource == nil)
{
// We don't know anything about the given jid
areCapabilitiesKnown = NO;
haveFailedFetchingBefore = NO;
node = nil;
ver = nil;
ext = nil;
hash = nil;
hashAlg = nil;
}
else
{
areCapabilitiesKnown = (resource.caps != nil);
haveFailedFetchingBefore = resource.haveFailed;
node = resource.node;
ver = resource.ver;
ext = resource.ext;
hash = resource.hashStr;
hashAlg = resource.hashAlgorithm;
}
}];
if (areCapabilitiesKnownPtr) *areCapabilitiesKnownPtr = areCapabilitiesKnown;
if (haveFailedFetchingBeforePtr) *haveFailedFetchingBeforePtr = haveFailedFetchingBefore;
if (nodePtr) *nodePtr = node;
if (verPtr) *verPtr = ver;
if (extPtr) *extPtr = ext;
if (hashPtr) *hashPtr = hash;
if (hashAlgPtr) *hashAlgPtr = hashAlg;
}
- (void)setCapabilities:(NSXMLElement *)capabilities forHash:(NSString *)hash algorithm:(NSString *)hashAlg
{
XMPPLogTrace();
if (hash == nil) return;
if (hashAlg == nil) return;
[self scheduleBlock:^{
XMPPCapsCoreDataStorageObject *caps = [self capsForHash:hash algorithm:hashAlg];
if (caps)
{
caps.capabilities = capabilities;
}
else
{
caps = [NSEntityDescription insertNewObjectForEntityForName:@"XMPPCapsCoreDataStorageObject"
inManagedObjectContext:[self managedObjectContext]];
caps.hashStr = hash;
caps.hashAlgorithm = hashAlg;
caps.capabilities = capabilities;
}
NSEntityDescription *entity = [NSEntityDescription entityForName:@"XMPPCapsResourceCoreDataStorageObject"
inManagedObjectContext:[self managedObjectContext]];
NSPredicate *predicate;
predicate = [NSPredicate predicateWithFormat:@"hashStr == %@ AND hashAlgorithm == %@", hash, hashAlg];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
[fetchRequest setEntity:entity];
[fetchRequest setPredicate:predicate];
[fetchRequest setFetchBatchSize:saveThreshold];
NSArray *results = [[self managedObjectContext] executeFetchRequest:fetchRequest error:nil];
NSUInteger unsavedCount = [self numberOfUnsavedChanges];
for (XMPPCapsResourceCoreDataStorageObject *resource in results)
{
resource.caps = caps;
if (++unsavedCount >= saveThreshold)
{
[self save];
}
}
}];
}
- (void)setCapabilities:(NSXMLElement *)capabilities forJID:(XMPPJID *)jid xmppStream:(XMPPStream *)stream
{
// By design this method should not be invoked from the storageQueue.
NSAssert(!dispatch_get_specific(storageQueueTag), @"Invoked on incorrect queue");
XMPPLogTrace();
if (jid == nil) return;
[self scheduleBlock:^{
XMPPCapsCoreDataStorageObject *caps;
caps = [NSEntityDescription insertNewObjectForEntityForName:@"XMPPCapsCoreDataStorageObject"
inManagedObjectContext:[self managedObjectContext]];
caps.capabilities = capabilities;
XMPPCapsResourceCoreDataStorageObject *resource = [self resourceForJID:jid xmppStream:stream];
if (resource == nil)
{
resource = [NSEntityDescription insertNewObjectForEntityForName:@"XMPPCapsResourceCoreDataStorageObject"
inManagedObjectContext:[self managedObjectContext]];
resource.jidStr = [jid full];
resource.streamBareJidStr = [[self myJIDForXMPPStream:stream] bare];
}
resource.caps = caps;
}];
}
- (void)setCapabilitiesFetchFailedForJID:(XMPPJID *)jid xmppStream:(XMPPStream *)stream
{
XMPPLogTrace();
[self scheduleBlock:^{
XMPPCapsResourceCoreDataStorageObject *resource = [self resourceForJID:jid xmppStream:stream];
resource.haveFailed = YES;
}];
}
- (void)clearAllNonPersistentCapabilitiesForXMPPStream:(XMPPStream *)stream
{
// This method is called for the protocol,
// but is also called when we first load the database file from disk.
XMPPLogTrace();
[self scheduleBlock:^{
[self _clearAllNonPersistentCapabilitiesForXMPPStream:stream];
}];
}
- (void)clearNonPersistentCapabilitiesForJID:(XMPPJID *)jid xmppStream:(XMPPStream *)stream
{
XMPPLogTrace();
[self scheduleBlock:^{
XMPPCapsResourceCoreDataStorageObject *resource = [self resourceForJID:jid xmppStream:stream];
if (resource != nil)
{
NSString *hash = resource.hashStr;
NSString *hashAlg = resource.hashAlgorithm;
if (hash && hashAlg)
{
// The associated capabilities are persistent
}
else
{
XMPPCapsCoreDataStorageObject *caps = resource.caps;
if (caps)
{
[[self managedObjectContext] deleteObject:caps];
}
}
[[self managedObjectContext] deleteObject:resource];
}
}];
}
@end
@@ -0,0 +1,30 @@
#import <CoreData/CoreData.h>
#if TARGET_OS_IPHONE
#import "DDXML.h"
#endif
@class XMPPCapsResourceCoreDataStorageObject;
@interface XMPPCapsCoreDataStorageObject : NSManagedObject
@property (nonatomic, strong) NSXMLElement *capabilities;
@property (nonatomic, strong) NSString * hashStr;
@property (nonatomic, strong) NSString * hashAlgorithm;
@property (nonatomic, strong) NSString * capabilitiesStr;
@property (nonatomic, strong) NSSet * resources;
@end
@interface XMPPCapsCoreDataStorageObject (CoreDataGeneratedAccessors)
- (void)addResourcesObject:(XMPPCapsResourceCoreDataStorageObject *)value;
- (void)removeResourcesObject:(XMPPCapsResourceCoreDataStorageObject *)value;
- (void)addResources:(NSSet *)value;
- (void)removeResources:(NSSet *)value;
@end
@@ -0,0 +1,30 @@
#import "XMPPCapsCoreDataStorageObject.h"
#import "XMPPCapsResourceCoreDataStorageObject.h"
#import "XMPP.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 XMPPCapsCoreDataStorageObject
@dynamic capabilities;
@dynamic hashStr;
@dynamic hashAlgorithm;
@dynamic capabilitiesStr;
@dynamic resources;
- (NSXMLElement *)capabilities
{
return [[NSXMLElement alloc] initWithXMLString:[self capabilitiesStr] error:nil];
}
- (void)setCapabilities:(NSXMLElement *)caps
{
self.capabilitiesStr = [caps compactXMLString];
}
@end
@@ -0,0 +1,23 @@
#import <CoreData/CoreData.h>
@class XMPPCapsCoreDataStorageObject;
@interface XMPPCapsResourceCoreDataStorageObject : NSManagedObject
@property (nonatomic, strong) NSString * jidStr;
@property (nonatomic, strong) NSString * streamBareJidStr;
@property (nonatomic, assign) BOOL haveFailed;
@property (nonatomic, strong) NSNumber * failed;
@property (nonatomic, strong) NSString * node;
@property (nonatomic, strong) NSString * ver;
@property (nonatomic, strong) NSString * ext;
@property (nonatomic, strong) NSString * hashStr;
@property (nonatomic, strong) NSString * hashAlgorithm;
@property (nonatomic, strong) XMPPCapsCoreDataStorageObject * caps;
@end
@@ -0,0 +1,36 @@
#import "XMPPCapsResourceCoreDataStorageObject.h"
#import "XMPPCapsCoreDataStorageObject.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 XMPPCapsResourceCoreDataStorageObject
@dynamic jidStr;
@dynamic streamBareJidStr;
@dynamic haveFailed;
@dynamic failed;
@dynamic node;
@dynamic ver;
@dynamic ext;
@dynamic hashStr;
@dynamic hashAlgorithm;
@dynamic caps;
- (BOOL)haveFailed
{
return [[self failed] boolValue];
}
- (void)setHaveFailed:(BOOL)flag
{
self.failed = @(flag);
}
@end
+380
View File
@@ -0,0 +1,380 @@
#import <Foundation/Foundation.h>
#import "XMPP.h"
#define _XMPP_CAPABILITIES_H
@protocol XMPPCapabilitiesStorage;
/**
* This class provides support for capabilities discovery.
*
* It collects our capabilities and publishes them according to the XEP by:
* - Injecting the <c/> element into outgoing presence stanzas
* - Responding to incoming disco#info queries
*
* It also collects the capabilities of available resources,
* provides a mechanism to persistently store XEP-0115 hased caps,
* and makes available a simple API to query (disco#info) a resource or server.
**/
@interface XMPPCapabilities : XMPPModule
{
__strong id <XMPPCapabilitiesStorage> xmppCapabilitiesStorage;
NSString *myCapabilitiesNode;
NSXMLElement *myCapabilitiesQuery; // Full list of capabilites <query/>
NSXMLElement *myCapabilitiesC; // Hashed element <c/>
BOOL collectingMyCapabilities;
NSMutableSet *discoRequestJidSet;
NSMutableDictionary *discoRequestHashDict;
NSMutableDictionary *discoTimerJidDict;
BOOL autoFetchHashedCapabilities;
BOOL autoFetchNonHashedCapabilities;
BOOL autoFetchMyServerCapabilities;
NSTimeInterval capabilitiesRequestTimeout;
NSMutableSet *timers;
}
- (id)initWithCapabilitiesStorage:(id <XMPPCapabilitiesStorage>)storage;
- (id)initWithCapabilitiesStorage:(id <XMPPCapabilitiesStorage>)storage dispatchQueue:(dispatch_queue_t)queue;
@property (nonatomic, strong, readonly) id <XMPPCapabilitiesStorage> xmppCapabilitiesStorage;
/**
* Defines the node attribute in a <c/> element qualified by the 'http://jabber.org/protocol/caps' namespace.
*
* It is RECOMMENDED for the value of the 'node' attribute to be an HTTP URL
* at which a user could find further information about the software product,
* such as "http://github.com/robbiehanson/XMPPFramework"
*
* This MUST NOT be nil
*
* The default value is http://github.com/robbiehanson/XMPPFramework
**/
@property (nonatomic, copy) NSString *myCapabilitiesNode;
/**
* Defines fetching behavior for entities using the XEP-0115 standard.
*
* XEP-0115 defines a technique for hashing capabilities (disco info responses),
* and broadcasting them within a presence element.
* Due to the standardized hashing technique, capabilities associated with a hash may be persisted indefinitely.
*
* The end result is that capabilities need to be fetched less often
* since they are already known due to the caching of responses.
*
* The default value is YES.
**/
@property (assign) BOOL autoFetchHashedCapabilities;
/**
* Defines fetching behavior for entities NOT using the XEP-0115 standard.
*
* Because the capabilities are not associated with a standardized hash,
* it is not possible to cache the capabilities between sessions.
*
* The default value is NO.
*
* It is recommended you leave this value set to NO unless you
* know that you'll need the capabilities of every resource,
* and that fetching of the capabilities cannot be delayed.
*
* You may always fetch the capabilities (if/when needed) via the fetchCapabilitiesForJID method.
**/
@property (assign) BOOL autoFetchNonHashedCapabilities;
/**
* Auto fetch the capabilities of the server upon authentication.
* This uses the non hashed approach outlined in XEP-0030: Service Discovery.
*
* The default value is NO.
**/
@property (assign) BOOL autoFetchMyServerCapabilities;
/**
* Manually fetch the capabilities for the given jid.
*
* The jid must be a full jid (user@domain/resource) or a domain JID (domain without user or resource).
* You would pass a full jid if you wanted to know the capabilities of a particular user's resource.
* You would pass a domain jid if you wanted to know the capabilities of a particular server.
*
* If there is an existing disco request associated with the given jid, this method does nothing.
*
* When the capabilities are received,
* the xmppCapabilities:didDiscoverCapabilities:forJID: delegate method is invoked.
**/
- (void)fetchCapabilitiesForJID:(XMPPJID *)jid;
/**
* This module automatically collects my capabilities.
* See the xmppCapabilities:collectingMyCapabilities: delegate method.
*
* The design of XEP-115 is such that capabilites are expected to remain rather static.
* However, if the capabilities change, this method may be used to perform a manual update.
**/
- (void)recollectMyCapabilities;
@end
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark -
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@protocol XMPPCapabilitiesStorage <NSObject>
@required
//
//
// -- PUBLIC METHODS --
//
//
/**
* Returns whether or not we know the capabilities for a given jid.
*
* The stream parameter is optional.
* If given, the jid must have been registered via the given stream.
* Otherwise it will match the given jid from any stream this storage instance is managing.
**/
- (BOOL)areCapabilitiesKnownForJID:(XMPPJID *)jid xmppStream:(XMPPStream *)stream;
/**
* Returns the capabilities for the given jid.
* The returned element is the <query/> element response to a disco#info request.
*
* The stream parameter is optional.
* If given, the jid must have been registered via the given stream.
* Otherwise it will match the given jid from any stream this storage instance is managing.
**/
- (NSXMLElement *)capabilitiesForJID:(XMPPJID *)jid xmppStream:(XMPPStream *)stream;
/**
* Returns the capabilities for the given jid.
* The returned element is the <query/> element response to a disco#info request.
*
* The given jid should be a full jid (user@domain/resource) or a domin JID (domain without user or resource).
*
* If the jid has broadcast capabilities via the legacy format of XEP-0115,
* the extension list may optionally be retrieved via the ext parameter.
*
* For example, the jid may send a presence element like:
*
* <presence from="jid">
* <c node="imclient.com/caps" ver="1.2" ext="rdserver rdclient avcap"/>
* </presence>
*
* In the above example, the ext string would be set to "rdserver rdclient avcap".
*
* You may pass nil for extPtr if you don't care about the legacy attributes,
* or you could simply use the capabilitiesForJID: method above.
*
* The stream parameter is optional.
* If given, the jid must have been registered via the given stream.
* Otherwise it will match the given jid from any stream this storage instance is managing.
**/
- (NSXMLElement *)capabilitiesForJID:(XMPPJID *)jid ext:(NSString **)extPtr xmppStream:(XMPPStream *)stream;
//
//
// -- PRIVATE METHODS --
//
// These methods are designed to be used ONLY by the XMPPCapabilities class.
//
//
/**
* Configures the capabilities storage class, passing it's parent and parent's dispatch queue.
*
* This method is called by the init methods of the XMPPCapabilities class.
* This method is designed to inform the storage class of it's parent
* and of the dispatch queue the parent will be operating on.
*
* A storage class may choose to operate on the same queue as it's parent,
* as the majority of the time it will be getting called by the parent.
* If both are operating on the same queue, the combination may run faster.
*
* Some storage classes support multiple xmppStreams,
* and may choose to operate on their own internal queue.
*
* This method should return YES if it was configured properly.
* It should return NO only if configuration failed.
* For example, a storage class designed to be used only with a single xmppStream is being added to a second stream.
* The XMPPCapabilites class is configured to ignore the passed
* storage class in it's init method if this method returns NO.
**/
- (BOOL)configureWithParent:(XMPPCapabilities *)aParent queue:(dispatch_queue_t)queue;
/**
* Sets metadata for the given jid.
*
* This method should return:
* - YES if the capabilities for the given jid are known.
* - NO if the capabilities for the given jid are NOT known.
*
* If the hash and algorithm are given, and an associated set of capabilities matches the hash/algorithm,
* this method should link the jid to the capabilities and return YES.
*
* If the linked set of capabilities was not previously linked to the jid,
* the newCapabilities parameter shoud be filled out.
*
* This method may be called multiple times for a given jid with the same information.
* If this method sets the newCapabilitiesPtr parameter,
* the XMPPCapabilities module will invoke the xmppCapabilities:didDiscoverCapabilities:forJID: delegate method.
* This delegate method is designed to be invoked only when the capabilities for the given JID have changed.
* That is, the capabilities for the jid have been discovered for the first time (jid just signed in)
* or the capabilities for the given jid have changed (jid broadcast new capabilities).
**/
- (BOOL)setCapabilitiesNode:(NSString *)node
ver:(NSString *)ver
ext:(NSString *)ext
hash:(NSString *)hash
algorithm:(NSString *)hashAlg
forJID:(XMPPJID *)jid
xmppStream:(XMPPStream *)stream
andGetNewCapabilities:(NSXMLElement **)newCapabilitiesPtr;
/**
* Fetches the associated capabilities hash for a given jid.
*
* If the jid is not associated with a capabilities hash, this method should return NO.
* Otherwise it should return YES, and set the corresponding variables.
**/
- (BOOL)getCapabilitiesHash:(NSString **)hashPtr
algorithm:(NSString **)hashAlgPtr
forJID:(XMPPJID *)jid
xmppStream:(XMPPStream *)stream;
/**
* Clears any associated hash from a jid.
* If the jid is linked to a set of capabilities, it should be unlinked.
*
* This method should not clear the actual capabilities information itself.
* It should simply unlink the connection between the jid and the capabilities.
**/
- (void)clearCapabilitiesHashAndAlgorithmForJID:(XMPPJID *)jid xmppStream:(XMPPStream *)stream;
/**
* Gets the metadata for the given jid.
*
* If the capabilities are known, the areCapabilitiesKnown boolean should be set to YES.
**/
- (void)getCapabilitiesKnown:(BOOL *)areCapabilitiesKnownPtr
failed:(BOOL *)haveFailedFetchingBeforePtr
node:(NSString **)nodePtr
ver:(NSString **)verPtr
ext:(NSString **)extPtr
hash:(NSString **)hashPtr
algorithm:(NSString **)hashAlgPtr
forJID:(XMPPJID *)jid
xmppStream:(XMPPStream *)stream;
/**
* Sets the capabilities associated with a given hash.
*
* Since the capabilities are linked to a hash, these capabilities (and associated hash)
* should be persisted to disk and persisted between multiple sessions/streams.
*
* It is the responsibility of the storage implementation to link the
* associated jids (those with the given hash) to the given set of capabilities.
*
* Implementation Note:
*
* If we receive multiple simultaneous presence elements from
* multiple jids all broadcasting the same capabilities hash:
*
* - A single disco request will be sent to one of the jids.
* - When the response comes back, the setCapabilities:forHash:algorithm: method will be invoked.
*
* The setCapabilities:forJID: method will NOT be invoked for each corresponding jid.
* This is by design to allow the storage implementation to optimize itself.
**/
- (void)setCapabilities:(NSXMLElement *)caps forHash:(NSString *)hash algorithm:(NSString *)hashAlg;
/**
* Sets the capabilities for a given jid.
*
* The jid is guaranteed NOT to be associated with a capabilities hash.
*
* Since the capabilities are NOT linked to a hash,
* these capabilities should not be persisted between multiple sessions/streams.
* See the various clear methods below.
**/
- (void)setCapabilities:(NSXMLElement *)caps forJID:(XMPPJID *)jid xmppStream:(XMPPStream *)stream;
/**
* Marks the disco fetch request as failed so we know not to bother trying again.
*
* This is temporary metadata associated with the jid.
* It should be cleared when we go unavailable or offline, or if the given jid goes unavailable.
* See the various clear methods below.
**/
- (void)setCapabilitiesFetchFailedForJID:(XMPPJID *)jid xmppStream:(XMPPStream *)stream;
/**
* This method is called when we go unavailable or offline.
*
* This method should clear all metadata (node, ver, ext, hash, algorithm, failed) from all jids in the roster.
* All jids should be unlinked from associated capabilities.
*
* If the associated capabilities are persistent, they should not be cleared.
* That is, if the associated capabilities are associated with a hash, they should be persisted.
*
* Non persistent capabilities (those not associated with a hash)
* should be cleared at this point as they will no longer be linked to any users.
**/
- (void)clearAllNonPersistentCapabilitiesForXMPPStream:(XMPPStream *)stream;
/**
* This method is called when the given jid goes unavailable.
*
* This method should clear all metadata (node, ver, ext, hash ,algorithm, failed) from the given jid.
* The jid should be unlinked from associated capabilities.
*
* If the associated capabilities are persistent, they should not be cleared.
* That is, if the associated capabilities are associated with a hash, they should be persisted.
*
* Non persistent capabilities (those not associated with a hash) should be cleared.
**/
- (void)clearNonPersistentCapabilitiesForJID:(XMPPJID *)jid xmppStream:(XMPPStream *)stream;
@end
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark -
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@protocol XMPPCapabilitiesDelegate
@optional
/**
* Use this delegate method to add specific capabilities.
* This method in invoked automatically when the stream is connected for the first time,
* or if the module detects an outgoing presence element and my capabilities haven't been collected yet
*
* The design of XEP-115 is such that capabilites are expected to remain rather static.
* However, if the capabilities change, the recollectMyCapabilities method may be used to perform a manual update.
**/
- (void)xmppCapabilities:(XMPPCapabilities *)sender collectingMyCapabilities:(NSXMLElement *)query;
/**
* Use this delegate method to return the feature you want to have in your capabilities e.g. @[@"urn:xmpp:archive"]
* Duplicate features are automatically discarded
* For more control over your capablities use xmppCapabilities:collectingMyCapabilities:
**/
- (NSArray *)myFeaturesForXMPPCapabilities:(XMPPCapabilities *)sender;
/**
* Invoked when capabilities have been discovered for an available JID.
*
* The caps element is the <query/> element response to a disco#info request.
**/
- (void)xmppCapabilities:(XMPPCapabilities *)sender didDiscoverCapabilities:(NSXMLElement *)caps forJID:(XMPPJID *)jid;
@end
File diff suppressed because it is too large. Load diff