1002 lines
25 KiB
Objective-C
1002 lines
25 KiB
Objective-C
#import "XMPP.h"
|
|
#import "XMPPLogging.h"
|
|
#import "XMPPPrivacy.h"
|
|
#import "NSNumber+XMPP.h"
|
|
|
|
// Log levels: off, error, warn, info, verbose
|
|
// Log flags: trace
|
|
#ifdef DEBUG
|
|
static const int xmppLogLevel = XMPP_LOG_LEVEL_VERBOSE; // | XMPP_LOG_FLAG_TRACE;
|
|
#else
|
|
static const int xmppLogLevel = XMPP_LOG_LEVEL_WARN;
|
|
#endif
|
|
|
|
#define QUERY_TIMEOUT 30.0 // NSTimeInterval (double) = seconds
|
|
|
|
NSString *const XMPPPrivacyErrorDomain = @"XMPPPrivacyErrorDomain";
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
#pragma mark -
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
typedef enum XMPPPrivacyQueryInfoType {
|
|
FetchList,
|
|
FetchRules,
|
|
EditList,
|
|
SetActiveList,
|
|
SetDefaultList
|
|
|
|
} XMPPPrivacyQueryInfoType;
|
|
|
|
@interface XMPPPrivacyQueryInfo : NSObject
|
|
{
|
|
XMPPPrivacyQueryInfoType type;
|
|
NSString *privacyListName;
|
|
NSArray *privacyListItems;
|
|
|
|
dispatch_source_t timer;
|
|
}
|
|
|
|
@property (nonatomic, readonly) XMPPPrivacyQueryInfoType type;
|
|
@property (nonatomic, readonly) NSString *privacyListName;
|
|
@property (nonatomic, readonly) NSArray *privacyListItems;
|
|
|
|
@property (nonatomic, readwrite) dispatch_source_t timer;
|
|
|
|
- (void)cancel;
|
|
|
|
+ (XMPPPrivacyQueryInfo *)queryInfoWithType:(XMPPPrivacyQueryInfoType)type;
|
|
+ (XMPPPrivacyQueryInfo *)queryInfoWithType:(XMPPPrivacyQueryInfoType)type name:(NSString *)name;
|
|
+ (XMPPPrivacyQueryInfo *)queryInfoWithType:(XMPPPrivacyQueryInfoType)type name:(NSString *)name items:(NSArray *)items;
|
|
|
|
@end
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
#pragma mark -
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
@interface XMPPPrivacy (/* Must be nameless for properties */)
|
|
|
|
- (void)addQueryInfo:(XMPPPrivacyQueryInfo *)qi withKey:(NSString *)uuid;
|
|
- (void)queryTimeout:(NSString *)uuid;
|
|
|
|
@end
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
#pragma mark -
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
@implementation XMPPPrivacy
|
|
|
|
- (id)init
|
|
{
|
|
return [self initWithDispatchQueue:NULL];
|
|
}
|
|
|
|
- (id)initWithDispatchQueue:(dispatch_queue_t)queue
|
|
{
|
|
if ((self = [super initWithDispatchQueue:queue]))
|
|
{
|
|
autoRetrievePrivacyListNames = YES;
|
|
autoRetrievePrivacyListItems = YES;
|
|
autoClearPrivacyListInfo = YES;
|
|
|
|
privacyDict = [[NSMutableDictionary alloc] init];
|
|
activeListName = nil;
|
|
defaultListName = nil;
|
|
|
|
pendingQueries = [[NSMutableDictionary alloc] init];
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (BOOL)activate:(XMPPStream *)aXmppStream
|
|
{
|
|
if ([super activate:aXmppStream])
|
|
{
|
|
// Reserved for possible future use.
|
|
|
|
return YES;
|
|
}
|
|
|
|
return NO;
|
|
}
|
|
|
|
- (void)deactivate
|
|
{
|
|
// Reserved for possible future use.
|
|
|
|
[super deactivate];
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
#pragma mark Properties
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
- (BOOL)autoRetrievePrivacyListNames
|
|
{
|
|
if (dispatch_get_specific(moduleQueueTag))
|
|
{
|
|
return autoRetrievePrivacyListNames;
|
|
}
|
|
else
|
|
{
|
|
__block BOOL result;
|
|
|
|
dispatch_sync(moduleQueue, ^{
|
|
result = autoRetrievePrivacyListNames;
|
|
});
|
|
|
|
return result;
|
|
}
|
|
}
|
|
|
|
- (void)setAutoRetrievePrivacyListNames:(BOOL)flag
|
|
{
|
|
dispatch_block_t block = ^{
|
|
|
|
autoRetrievePrivacyListNames = flag;
|
|
};
|
|
|
|
if (dispatch_get_specific(moduleQueueTag))
|
|
block();
|
|
else
|
|
dispatch_async(moduleQueue, block);
|
|
}
|
|
|
|
- (BOOL)autoRetrievePrivacyListItems
|
|
{
|
|
if (dispatch_get_specific(moduleQueueTag))
|
|
{
|
|
return autoRetrievePrivacyListItems;
|
|
}
|
|
else
|
|
{
|
|
__block BOOL result;
|
|
|
|
dispatch_sync(moduleQueue, ^{
|
|
result = autoRetrievePrivacyListItems;
|
|
});
|
|
|
|
return result;
|
|
}
|
|
}
|
|
|
|
- (void)setAutoRetrievePrivacyListItems:(BOOL)flag
|
|
{
|
|
dispatch_block_t block = ^{
|
|
|
|
autoRetrievePrivacyListItems = flag;
|
|
};
|
|
|
|
if (dispatch_get_specific(moduleQueueTag))
|
|
block();
|
|
else
|
|
dispatch_async(moduleQueue, block);
|
|
}
|
|
|
|
- (BOOL)autoClearPrivacyListInfo
|
|
{
|
|
if (dispatch_get_specific(moduleQueueTag))
|
|
{
|
|
return autoClearPrivacyListInfo;
|
|
}
|
|
else
|
|
{
|
|
__block BOOL result;
|
|
|
|
dispatch_sync(moduleQueue, ^{
|
|
result = autoClearPrivacyListInfo;
|
|
});
|
|
|
|
return result;
|
|
}
|
|
}
|
|
|
|
- (void)setAutoClearPrivacyListInfo:(BOOL)flag
|
|
{
|
|
dispatch_block_t block = ^{
|
|
|
|
autoClearPrivacyListInfo = flag;
|
|
};
|
|
|
|
if (dispatch_get_specific(moduleQueueTag))
|
|
block();
|
|
else
|
|
dispatch_async(moduleQueue, block);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
#pragma mark Public API
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
- (void)retrieveListNames
|
|
{
|
|
XMPPLogTrace();
|
|
|
|
// <iq type='get' id='abc123'>
|
|
// <query xmlns='jabber:iq:privacy'/>
|
|
// </iq>
|
|
|
|
NSXMLElement *query = [NSXMLElement elementWithName:@"query" xmlns:@"jabber:iq:privacy"];
|
|
|
|
NSString *uuid = [xmppStream generateUUID];
|
|
XMPPIQ *iq = [XMPPIQ iqWithType:@"get" to:nil elementID:uuid child:query];
|
|
|
|
[xmppStream sendElement:iq];
|
|
|
|
XMPPPrivacyQueryInfo *qi = [XMPPPrivacyQueryInfo queryInfoWithType:FetchList];
|
|
[self addQueryInfo:qi withKey:uuid];
|
|
}
|
|
|
|
- (void)retrieveListWithName:(NSString *)privacyListName
|
|
{
|
|
XMPPLogTrace();
|
|
|
|
if (privacyListName == nil) return;
|
|
|
|
// <iq type='get' id='abc123'>
|
|
// <query xmlns='jabber:iq:privacy'>
|
|
// <list name='public'/>
|
|
// </query>
|
|
// </iq>
|
|
|
|
NSXMLElement *list = [NSXMLElement elementWithName:@"list"];
|
|
[list addAttributeWithName:@"name" stringValue:privacyListName];
|
|
|
|
NSXMLElement *query = [NSXMLElement elementWithName:@"query" xmlns:@"jabber:iq:privacy"];
|
|
[query addChild:list];
|
|
|
|
NSString *uuid = [xmppStream generateUUID];
|
|
XMPPIQ *iq = [XMPPIQ iqWithType:@"get" to:nil elementID:uuid child:query];
|
|
|
|
[xmppStream sendElement:iq];
|
|
|
|
XMPPPrivacyQueryInfo *qi = [XMPPPrivacyQueryInfo queryInfoWithType:FetchRules name:privacyListName];
|
|
[self addQueryInfo:qi withKey:uuid];
|
|
}
|
|
|
|
- (void)clearPrivacyListInfo
|
|
{
|
|
XMPPLogTrace();
|
|
|
|
if (dispatch_get_specific(moduleQueueTag))
|
|
{
|
|
[privacyDict removeAllObjects];
|
|
}
|
|
else
|
|
{
|
|
dispatch_async(moduleQueue, ^{ @autoreleasepool {
|
|
|
|
[privacyDict removeAllObjects];
|
|
}});
|
|
}
|
|
}
|
|
|
|
- (NSArray *)listNames
|
|
{
|
|
if (dispatch_get_specific(moduleQueueTag))
|
|
{
|
|
return [privacyDict allKeys];
|
|
}
|
|
else
|
|
{
|
|
__block NSArray *result;
|
|
|
|
dispatch_sync(moduleQueue, ^{ @autoreleasepool {
|
|
|
|
result = [[privacyDict allKeys] copy];
|
|
}});
|
|
|
|
return result;
|
|
}
|
|
}
|
|
|
|
- (NSArray *)listWithName:(NSString *)privacyListName
|
|
{
|
|
NSArray* (^block)() = ^ NSArray* () {
|
|
|
|
id result = privacyDict[privacyListName];
|
|
|
|
if (result == [NSNull null]) // Not fetched yet
|
|
return nil;
|
|
else
|
|
return (NSArray *)result;
|
|
};
|
|
|
|
// ExecuteVoidBlock(moduleQueue, block);
|
|
// ExecuteNonVoidBlock(moduleQueue, block, NSArray*)
|
|
|
|
if (dispatch_get_specific(moduleQueueTag))
|
|
{
|
|
return block();
|
|
}
|
|
else
|
|
{
|
|
__block NSArray *result;
|
|
|
|
dispatch_sync(moduleQueue, ^{ @autoreleasepool {
|
|
|
|
result = block();
|
|
}});
|
|
|
|
return result;
|
|
}
|
|
}
|
|
|
|
- (NSString *)activeListName
|
|
{
|
|
return activeListName;
|
|
}
|
|
|
|
- (NSArray *)activeList
|
|
{
|
|
return [self listWithName:activeListName];
|
|
}
|
|
|
|
- (void)setActiveListName:(NSString *)privacyListName
|
|
{
|
|
// Setting active list:
|
|
//
|
|
// <iq type='set' id='active1'>
|
|
// <query xmlns='jabber:iq:privacy'>
|
|
// <active name='special'/>
|
|
// </query>
|
|
// </iq>
|
|
//
|
|
// Decline the use of active lists:
|
|
//
|
|
// <iq type='set' id='active3'>
|
|
// <query xmlns='jabber:iq:privacy'>
|
|
// <active/>
|
|
// </query>
|
|
// </iq>
|
|
|
|
NSXMLElement *active = [NSXMLElement elementWithName:@"active"];
|
|
if (privacyListName)
|
|
{
|
|
[active addAttributeWithName:@"name" stringValue:privacyListName];
|
|
}
|
|
|
|
NSXMLElement *query = [NSXMLElement elementWithName:@"query" xmlns:@"jabber:iq:privacy"];
|
|
[query addChild:active];
|
|
|
|
NSString *uuid = [xmppStream generateUUID];
|
|
XMPPIQ *iq = [XMPPIQ iqWithType:@"set" to:nil elementID:uuid child:query];
|
|
|
|
[xmppStream sendElement:iq];
|
|
|
|
XMPPPrivacyQueryInfo *qi = [XMPPPrivacyQueryInfo queryInfoWithType:SetActiveList name:privacyListName];
|
|
[self addQueryInfo:qi withKey:uuid];
|
|
}
|
|
|
|
- (NSString *)defaultListName
|
|
{
|
|
return defaultListName;
|
|
}
|
|
|
|
- (NSArray *)defaultList
|
|
{
|
|
return [self listWithName:defaultListName];
|
|
}
|
|
|
|
- (void)setDefaultListName:(NSString *)privacyListName
|
|
{
|
|
// Setting default list:
|
|
//
|
|
// <iq type='set' id='default1'>
|
|
// <query xmlns='jabber:iq:privacy'>
|
|
// <default name='special'/>
|
|
// </query>
|
|
// </iq>
|
|
//
|
|
// Decline the use of default list:
|
|
//
|
|
// <iq type='set' id='default2'>
|
|
// <query xmlns='jabber:iq:privacy'>
|
|
// <default/>
|
|
// </query>
|
|
// </iq>
|
|
|
|
NSXMLElement *dfault = [NSXMLElement elementWithName:@"default"];
|
|
if (privacyListName)
|
|
{
|
|
[dfault addAttributeWithName:@"name" stringValue:privacyListName];
|
|
}
|
|
|
|
NSXMLElement *query = [NSXMLElement elementWithName:@"query" xmlns:@"jabber:iq:privacy"];
|
|
[query addChild:dfault];
|
|
|
|
NSString *uuid = [xmppStream generateUUID];
|
|
XMPPIQ *iq = [XMPPIQ iqWithType:@"set" to:nil elementID:uuid child:query];
|
|
|
|
[xmppStream sendElement:iq];
|
|
|
|
XMPPPrivacyQueryInfo *qi = [XMPPPrivacyQueryInfo queryInfoWithType:SetDefaultList name:privacyListName];
|
|
[self addQueryInfo:qi withKey:uuid];
|
|
}
|
|
|
|
- (void)setListWithName:(NSString *)privacyListName items:(NSArray *)items
|
|
{
|
|
// Edit a privacy list:
|
|
//
|
|
// <iq type='set' id='edit1'>
|
|
// <query xmlns='jabber:iq:privacy'>
|
|
// <list name='public'>
|
|
// <item type='jid' value='tybalt@example.com' action='deny' order='3'/>
|
|
// <item type='jid' value='paris@example.org' action='deny' order='5'/>
|
|
// <item action='allow' order='68'/>
|
|
// </list>
|
|
// </query>
|
|
// </iq>
|
|
//
|
|
//
|
|
// Remove a privacy list:
|
|
//
|
|
// <iq type='set' id='remove1'>
|
|
// <query xmlns='jabber:iq:privacy'>
|
|
// <list name='private'/>
|
|
// </query>
|
|
// </iq>
|
|
|
|
NSXMLElement *list = [NSXMLElement elementWithName:@"list"];
|
|
[list addAttributeWithName:@"name" stringValue:privacyListName];
|
|
|
|
if (items && ([items count] > 0))
|
|
{
|
|
[list setChildren:items];
|
|
}
|
|
|
|
NSXMLElement *query = [NSXMLElement elementWithName:@"query" xmlns:@"jabber:iq:privacy"];
|
|
[query addChild:list];
|
|
|
|
NSString *uuid = [xmppStream generateUUID];
|
|
XMPPIQ *iq = [XMPPIQ iqWithType:@"set" to:nil elementID:uuid child:query];
|
|
|
|
[xmppStream sendElement:iq];
|
|
|
|
XMPPPrivacyQueryInfo *qi = [XMPPPrivacyQueryInfo queryInfoWithType:EditList name:privacyListName items:items];
|
|
[self addQueryInfo:qi withKey:uuid];
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
#pragma mark Query Processing
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
- (void)addQueryInfo:(XMPPPrivacyQueryInfo *)queryInfo withKey:(NSString *)uuid
|
|
{
|
|
// Setup timer
|
|
|
|
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, moduleQueue);
|
|
|
|
dispatch_source_set_event_handler(timer, ^{ @autoreleasepool {
|
|
|
|
[self queryTimeout:uuid];
|
|
}});
|
|
|
|
dispatch_time_t fireTime = dispatch_time(DISPATCH_TIME_NOW, (QUERY_TIMEOUT * NSEC_PER_SEC));
|
|
|
|
dispatch_source_set_timer(timer, fireTime, DISPATCH_TIME_FOREVER, 1.0);
|
|
dispatch_resume(timer);
|
|
|
|
queryInfo.timer = timer;
|
|
|
|
// Add to dictionary
|
|
pendingQueries[uuid] = queryInfo;
|
|
}
|
|
|
|
- (void)removeQueryInfo:(XMPPPrivacyQueryInfo *)queryInfo withKey:(NSString *)uuid
|
|
{
|
|
// Invalidate timer
|
|
[queryInfo cancel];
|
|
|
|
// Remove from dictionary
|
|
[pendingQueries removeObjectForKey:uuid];
|
|
}
|
|
|
|
- (void)processQuery:(XMPPPrivacyQueryInfo *)queryInfo withFailureCode:(XMPPPrivacyErrorCode)errorCode
|
|
{
|
|
NSError *error = [NSError errorWithDomain:XMPPPrivacyErrorDomain code:errorCode userInfo:nil];
|
|
|
|
if (queryInfo.type == FetchList)
|
|
{
|
|
[multicastDelegate xmppPrivacy:self didNotReceiveListNamesDueToError:error];
|
|
}
|
|
else if (queryInfo.type == FetchRules)
|
|
{
|
|
[multicastDelegate xmppPrivacy:self didNotReceiveListWithName:queryInfo.privacyListName error:error];
|
|
}
|
|
else if (queryInfo.type == EditList)
|
|
{
|
|
[multicastDelegate xmppPrivacy:self didNotSetListWithName:queryInfo.privacyListName error:error];
|
|
}
|
|
else if (queryInfo.type == SetActiveList)
|
|
{
|
|
[multicastDelegate xmppPrivacy:self didNotSetActiveListName:queryInfo.privacyListName error:error];
|
|
}
|
|
else if (queryInfo.type == SetDefaultList)
|
|
{
|
|
[multicastDelegate xmppPrivacy:self didNotSetDefaultListName:queryInfo.privacyListName error:error];
|
|
}
|
|
}
|
|
|
|
- (void)queryTimeout:(NSString *)uuid
|
|
{
|
|
XMPPPrivacyQueryInfo *queryInfo = privacyDict[uuid];
|
|
if (queryInfo)
|
|
{
|
|
[self processQuery:queryInfo withFailureCode:XMPPPrivacyQueryTimeout];
|
|
[self removeQueryInfo:queryInfo withKey:uuid];
|
|
}
|
|
}
|
|
|
|
NSInteger sortItems(id itemOne, id itemTwo, void *context)
|
|
{
|
|
NSXMLElement *item1 = (NSXMLElement *)itemOne;
|
|
NSXMLElement *item2 = (NSXMLElement *)itemTwo;
|
|
|
|
NSString *orderStr1 = [item1 attributeStringValueForName:@"order"];
|
|
NSString *orderStr2 = [item2 attributeStringValueForName:@"order"];
|
|
|
|
NSUInteger order1;
|
|
BOOL parse1 = [NSNumber xmpp_parseString:orderStr1 intoNSUInteger:&order1];
|
|
|
|
NSUInteger order2;
|
|
BOOL parse2 = [NSNumber xmpp_parseString:orderStr2 intoNSUInteger:&order2];
|
|
|
|
if (parse1)
|
|
{
|
|
if (parse2)
|
|
{
|
|
// item1 = valid
|
|
// item2 = valid
|
|
|
|
if (order1 < order2)
|
|
return NSOrderedAscending;
|
|
if (order1 > order2)
|
|
return NSOrderedDescending;
|
|
|
|
return NSOrderedSame;
|
|
}
|
|
else
|
|
{
|
|
// item1 = valid
|
|
// item2 = invalid
|
|
|
|
return NSOrderedAscending;
|
|
}
|
|
}
|
|
else if (parse2)
|
|
{
|
|
// item1 = invalid
|
|
// item2 = valid
|
|
|
|
return NSOrderedDescending;
|
|
}
|
|
else
|
|
{
|
|
// item1 = invalid
|
|
// item2 = invalid
|
|
|
|
return NSOrderedSame;
|
|
}
|
|
}
|
|
|
|
- (void)processQueryResponse:(XMPPIQ *)iq withInfo:(XMPPPrivacyQueryInfo *)queryInfo
|
|
{
|
|
if (queryInfo.type == FetchList)
|
|
{
|
|
// Privacy List Names Query Response:
|
|
//
|
|
// <iq type='result' id='getlist1'>
|
|
// <query xmlns='jabber:iq:privacy'>
|
|
// <active name='private'/>
|
|
// <default name='public'/>
|
|
// <list name='public'/>
|
|
// <list name='private'/>
|
|
// <list name='special'/>
|
|
// </query>
|
|
// </iq>
|
|
|
|
if ([[iq type] isEqualToString:@"result"])
|
|
{
|
|
NSXMLElement *query = [iq elementForName:@"query" xmlns:@"jabber:iq:privacy"];
|
|
if (query == nil) return;
|
|
|
|
NSXMLElement *active = [query elementForName:@"active"];
|
|
activeListName = [[active attributeStringValueForName:@"name"] copy];
|
|
|
|
NSXMLElement *dfault = [query elementForName:@"default"];
|
|
defaultListName = [[dfault attributeStringValueForName:@"name"] copy];
|
|
|
|
NSArray *listNames = [query elementsForName:@"list"];
|
|
for (NSXMLElement *listName in listNames)
|
|
{
|
|
NSString *name = [listName attributeStringValueForName:@"name"];
|
|
if (name)
|
|
{
|
|
id value = privacyDict[name];
|
|
if (value == nil)
|
|
{
|
|
privacyDict[name] = [NSNull null];
|
|
}
|
|
}
|
|
}
|
|
|
|
[multicastDelegate xmppPrivacy:self didReceiveListNames:[self listNames]];
|
|
}
|
|
else
|
|
{
|
|
[multicastDelegate xmppPrivacy:self didNotReceiveListNamesDueToError:iq];
|
|
}
|
|
}
|
|
else if (queryInfo.type == FetchRules)
|
|
{
|
|
// Privacy List Rules Query Response (success):
|
|
//
|
|
// <iq type='result' id='getlist2'>
|
|
// <query xmlns='jabber:iq:privacy'>
|
|
// <list name='public'>
|
|
// <item type='jid' value='tybalt@example.com' action='deny' order='1'/>
|
|
// <item action='allow' order='2'/>
|
|
// </list>
|
|
// </query>
|
|
// </iq>
|
|
//
|
|
//
|
|
// Privacy List Rules Query Response (error):
|
|
//
|
|
// <iq type='error' id='getlist5'>
|
|
// <query xmlns='jabber:iq:privacy'>
|
|
// <list name='The Empty Set'/>
|
|
// </query>
|
|
// <error type='cancel'>
|
|
// <item-not-found xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
|
|
// </error>
|
|
// </iq>
|
|
|
|
if ([[iq type] isEqualToString:@"result"])
|
|
{
|
|
NSXMLElement *query = [iq elementForName:@"query" xmlns:@"jabber:iq:privacy"];
|
|
if (query == nil) return;
|
|
|
|
NSXMLElement *list = [query elementForName:@"list"];
|
|
if (list == nil) return;
|
|
|
|
NSArray *items = [[list elementsForName:@"item"] sortedArrayUsingFunction:sortItems context:NULL];
|
|
|
|
if (items == nil)
|
|
{
|
|
items = [NSArray array];
|
|
}
|
|
|
|
privacyDict[queryInfo.privacyListName] = items;
|
|
|
|
[multicastDelegate xmppPrivacy:self didReceiveListWithName:queryInfo.privacyListName items:items];
|
|
}
|
|
else
|
|
{
|
|
[multicastDelegate xmppPrivacy:self didNotReceiveListWithName:queryInfo.privacyListName error:iq];
|
|
}
|
|
}
|
|
else if (queryInfo.type == EditList)
|
|
{
|
|
// Privacy List Add/Edit/Remove Response (success):
|
|
//
|
|
// <iq type='result' id='abc123' to='romeo@example.net/orchard'/>
|
|
//
|
|
// Note: The result iq does NOT have a query child.
|
|
|
|
if ([[iq type] isEqualToString:@"result"])
|
|
{
|
|
NSArray *items = [[queryInfo privacyListItems] sortedArrayUsingFunction:sortItems context:NULL];
|
|
|
|
if (items == nil)
|
|
{
|
|
items = [NSArray array];
|
|
}
|
|
|
|
privacyDict[queryInfo.privacyListName] = items;
|
|
|
|
[multicastDelegate xmppPrivacy:self didSetListWithName:queryInfo.privacyListName];
|
|
}
|
|
else
|
|
{
|
|
[multicastDelegate xmppPrivacy:self didNotSetListWithName:queryInfo.privacyListName error:iq];
|
|
}
|
|
}
|
|
else if (queryInfo.type == SetActiveList)
|
|
{
|
|
// Change of active list (success):
|
|
//
|
|
// <iq type='result' id='active1' to='romeo@example.net/orchard'/>
|
|
//
|
|
//
|
|
// Change of active list (error):
|
|
//
|
|
// <iq to='romeo@example.net/orchard' type='error' id='active2'>
|
|
// <query xmlns='jabber:iq:privacy'>
|
|
// <active name='Invalid List Name'/>
|
|
// </query>
|
|
// <error type='cancel'>
|
|
// <item-not-found xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
|
|
// </error>
|
|
// </iq>
|
|
//
|
|
//
|
|
// Note: The result iq does NOT have a query child.
|
|
// Note: The success could also mean we declined the use of an active list.
|
|
|
|
if ([[iq type] isEqualToString:@"result"])
|
|
{
|
|
activeListName = [[queryInfo privacyListName] copy];
|
|
|
|
[multicastDelegate xmppPrivacy:self didSetActiveListName:queryInfo.privacyListName];
|
|
}
|
|
else
|
|
{
|
|
[multicastDelegate xmppPrivacy:self didNotSetActiveListName:queryInfo.privacyListName error:iq];
|
|
}
|
|
}
|
|
else if (queryInfo.type == SetDefaultList)
|
|
{
|
|
// Change of default list (success):
|
|
//
|
|
// <iq type='result' id='default1' to='romeo@example.net/orchard'/>
|
|
//
|
|
//
|
|
// Change of default list (error):
|
|
//
|
|
// <iq to='romeo@example.net/orchard' type='error' id='default1'>
|
|
// <query xmlns='jabber:iq:privacy'>
|
|
// <default name='special'/>
|
|
// </query>
|
|
// <error type='cancel'>
|
|
// <conflict xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
|
|
// </error>
|
|
// </iq>
|
|
//
|
|
//
|
|
// Note: The result iq does NOT have a query child.
|
|
// Note: The success could also mean we declined the use of a default list.
|
|
|
|
if ([[iq type] isEqualToString:@"result"])
|
|
{
|
|
defaultListName = [[queryInfo privacyListName] copy];
|
|
|
|
[multicastDelegate xmppPrivacy:self didSetDefaultListName:queryInfo.privacyListName];
|
|
}
|
|
else
|
|
{
|
|
[multicastDelegate xmppPrivacy:self didNotSetDefaultListName:queryInfo.privacyListName error:iq];
|
|
}
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
#pragma mark XMPPStream Delegate
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
- (void)xmppStreamDidAuthenticate:(XMPPStream *)sender
|
|
{
|
|
if (self.autoRetrievePrivacyListNames)
|
|
{
|
|
[self retrieveListNames];
|
|
}
|
|
}
|
|
|
|
- (BOOL)xmppStream:(XMPPStream *)sender didReceiveIQ:(XMPPIQ *)iq
|
|
{
|
|
NSString *type = [iq type];
|
|
|
|
if ([type isEqualToString:@"set"])
|
|
{
|
|
NSXMLElement *query = [iq elementForName:@"query" xmlns:@"jabber:iq:privacy"];
|
|
if (query)
|
|
{
|
|
// Privacy List Push:
|
|
//
|
|
// <iq type='set' id='push1'>
|
|
// <query xmlns='jabber:iq:privacy'>
|
|
// <list name='public'/>
|
|
// </query>
|
|
// </iq>
|
|
//
|
|
//
|
|
// Push response:
|
|
//
|
|
// <iq type='result' id='push1'/>
|
|
|
|
NSXMLElement *list = [query elementForName:@"list"];
|
|
|
|
NSString *listName = [list attributeStringValueForName:@"name"];
|
|
if (listName == nil)
|
|
{
|
|
return NO;
|
|
}
|
|
|
|
[multicastDelegate xmppPrivacy:self didReceivePushWithListName:listName];
|
|
|
|
XMPPIQ *iqResponse = [XMPPIQ iqWithType:@"result" to:[iq from] elementID:[iq elementID]];
|
|
[xmppStream sendElement:iqResponse];
|
|
|
|
if (self.autoRetrievePrivacyListItems)
|
|
{
|
|
[self retrieveListWithName:listName];
|
|
}
|
|
|
|
return YES;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// This may be a response to a query we sent
|
|
|
|
XMPPPrivacyQueryInfo *queryInfo = pendingQueries[[iq elementID]];
|
|
if (queryInfo)
|
|
{
|
|
[self processQueryResponse:iq withInfo:queryInfo];
|
|
|
|
if (queryInfo.type == FetchList && self.autoRetrievePrivacyListItems)
|
|
{
|
|
for (NSString *privacyListName in privacyDict)
|
|
{
|
|
id privacyListItems = privacyDict[privacyListName];
|
|
|
|
if (privacyListItems == [NSNull null])
|
|
{
|
|
[self retrieveListWithName:privacyListName];
|
|
}
|
|
}
|
|
}
|
|
|
|
return YES;
|
|
}
|
|
}
|
|
|
|
return NO;
|
|
}
|
|
|
|
-(void)xmppStreamDidDisconnect:(XMPPStream *)sender withError:(NSError *)error
|
|
{
|
|
// If there are any pending queries,
|
|
// they just failed due to the disconnection.
|
|
|
|
for (NSString *uuid in pendingQueries)
|
|
{
|
|
XMPPPrivacyQueryInfo *queryInfo = privacyDict[uuid];
|
|
|
|
[self processQuery:queryInfo withFailureCode:XMPPPrivacyDisconnect];
|
|
}
|
|
|
|
// Clear the list of pending queries
|
|
|
|
[pendingQueries removeAllObjects];
|
|
|
|
// Maybe clear all stored privacy info
|
|
|
|
if (self.autoClearPrivacyListInfo)
|
|
{
|
|
[self clearPrivacyListInfo];
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
#pragma mark Privacy Items
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
+ (NSXMLElement *)privacyItemWithAction:(NSString *)action order:(NSUInteger)orderValue
|
|
{
|
|
return [self privacyItemWithType:nil value:nil action:action order:orderValue];
|
|
}
|
|
|
|
+ (NSXMLElement *)privacyItemWithType:(NSString *)type
|
|
value:(NSString *)value
|
|
action:(NSString *)action
|
|
order:(NSUInteger)orderValue
|
|
{
|
|
NSString *order = [[NSString alloc] initWithFormat:@"%lu", (unsigned long)orderValue];
|
|
|
|
// <item type='[jid|group|subscription]'
|
|
// value='bar'
|
|
// action='[allow|deny]'
|
|
// order='unsignedInt'>
|
|
// [<iq/>]
|
|
// [<message/>]
|
|
// [<presence-in/>]
|
|
// [<presence-out/>]
|
|
// </item>
|
|
|
|
NSXMLElement *item = [NSXMLElement elementWithName:@"item"];
|
|
|
|
if (type)
|
|
[item addAttributeWithName:@"type" stringValue:type];
|
|
|
|
if (value)
|
|
[item addAttributeWithName:@"value" stringValue:value];
|
|
|
|
[item addAttributeWithName:@"action" stringValue:action];
|
|
[item addAttributeWithName:@"order" stringValue:order];
|
|
|
|
return item;
|
|
}
|
|
|
|
+ (void)blockIQs:(NSXMLElement *)privacyItem
|
|
{
|
|
[privacyItem addChild:[NSXMLElement elementWithName:@"iq"]];
|
|
}
|
|
|
|
+ (void)blockMessages:(NSXMLElement *)privacyItem
|
|
{
|
|
[privacyItem addChild:[NSXMLElement elementWithName:@"message"]];
|
|
}
|
|
|
|
+ (void)blockPresenceIn:(NSXMLElement *)privacyItem
|
|
{
|
|
[privacyItem addChild:[NSXMLElement elementWithName:@"presence-in"]];
|
|
}
|
|
|
|
+ (void)blockPresenceOut:(NSXMLElement *)privacyItem
|
|
{
|
|
[privacyItem addChild:[NSXMLElement elementWithName:@"presence-out"]];
|
|
}
|
|
|
|
@end
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
#pragma mark -
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
@implementation XMPPPrivacyQueryInfo
|
|
|
|
@synthesize type;
|
|
@synthesize privacyListName;
|
|
@synthesize privacyListItems;
|
|
@synthesize timer;
|
|
|
|
|
|
- (id)initWithType:(XMPPPrivacyQueryInfoType)aType name:(NSString *)name items:(NSArray *)items
|
|
{
|
|
if ((self = [super init]))
|
|
{
|
|
type = aType;
|
|
privacyListName = [name copy];
|
|
privacyListItems = [items copy];
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (void)cancel
|
|
{
|
|
if (timer)
|
|
{
|
|
dispatch_source_cancel(timer);
|
|
#if !OS_OBJECT_USE_OBJC
|
|
dispatch_release(timer);
|
|
#endif
|
|
timer = NULL;
|
|
}
|
|
}
|
|
|
|
- (void)dealloc
|
|
{
|
|
[self cancel];
|
|
}
|
|
|
|
+ (XMPPPrivacyQueryInfo *)queryInfoWithType:(XMPPPrivacyQueryInfoType)type
|
|
{
|
|
return [self queryInfoWithType:type name:nil items:nil];
|
|
}
|
|
|
|
+ (XMPPPrivacyQueryInfo *)queryInfoWithType:(XMPPPrivacyQueryInfoType)type name:(NSString *)name
|
|
{
|
|
return [self queryInfoWithType:type name:name items:nil];
|
|
}
|
|
|
|
+ (XMPPPrivacyQueryInfo *)queryInfoWithType:(XMPPPrivacyQueryInfoType)type name:(NSString *)name items:(NSArray *)items
|
|
{
|
|
return [[XMPPPrivacyQueryInfo alloc] initWithType:type name:name items:items];
|
|
}
|
|
|
|
@end
|