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