- Update
This commit is contained in:
1 parent
d281d765ea
commit
5b074a5176
1261 files changed
+199158
-7303
No files matched your search
Binary file not shown.
Binary file not shown.
@@ -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
|
||||
@@ -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
Reference in new issue
Block a user