- Update
This commit is contained in:
parent
d281d765ea
commit
5b074a5176
45
Authentication/Anonymous/XMPPAnonymousAuthentication.h
Normal file
45
Authentication/Anonymous/XMPPAnonymousAuthentication.h
Normal file
@ -0,0 +1,45 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "XMPPSASLAuthentication.h"
|
||||
#import "XMPP.h"
|
||||
|
||||
|
||||
@interface XMPPAnonymousAuthentication : NSObject <XMPPSASLAuthentication>
|
||||
|
||||
- (id)initWithStream:(XMPPStream *)stream;
|
||||
|
||||
// This class implements the XMPPSASLAuthentication protocol.
|
||||
//
|
||||
// See XMPPSASLAuthentication.h for more information.
|
||||
|
||||
@end
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark -
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@interface XMPPStream (XMPPAnonymousAuthentication)
|
||||
|
||||
/**
|
||||
* Returns whether or not the server support anonymous authentication.
|
||||
*
|
||||
* This information is available after the stream is connected.
|
||||
* In other words, after the delegate has received xmppStreamDidConnect: notification.
|
||||
**/
|
||||
- (BOOL)supportsAnonymousAuthentication;
|
||||
|
||||
/**
|
||||
* This method attempts to start the anonymous authentication process.
|
||||
*
|
||||
* This method is asynchronous.
|
||||
*
|
||||
* If there is something immediately wrong,
|
||||
* such as the stream is not connected or doesn't support anonymous authentication,
|
||||
* the method will return NO and set the error.
|
||||
* Otherwise the delegate callbacks are used to communicate auth success or failure.
|
||||
*
|
||||
* @see xmppStreamDidAuthenticate:
|
||||
* @see xmppStream:didNotAuthenticate:
|
||||
**/
|
||||
- (BOOL)authenticateAnonymously:(NSError **)errPtr;
|
||||
|
||||
@end
|
||||
131
Authentication/Anonymous/XMPPAnonymousAuthentication.m
Normal file
131
Authentication/Anonymous/XMPPAnonymousAuthentication.m
Normal file
@ -0,0 +1,131 @@
|
||||
#import "XMPPAnonymousAuthentication.h"
|
||||
#import "XMPP.h"
|
||||
#import "XMPPLogging.h"
|
||||
#import "XMPPInternal.h"
|
||||
#import "NSXMLElement+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
|
||||
|
||||
// Log levels: off, error, warn, info, verbose
|
||||
#if DEBUG
|
||||
static const int xmppLogLevel = XMPP_LOG_LEVEL_INFO; // | XMPP_LOG_FLAG_TRACE;
|
||||
#else
|
||||
static const int xmppLogLevel = XMPP_LOG_LEVEL_WARN;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Seeing a return statements within an inner block
|
||||
* can sometimes be mistaken for a return point of the enclosing method.
|
||||
* This makes inline blocks a bit easier to read.
|
||||
**/
|
||||
#define return_from_block return
|
||||
|
||||
|
||||
@implementation XMPPAnonymousAuthentication
|
||||
{
|
||||
#if __has_feature(objc_arc_weak)
|
||||
__weak XMPPStream *xmppStream;
|
||||
#else
|
||||
__unsafe_unretained XMPPStream *xmppStream;
|
||||
#endif
|
||||
}
|
||||
|
||||
+ (NSString *)mechanismName
|
||||
{
|
||||
return @"ANONYMOUS";
|
||||
}
|
||||
|
||||
- (id)initWithStream:(XMPPStream *)stream
|
||||
{
|
||||
if ((self = [super init]))
|
||||
{
|
||||
xmppStream = stream;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)initWithStream:(XMPPStream *)stream password:(NSString *)password
|
||||
{
|
||||
return [self initWithStream:stream];
|
||||
}
|
||||
|
||||
- (BOOL)start:(NSError **)errPtr
|
||||
{
|
||||
// <auth xmlns="urn:ietf:params:xml:ns:xmpp-sasl" mechanism="ANONYMOUS" />
|
||||
|
||||
NSXMLElement *auth = [NSXMLElement elementWithName:@"auth" xmlns:@"urn:ietf:params:xml:ns:xmpp-sasl"];
|
||||
[auth addAttributeWithName:@"mechanism" stringValue:@"ANONYMOUS"];
|
||||
|
||||
[xmppStream sendAuthElement:auth];
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (XMPPHandleAuthResponse)handleAuth:(NSXMLElement *)authResponse
|
||||
{
|
||||
// We're expecting a success response.
|
||||
// If we get anything else we can safely assume it's the equivalent of a failure response.
|
||||
|
||||
if ([[authResponse name] isEqualToString:@"success"])
|
||||
{
|
||||
return XMPP_AUTH_SUCCESS;
|
||||
}
|
||||
else
|
||||
{
|
||||
return XMPP_AUTH_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark -
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@implementation XMPPStream (XMPPAnonymousAuthentication)
|
||||
|
||||
- (BOOL)supportsAnonymousAuthentication
|
||||
{
|
||||
return [self supportsAuthenticationMechanism:[XMPPAnonymousAuthentication mechanismName]];
|
||||
}
|
||||
|
||||
- (BOOL)authenticateAnonymously:(NSError **)errPtr
|
||||
{
|
||||
XMPPLogTrace();
|
||||
|
||||
__block BOOL result = YES;
|
||||
__block NSError *err = nil;
|
||||
|
||||
dispatch_block_t block = ^{ @autoreleasepool {
|
||||
|
||||
if ([self supportsAnonymousAuthentication])
|
||||
{
|
||||
XMPPAnonymousAuthentication *anonymousAuth = [[XMPPAnonymousAuthentication alloc] initWithStream:self];
|
||||
|
||||
result = [self authenticate:anonymousAuth error:&err];
|
||||
}
|
||||
else
|
||||
{
|
||||
NSString *errMsg = @"The server does not support anonymous authentication.";
|
||||
NSDictionary *info = @{NSLocalizedDescriptionKey : errMsg};
|
||||
|
||||
err = [NSError errorWithDomain:XMPPStreamErrorDomain code:XMPPStreamUnsupportedAction userInfo:info];
|
||||
|
||||
result = NO;
|
||||
}
|
||||
}};
|
||||
|
||||
if (dispatch_get_specific(self.xmppQueueTag))
|
||||
block();
|
||||
else
|
||||
dispatch_sync(self.xmppQueue, block);
|
||||
|
||||
if (errPtr)
|
||||
*errPtr = err;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@end
|
||||
@ -0,0 +1,22 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "XMPPSASLAuthentication.h"
|
||||
#import "XMPPStream.h"
|
||||
|
||||
|
||||
@interface XMPPDeprecatedDigestAuthentication : NSObject <XMPPSASLAuthentication>
|
||||
|
||||
// This class implements the XMPPSASLAuthentication protocol.
|
||||
//
|
||||
// See XMPPSASLAuthentication.h for more information.
|
||||
|
||||
@end
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark -
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@interface XMPPStream (XMPPDeprecatedDigestAuthentication)
|
||||
|
||||
- (BOOL)supportsDeprecatedDigestAuthentication;
|
||||
|
||||
@end
|
||||
@ -0,0 +1,162 @@
|
||||
#import "XMPPDeprecatedDigestAuthentication.h"
|
||||
#import "XMPP.h"
|
||||
#import "XMPPInternal.h"
|
||||
#import "XMPPLogging.h"
|
||||
#import "NSData+XMPP.h"
|
||||
#import "NSXMLElement+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
|
||||
|
||||
// Log levels: off, error, warn, info, verbose
|
||||
#if DEBUG
|
||||
static const int xmppLogLevel = XMPP_LOG_LEVEL_INFO; // | XMPP_LOG_FLAG_TRACE;
|
||||
#else
|
||||
static const int xmppLogLevel = XMPP_LOG_LEVEL_WARN;
|
||||
#endif
|
||||
|
||||
|
||||
@implementation XMPPDeprecatedDigestAuthentication
|
||||
{
|
||||
#if __has_feature(objc_arc_weak)
|
||||
__weak XMPPStream *xmppStream;
|
||||
#else
|
||||
__unsafe_unretained XMPPStream *xmppStream;
|
||||
#endif
|
||||
|
||||
NSString *password;
|
||||
}
|
||||
|
||||
+ (NSString *)mechanismName
|
||||
{
|
||||
// This deprecated method isn't listed in the normal mechanisms list
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (id)initWithStream:(XMPPStream *)stream password:(NSString *)inPassword
|
||||
{
|
||||
if ((self = [super init]))
|
||||
{
|
||||
xmppStream = stream;
|
||||
password = inPassword;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (BOOL)start:(NSError **)errPtr
|
||||
{
|
||||
XMPPLogTrace();
|
||||
|
||||
// The server does not appear to support SASL authentication (at least any type we can use)
|
||||
// So we'll revert back to the old fashioned jabber:iq:auth mechanism
|
||||
|
||||
XMPPJID *myJID = xmppStream.myJID;
|
||||
|
||||
NSString *username = [myJID user];
|
||||
NSString *resource = [myJID resource];
|
||||
|
||||
if ([resource length] == 0)
|
||||
{
|
||||
// If resource is nil or empty, we need to auto-create one
|
||||
|
||||
resource = [XMPPStream generateUUID];
|
||||
}
|
||||
|
||||
NSString *rootID = [[[xmppStream rootElement] attributeForName:@"id"] stringValue];
|
||||
NSString *digestStr = [NSString stringWithFormat:@"%@%@", rootID, password];
|
||||
|
||||
NSString *digest = [[[digestStr dataUsingEncoding:NSUTF8StringEncoding] xmpp_sha1Digest] xmpp_hexStringValue];
|
||||
|
||||
NSXMLElement *query = [NSXMLElement elementWithName:@"query" xmlns:@"jabber:iq:auth"];
|
||||
[query addChild:[NSXMLElement elementWithName:@"username" stringValue:username]];
|
||||
[query addChild:[NSXMLElement elementWithName:@"resource" stringValue:resource]];
|
||||
[query addChild:[NSXMLElement elementWithName:@"digest" stringValue:digest]];
|
||||
|
||||
XMPPIQ *iq = [XMPPIQ iqWithType:@"set"];
|
||||
[iq addChild:query];
|
||||
|
||||
[xmppStream sendAuthElement:iq];
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (XMPPHandleAuthResponse)handleAuth:(NSXMLElement *)authResponse
|
||||
{
|
||||
XMPPLogTrace();
|
||||
|
||||
// We used the old fashioned jabber:iq:auth mechanism
|
||||
|
||||
if ([[authResponse attributeStringValueForName:@"type"] isEqualToString:@"error"])
|
||||
{
|
||||
return XMPP_AUTH_FAIL;
|
||||
}
|
||||
else
|
||||
{
|
||||
return XMPP_AUTH_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)shouldResendOpeningNegotiationAfterSuccessfulAuthentication
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark -
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@implementation XMPPStream (XMPPDeprecatedDigestAuthentication)
|
||||
|
||||
/**
|
||||
* This method only applies to servers that don't support XMPP version 1.0, as defined in RFC 3920.
|
||||
* With these servers, we attempt to discover supported authentication modes via the jabber:iq:auth namespace.
|
||||
**/
|
||||
- (BOOL)supportsDeprecatedDigestAuthentication
|
||||
{
|
||||
__block BOOL result = NO;
|
||||
|
||||
dispatch_block_t block = ^{ @autoreleasepool {
|
||||
|
||||
// The root element can be properly queried for authentication mechanisms anytime after the
|
||||
// stream:features are received, and TLS has been setup (if required)
|
||||
if (self.state >= STATE_XMPP_POST_NEGOTIATION)
|
||||
{
|
||||
// Search for an iq element within the rootElement.
|
||||
// Recall that some servers might stupidly add a "jabber:client" namespace which might cause problems
|
||||
// if we simply used the elementForName method.
|
||||
|
||||
NSXMLElement *iq = nil;
|
||||
|
||||
NSUInteger i, count = [self.rootElement childCount];
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
NSXMLNode *childNode = [self.rootElement childAtIndex:i];
|
||||
|
||||
if ([childNode kind] == NSXMLElementKind)
|
||||
{
|
||||
if ([[childNode name] isEqualToString:@"iq"])
|
||||
{
|
||||
iq = (NSXMLElement *)childNode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NSXMLElement *query = [iq elementForName:@"query" xmlns:@"jabber:iq:auth"];
|
||||
NSXMLElement *digest = [query elementForName:@"digest"];
|
||||
|
||||
result = (digest != nil);
|
||||
}
|
||||
}};
|
||||
|
||||
if (dispatch_get_specific(self.xmppQueueTag))
|
||||
block();
|
||||
else
|
||||
dispatch_sync(self.xmppQueue, block);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@end
|
||||
@ -0,0 +1,22 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "XMPPSASLAuthentication.h"
|
||||
#import "XMPPStream.h"
|
||||
|
||||
|
||||
@interface XMPPDeprecatedPlainAuthentication : NSObject <XMPPSASLAuthentication>
|
||||
|
||||
// This class implements the XMPPSASLAuthentication protocol.
|
||||
//
|
||||
// See XMPPSASLAuthentication.h for more information.
|
||||
|
||||
@end
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark -
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@interface XMPPStream (XMPPDeprecatedPlainAuthentication)
|
||||
|
||||
- (BOOL)supportsDeprecatedPlainAuthentication;
|
||||
|
||||
@end
|
||||
@ -0,0 +1,156 @@
|
||||
#import "XMPPDeprecatedPlainAuthentication.h"
|
||||
#import "XMPP.h"
|
||||
#import "XMPPInternal.h"
|
||||
#import "XMPPLogging.h"
|
||||
#import "NSXMLElement+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
|
||||
|
||||
// Log levels: off, error, warn, info, verbose
|
||||
#if DEBUG
|
||||
static const int xmppLogLevel = XMPP_LOG_LEVEL_INFO; // | XMPP_LOG_FLAG_TRACE;
|
||||
#else
|
||||
static const int xmppLogLevel = XMPP_LOG_LEVEL_WARN;
|
||||
#endif
|
||||
|
||||
|
||||
@implementation XMPPDeprecatedPlainAuthentication
|
||||
{
|
||||
#if __has_feature(objc_arc_weak)
|
||||
__weak XMPPStream *xmppStream;
|
||||
#else
|
||||
__unsafe_unretained XMPPStream *xmppStream;
|
||||
#endif
|
||||
|
||||
NSString *password;
|
||||
}
|
||||
|
||||
+ (NSString *)mechanismName
|
||||
{
|
||||
// This deprecated method isn't listed in the normal mechanisms list
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (id)initWithStream:(XMPPStream *)stream password:(NSString *)inPassword
|
||||
{
|
||||
if ((self = [super init]))
|
||||
{
|
||||
xmppStream = stream;
|
||||
password = inPassword;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (BOOL)start:(NSError **)errPtr
|
||||
{
|
||||
XMPPLogTrace();
|
||||
|
||||
// The server does not appear to support SASL authentication (at least any type we can use)
|
||||
// So we'll revert back to the old fashioned jabber:iq:auth mechanism
|
||||
|
||||
XMPPJID *myJID = xmppStream.myJID;
|
||||
|
||||
NSString *username = [myJID user];
|
||||
NSString *resource = [myJID resource];
|
||||
|
||||
if ([resource length] == 0)
|
||||
{
|
||||
// If resource is nil or empty, we need to auto-create one
|
||||
|
||||
resource = [XMPPStream generateUUID];
|
||||
}
|
||||
|
||||
NSXMLElement *query = [NSXMLElement elementWithName:@"query" xmlns:@"jabber:iq:auth"];
|
||||
[query addChild:[NSXMLElement elementWithName:@"username" stringValue:username]];
|
||||
[query addChild:[NSXMLElement elementWithName:@"resource" stringValue:resource]];
|
||||
[query addChild:[NSXMLElement elementWithName:@"password" stringValue:password]];
|
||||
|
||||
XMPPIQ *iq = [XMPPIQ iqWithType:@"set"];
|
||||
[iq addChild:query];
|
||||
|
||||
[xmppStream sendAuthElement:iq];
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (XMPPHandleAuthResponse)handleAuth:(NSXMLElement *)authResponse
|
||||
{
|
||||
XMPPLogTrace();
|
||||
|
||||
// We used the old fashioned jabber:iq:auth mechanism
|
||||
|
||||
if ([[authResponse attributeStringValueForName:@"type"] isEqualToString:@"error"])
|
||||
{
|
||||
return XMPP_AUTH_FAIL;
|
||||
}
|
||||
else
|
||||
{
|
||||
return XMPP_AUTH_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)shouldResendOpeningNegotiationAfterSuccessfulAuthentication
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark -
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@implementation XMPPStream (XMPPDeprecatedPlainAuthentication)
|
||||
|
||||
/**
|
||||
* This method only applies to servers that don't support XMPP version 1.0, as defined in RFC 3920.
|
||||
* With these servers, we attempt to discover supported authentication modes via the jabber:iq:auth namespace.
|
||||
**/
|
||||
- (BOOL)supportsDeprecatedPlainAuthentication
|
||||
{
|
||||
__block BOOL result = NO;
|
||||
|
||||
dispatch_block_t block = ^{ @autoreleasepool {
|
||||
|
||||
// The root element can be properly queried for authentication mechanisms anytime after the
|
||||
// stream:features are received, and TLS has been setup (if required)
|
||||
if (self.state >= STATE_XMPP_POST_NEGOTIATION)
|
||||
{
|
||||
// Search for an iq element within the rootElement.
|
||||
// Recall that some servers might stupidly add a "jabber:client" namespace which might cause problems
|
||||
// if we simply used the elementForName method.
|
||||
|
||||
NSXMLElement *iq = nil;
|
||||
|
||||
NSUInteger i, count = [self.rootElement childCount];
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
NSXMLNode *childNode = [self.rootElement childAtIndex:i];
|
||||
|
||||
if ([childNode kind] == NSXMLElementKind)
|
||||
{
|
||||
if ([[childNode name] isEqualToString:@"iq"])
|
||||
{
|
||||
iq = (NSXMLElement *)childNode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NSXMLElement *query = [iq elementForName:@"query" xmlns:@"jabber:iq:auth"];
|
||||
NSXMLElement *plain = [query elementForName:@"password"];
|
||||
|
||||
result = (plain != nil);
|
||||
}
|
||||
}};
|
||||
|
||||
if (dispatch_get_specific(self.xmppQueueTag))
|
||||
block();
|
||||
else
|
||||
dispatch_sync(self.xmppQueue, block);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@end
|
||||
22
Authentication/Digest-MD5/XMPPDigestMD5Authentication.h
Normal file
22
Authentication/Digest-MD5/XMPPDigestMD5Authentication.h
Normal file
@ -0,0 +1,22 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "XMPPSASLAuthentication.h"
|
||||
#import "XMPPStream.h"
|
||||
|
||||
|
||||
@interface XMPPDigestMD5Authentication : NSObject <XMPPSASLAuthentication>
|
||||
|
||||
// This class implements the XMPPSASLAuthentication protocol.
|
||||
//
|
||||
// See XMPPSASLAuthentication.h for more information.
|
||||
|
||||
@end
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark -
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@interface XMPPStream (XMPPDigestMD5Authentication)
|
||||
|
||||
- (BOOL)supportsDigestMD5Authentication;
|
||||
|
||||
@end
|
||||
336
Authentication/Digest-MD5/XMPPDigestMD5Authentication.m
Normal file
336
Authentication/Digest-MD5/XMPPDigestMD5Authentication.m
Normal file
@ -0,0 +1,336 @@
|
||||
#import "XMPPDigestMD5Authentication.h"
|
||||
#import "XMPP.h"
|
||||
#import "XMPPLogging.h"
|
||||
#import "XMPPInternal.h"
|
||||
#import "NSData+XMPP.h"
|
||||
#import "NSXMLElement+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
|
||||
|
||||
// Log levels: off, error, warn, info, verbose
|
||||
#if DEBUG
|
||||
static const int xmppLogLevel = XMPP_LOG_LEVEL_INFO; // | XMPP_LOG_FLAG_TRACE;
|
||||
#else
|
||||
static const int xmppLogLevel = XMPP_LOG_LEVEL_WARN;
|
||||
#endif
|
||||
|
||||
@interface XMPPDigestMD5Authentication ()
|
||||
{
|
||||
#if __has_feature(objc_arc_weak)
|
||||
__weak XMPPStream *xmppStream;
|
||||
#else
|
||||
__unsafe_unretained XMPPStream *xmppStream;
|
||||
#endif
|
||||
|
||||
BOOL awaitingChallenge;
|
||||
|
||||
NSString *realm;
|
||||
NSString *nonce;
|
||||
NSString *qop;
|
||||
NSString *cnonce;
|
||||
NSString *digestURI;
|
||||
NSString *username;
|
||||
NSString *password;
|
||||
}
|
||||
|
||||
// The properties are hooks (primarily for testing)
|
||||
|
||||
@property (nonatomic, strong) NSString *realm;
|
||||
@property (nonatomic, strong) NSString *nonce;
|
||||
@property (nonatomic, strong) NSString *qop;
|
||||
@property (nonatomic, strong) NSString *cnonce;
|
||||
@property (nonatomic, strong) NSString *digestURI;
|
||||
@property (nonatomic, strong) NSString *username;
|
||||
@property (nonatomic, strong) NSString *password;
|
||||
|
||||
- (NSDictionary *)dictionaryFromChallenge:(NSXMLElement *)challenge;
|
||||
- (NSString *)base64EncodedFullResponse;
|
||||
|
||||
@end
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark -
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@implementation XMPPDigestMD5Authentication
|
||||
|
||||
+ (NSString *)mechanismName
|
||||
{
|
||||
return @"DIGEST-MD5";
|
||||
}
|
||||
|
||||
@synthesize realm;
|
||||
@synthesize nonce;
|
||||
@synthesize qop;
|
||||
@synthesize cnonce;
|
||||
@synthesize digestURI;
|
||||
@synthesize username;
|
||||
@synthesize password;
|
||||
|
||||
- (id)initWithStream:(XMPPStream *)stream password:(NSString *)inPassword
|
||||
{
|
||||
return [self initWithStream:stream username:nil password:inPassword];
|
||||
}
|
||||
|
||||
- (id)initWithStream:(XMPPStream *)stream username:(NSString *)inUsername password:(NSString *)inPassword
|
||||
{
|
||||
if ((self = [super init]))
|
||||
{
|
||||
xmppStream = stream;
|
||||
username = inUsername;
|
||||
password = inPassword;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (BOOL)start:(NSError **)errPtr
|
||||
{
|
||||
XMPPLogTrace();
|
||||
|
||||
// <auth xmlns="urn:ietf:params:xml:ns:xmpp-sasl" mechanism="DIGEST-MD5" />
|
||||
|
||||
NSXMLElement *auth = [NSXMLElement elementWithName:@"auth" xmlns:@"urn:ietf:params:xml:ns:xmpp-sasl"];
|
||||
[auth addAttributeWithName:@"mechanism" stringValue:@"DIGEST-MD5"];
|
||||
|
||||
[xmppStream sendAuthElement:auth];
|
||||
awaitingChallenge = YES;
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (XMPPHandleAuthResponse)handleAuth1:(NSXMLElement *)authResponse
|
||||
{
|
||||
XMPPLogTrace();
|
||||
|
||||
// We're expecting a challenge response.
|
||||
// If we get anything else we're going to assume it's some kind of failure response.
|
||||
|
||||
if (![[authResponse name] isEqualToString:@"challenge"])
|
||||
{
|
||||
return XMPP_AUTH_FAIL;
|
||||
}
|
||||
|
||||
// Extract components from incoming challenge
|
||||
|
||||
NSDictionary *auth = [self dictionaryFromChallenge:authResponse];
|
||||
|
||||
realm = auth[@"realm"];
|
||||
nonce = auth[@"nonce"];
|
||||
qop = auth[@"qop"];
|
||||
|
||||
// Fill out all the other variables
|
||||
//
|
||||
// Sometimes the realm isn't specified.
|
||||
// In this case I believe the realm is implied as the virtual host name.
|
||||
|
||||
XMPPJID *myJID = xmppStream.myJID;
|
||||
|
||||
NSString *virtualHostName = [myJID domain];
|
||||
NSString *serverHostName = xmppStream.hostName;
|
||||
|
||||
if (realm == nil)
|
||||
{
|
||||
if ([virtualHostName length] > 0)
|
||||
realm = virtualHostName;
|
||||
else
|
||||
realm = serverHostName;
|
||||
}
|
||||
|
||||
if ([virtualHostName length] > 0)
|
||||
digestURI = [NSString stringWithFormat:@"xmpp/%@", virtualHostName];
|
||||
else
|
||||
digestURI = [NSString stringWithFormat:@"xmpp/%@", serverHostName];
|
||||
|
||||
if (cnonce == nil)
|
||||
cnonce = [XMPPStream generateUUID];
|
||||
|
||||
if (username == nil)
|
||||
{
|
||||
username = [myJID user];
|
||||
}
|
||||
|
||||
// Create and send challenge response element
|
||||
|
||||
NSXMLElement *response = [NSXMLElement elementWithName:@"response" xmlns:@"urn:ietf:params:xml:ns:xmpp-sasl"];
|
||||
[response setStringValue:[self base64EncodedFullResponse]];
|
||||
|
||||
[xmppStream sendAuthElement:response];
|
||||
awaitingChallenge = NO;
|
||||
|
||||
return XMPP_AUTH_CONTINUE;
|
||||
}
|
||||
|
||||
- (XMPPHandleAuthResponse)handleAuth2:(NSXMLElement *)authResponse
|
||||
{
|
||||
XMPPLogTrace();
|
||||
|
||||
if ([[authResponse name] isEqualToString:@"challenge"])
|
||||
{
|
||||
NSDictionary *auth = [self dictionaryFromChallenge:authResponse];
|
||||
NSString *rspauth = auth[@"rspauth"];
|
||||
|
||||
if (rspauth == nil)
|
||||
{
|
||||
// We're getting another challenge?
|
||||
// Not sure what this could possibly be, so for now we'll assume it's a failure.
|
||||
|
||||
return XMPP_AUTH_FAIL;
|
||||
}
|
||||
else
|
||||
{
|
||||
// We received another challenge, but it's really just an rspauth
|
||||
// This is supposed to be included in the success element (according to the updated RFC)
|
||||
// but many implementations incorrectly send it inside a second challenge request.
|
||||
//
|
||||
// Create and send empty challenge response element.
|
||||
|
||||
NSXMLElement *response =
|
||||
[NSXMLElement elementWithName:@"response" xmlns:@"urn:ietf:params:xml:ns:xmpp-sasl"];
|
||||
|
||||
[xmppStream sendAuthElement:response];
|
||||
|
||||
return XMPP_AUTH_CONTINUE;
|
||||
}
|
||||
}
|
||||
else if ([[authResponse name] isEqualToString:@"success"])
|
||||
{
|
||||
return XMPP_AUTH_SUCCESS;
|
||||
}
|
||||
else
|
||||
{
|
||||
return XMPP_AUTH_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
- (XMPPHandleAuthResponse)handleAuth:(NSXMLElement *)auth
|
||||
{
|
||||
XMPPLogTrace();
|
||||
|
||||
if (awaitingChallenge)
|
||||
{
|
||||
return [self handleAuth1:auth];
|
||||
}
|
||||
else
|
||||
{
|
||||
return [self handleAuth2:auth];
|
||||
}
|
||||
}
|
||||
|
||||
- (NSDictionary *)dictionaryFromChallenge:(NSXMLElement *)challenge
|
||||
{
|
||||
// The value of the challenge stanza is base 64 encoded.
|
||||
// Once "decoded", it's just a string of key=value pairs separated by commas.
|
||||
|
||||
NSData *base64Data = [[challenge stringValue] dataUsingEncoding:NSASCIIStringEncoding];
|
||||
NSData *decodedData = [base64Data xmpp_base64Decoded];
|
||||
|
||||
NSString *authStr = [[NSString alloc] initWithData:decodedData encoding:NSUTF8StringEncoding];
|
||||
|
||||
XMPPLogVerbose(@"%@: Decoded challenge: %@", THIS_FILE, authStr);
|
||||
|
||||
NSArray *components = [authStr componentsSeparatedByString:@","];
|
||||
NSMutableDictionary *auth = [NSMutableDictionary dictionaryWithCapacity:5];
|
||||
|
||||
for (NSString *component in components)
|
||||
{
|
||||
NSRange separator = [component rangeOfString:@"="];
|
||||
if (separator.location != NSNotFound)
|
||||
{
|
||||
NSMutableString *key = [[component substringToIndex:separator.location] mutableCopy];
|
||||
NSMutableString *value = [[component substringFromIndex:separator.location+1] mutableCopy];
|
||||
|
||||
if(key) CFStringTrimWhitespace((__bridge CFMutableStringRef)key);
|
||||
if(value) CFStringTrimWhitespace((__bridge CFMutableStringRef)value);
|
||||
|
||||
if ([value hasPrefix:@"\""] && [value hasSuffix:@"\""] && [value length] > 2)
|
||||
{
|
||||
// Strip quotes from value
|
||||
[value deleteCharactersInRange:NSMakeRange(0, 1)];
|
||||
[value deleteCharactersInRange:NSMakeRange([value length]-1, 1)];
|
||||
}
|
||||
|
||||
if(key && value)
|
||||
{
|
||||
auth[key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return auth;
|
||||
}
|
||||
|
||||
- (NSString *)response
|
||||
{
|
||||
NSString *HA1str = [NSString stringWithFormat:@"%@:%@:%@", username, realm, password];
|
||||
NSString *HA2str = [NSString stringWithFormat:@"AUTHENTICATE:%@", digestURI];
|
||||
|
||||
XMPPLogVerbose(@"HA1str: %@", HA1str);
|
||||
XMPPLogVerbose(@"HA2str: %@", HA2str);
|
||||
|
||||
NSData *HA1dataA = [[HA1str dataUsingEncoding:NSUTF8StringEncoding] xmpp_md5Digest];
|
||||
NSData *HA1dataB = [[NSString stringWithFormat:@":%@:%@", nonce, cnonce] dataUsingEncoding:NSUTF8StringEncoding];
|
||||
|
||||
XMPPLogVerbose(@"HA1dataA: %@", HA1dataA);
|
||||
XMPPLogVerbose(@"HA1dataB: %@", HA1dataB);
|
||||
|
||||
NSMutableData *HA1data = [NSMutableData dataWithCapacity:([HA1dataA length] + [HA1dataB length])];
|
||||
[HA1data appendData:HA1dataA];
|
||||
[HA1data appendData:HA1dataB];
|
||||
|
||||
XMPPLogVerbose(@"HA1data: %@", HA1data);
|
||||
|
||||
NSString *HA1 = [[HA1data xmpp_md5Digest] xmpp_hexStringValue];
|
||||
|
||||
NSString *HA2 = [[[HA2str dataUsingEncoding:NSUTF8StringEncoding] xmpp_md5Digest] xmpp_hexStringValue];
|
||||
|
||||
XMPPLogVerbose(@"HA1: %@", HA1);
|
||||
XMPPLogVerbose(@"HA2: %@", HA2);
|
||||
|
||||
NSString *responseStr = [NSString stringWithFormat:@"%@:%@:00000001:%@:auth:%@",
|
||||
HA1, nonce, cnonce, HA2];
|
||||
|
||||
XMPPLogVerbose(@"responseStr: %@", responseStr);
|
||||
|
||||
NSString *response = [[[responseStr dataUsingEncoding:NSUTF8StringEncoding] xmpp_md5Digest] xmpp_hexStringValue];
|
||||
|
||||
XMPPLogVerbose(@"response: %@", response);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
- (NSString *)base64EncodedFullResponse
|
||||
{
|
||||
NSMutableString *buffer = [NSMutableString stringWithCapacity:100];
|
||||
[buffer appendFormat:@"username=\"%@\",", username];
|
||||
[buffer appendFormat:@"realm=\"%@\",", realm];
|
||||
[buffer appendFormat:@"nonce=\"%@\",", nonce];
|
||||
[buffer appendFormat:@"cnonce=\"%@\",", cnonce];
|
||||
[buffer appendFormat:@"nc=00000001,"];
|
||||
[buffer appendFormat:@"qop=auth,"];
|
||||
[buffer appendFormat:@"digest-uri=\"%@\",", digestURI];
|
||||
[buffer appendFormat:@"response=%@,", [self response]];
|
||||
[buffer appendFormat:@"charset=utf-8"];
|
||||
|
||||
XMPPLogVerbose(@"%@: Decoded response: %@", THIS_FILE, buffer);
|
||||
|
||||
NSData *utf8data = [buffer dataUsingEncoding:NSUTF8StringEncoding];
|
||||
|
||||
return [utf8data xmpp_base64Encoded];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark -
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@implementation XMPPStream (XMPPDigestMD5Authentication)
|
||||
|
||||
- (BOOL)supportsDigestMD5Authentication
|
||||
{
|
||||
return [self supportsAuthenticationMechanism:[XMPPDigestMD5Authentication mechanismName]];
|
||||
}
|
||||
|
||||
@end
|
||||
22
Authentication/Plain/XMPPPlainAuthentication.h
Normal file
22
Authentication/Plain/XMPPPlainAuthentication.h
Normal file
@ -0,0 +1,22 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "XMPPSASLAuthentication.h"
|
||||
#import "XMPPStream.h"
|
||||
|
||||
|
||||
@interface XMPPPlainAuthentication : NSObject <XMPPSASLAuthentication>
|
||||
|
||||
// This class implements the XMPPSASLAuthentication protocol.
|
||||
//
|
||||
// See XMPPSASLAuthentication.h for more information.
|
||||
|
||||
@end
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark -
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@interface XMPPStream (XMPPPlainAuthentication)
|
||||
|
||||
- (BOOL)supportsPlainAuthentication;
|
||||
|
||||
@end
|
||||
114
Authentication/Plain/XMPPPlainAuthentication.m
Normal file
114
Authentication/Plain/XMPPPlainAuthentication.m
Normal file
@ -0,0 +1,114 @@
|
||||
#import "XMPPPlainAuthentication.h"
|
||||
#import "XMPP.h"
|
||||
#import "XMPPLogging.h"
|
||||
#import "XMPPInternal.h"
|
||||
#import "NSData+XMPP.h"
|
||||
#import "NSXMLElement+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
|
||||
|
||||
// Log levels: off, error, warn, info, verbose
|
||||
#if DEBUG
|
||||
static const int xmppLogLevel = XMPP_LOG_LEVEL_INFO; // | XMPP_LOG_FLAG_TRACE;
|
||||
#else
|
||||
static const int xmppLogLevel = XMPP_LOG_LEVEL_WARN;
|
||||
#endif
|
||||
|
||||
|
||||
@implementation XMPPPlainAuthentication
|
||||
{
|
||||
#if __has_feature(objc_arc_weak)
|
||||
__weak XMPPStream *xmppStream;
|
||||
#else
|
||||
__unsafe_unretained XMPPStream *xmppStream;
|
||||
#endif
|
||||
|
||||
NSString *username;
|
||||
NSString *password;
|
||||
}
|
||||
|
||||
+ (NSString *)mechanismName
|
||||
{
|
||||
return @"PLAIN";
|
||||
}
|
||||
|
||||
- (id)initWithStream:(XMPPStream *)stream password:(NSString *)inPassword
|
||||
{
|
||||
return [self initWithStream:stream username:nil password:inPassword];
|
||||
}
|
||||
|
||||
- (id)initWithStream:(XMPPStream *)stream username:(NSString *)inUsername password:(NSString *)inPassword
|
||||
{
|
||||
if ((self = [super init]))
|
||||
{
|
||||
xmppStream = stream;
|
||||
username = inUsername;
|
||||
password = inPassword;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (BOOL)start:(NSError **)errPtr
|
||||
{
|
||||
XMPPLogTrace();
|
||||
|
||||
// From RFC 4616 - PLAIN SASL Mechanism:
|
||||
// [authzid] UTF8NUL authcid UTF8NUL passwd
|
||||
//
|
||||
// authzid: authorization identity
|
||||
// authcid: authentication identity (username)
|
||||
// passwd : password for authcid
|
||||
|
||||
NSString *authUsername = username;
|
||||
if (!authUsername)
|
||||
{
|
||||
authUsername = [xmppStream.myJID user];
|
||||
}
|
||||
|
||||
NSString *payload = [NSString stringWithFormat:@"\0%@\0%@", authUsername, password];
|
||||
NSString *base64 = [[payload dataUsingEncoding:NSUTF8StringEncoding] xmpp_base64Encoded];
|
||||
|
||||
// <auth xmlns="urn:ietf:params:xml:ns:xmpp-sasl" mechanism="PLAIN">Base-64-Info</auth>
|
||||
|
||||
NSXMLElement *auth = [NSXMLElement elementWithName:@"auth" xmlns:@"urn:ietf:params:xml:ns:xmpp-sasl"];
|
||||
[auth addAttributeWithName:@"mechanism" stringValue:@"PLAIN"];
|
||||
[auth setStringValue:base64];
|
||||
|
||||
[xmppStream sendAuthElement:auth];
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (XMPPHandleAuthResponse)handleAuth:(NSXMLElement *)authResponse
|
||||
{
|
||||
XMPPLogTrace();
|
||||
|
||||
// We're expecting a success response.
|
||||
// If we get anything else we can safely assume it's the equivalent of a failure response.
|
||||
|
||||
if ([[authResponse name] isEqualToString:@"success"])
|
||||
{
|
||||
return XMPP_AUTH_SUCCESS;
|
||||
}
|
||||
else
|
||||
{
|
||||
return XMPP_AUTH_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark -
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@implementation XMPPStream (XMPPPlainAuthentication)
|
||||
|
||||
- (BOOL)supportsPlainAuthentication
|
||||
{
|
||||
return [self supportsAuthenticationMechanism:[XMPPPlainAuthentication mechanismName]];
|
||||
}
|
||||
|
||||
@end
|
||||
21
Authentication/SCRAM-SHA-1/XMPPSCRAMSHA1Authentication.h
Normal file
21
Authentication/SCRAM-SHA-1/XMPPSCRAMSHA1Authentication.h
Normal file
@ -0,0 +1,21 @@
|
||||
//
|
||||
// XMPPSCRAMSHA1Authentication.h
|
||||
// iPhoneXMPP
|
||||
//
|
||||
// Created by David Chiles on 3/21/14.
|
||||
//
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "XMPPSASLAuthentication.h"
|
||||
#import "XMPPStream.h"
|
||||
|
||||
@interface XMPPSCRAMSHA1Authentication : NSObject <XMPPSASLAuthentication>
|
||||
|
||||
@end
|
||||
|
||||
@interface XMPPStream (XMPPSCRAMSHA1Authentication)
|
||||
|
||||
- (BOOL)supportsSCRAMSHA1Authentication;
|
||||
|
||||
@end
|
||||
342
Authentication/SCRAM-SHA-1/XMPPSCRAMSHA1Authentication.m
Normal file
342
Authentication/SCRAM-SHA-1/XMPPSCRAMSHA1Authentication.m
Normal file
@ -0,0 +1,342 @@
|
||||
//
|
||||
// XMPPSCRAMSHA1Authentication.m
|
||||
// iPhoneXMPP
|
||||
//
|
||||
// Created by David Chiles on 3/21/14.
|
||||
//
|
||||
//
|
||||
|
||||
#import "XMPPSCRAMSHA1Authentication.h"
|
||||
#import "XMPP.h"
|
||||
#import "XMPPLogging.h"
|
||||
#import "XMPPStream.h"
|
||||
#import "XMPPInternal.h"
|
||||
#import "NSData+XMPP.h"
|
||||
#import "XMPPStringPrep.h"
|
||||
|
||||
#import <CommonCrypto/CommonKeyDerivation.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_INFO; // | XMPP_LOG_FLAG_TRACE;
|
||||
#else
|
||||
static const int xmppLogLevel = XMPP_LOG_LEVEL_WARN;
|
||||
#endif
|
||||
|
||||
@interface XMPPSCRAMSHA1Authentication ()
|
||||
{
|
||||
#if __has_feature(objc_arc_weak)
|
||||
__weak XMPPStream *xmppStream;
|
||||
#else
|
||||
__unsafe_unretained XMPPStream *xmppStream;
|
||||
#endif
|
||||
}
|
||||
|
||||
@property (nonatomic) BOOL awaitingChallenge;
|
||||
@property (nonatomic, strong) NSString *username;
|
||||
@property (nonatomic, strong) NSString *password;
|
||||
@property (nonatomic, strong) NSString *clientNonce;
|
||||
@property (nonatomic, strong) NSString *combinedNonce;
|
||||
@property (nonatomic, strong) NSString *salt;
|
||||
@property (nonatomic, strong) NSNumber *count;
|
||||
@property (nonatomic, strong) NSString *serverMessage1;
|
||||
@property (nonatomic, strong) NSString *clientFirstMessageBare;
|
||||
@property (nonatomic, strong) NSData *serverSignatureData;
|
||||
@property (nonatomic, strong) NSData *clientProofData;
|
||||
@property (nonatomic) CCHmacAlgorithm hashAlgorithm;
|
||||
|
||||
@end
|
||||
|
||||
///////////RFC5802 http://tools.ietf.org/html/rfc5802 //////////////
|
||||
|
||||
//Channel binding not yet supported
|
||||
|
||||
@implementation XMPPSCRAMSHA1Authentication
|
||||
|
||||
+ (NSString *)mechanismName
|
||||
{
|
||||
return @"SCRAM-SHA-1";
|
||||
}
|
||||
|
||||
- (id)initWithStream:(XMPPStream *)stream password:(NSString *)password
|
||||
{
|
||||
return [self initWithStream:stream username:nil password:password];
|
||||
}
|
||||
|
||||
- (id)initWithStream:(XMPPStream *)stream username:(NSString *)username password:(NSString *)password
|
||||
{
|
||||
if ((self = [super init])) {
|
||||
xmppStream = stream;
|
||||
if (username)
|
||||
{
|
||||
_username = username;
|
||||
}
|
||||
else
|
||||
{
|
||||
_username = [XMPPStringPrep prepNode:[xmppStream.myJID user]];
|
||||
}
|
||||
_password = [XMPPStringPrep prepPassword:password];
|
||||
_hashAlgorithm = kCCHmacAlgSHA1;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (BOOL)start:(NSError **)errPtr
|
||||
{
|
||||
XMPPLogTrace();
|
||||
|
||||
if(self.username.length || self.password.length) {
|
||||
|
||||
NSXMLElement *auth = [NSXMLElement elementWithName:@"auth" xmlns:@"urn:ietf:params:xml:ns:xmpp-sasl"];
|
||||
[auth addAttributeWithName:@"mechanism" stringValue:@"SCRAM-SHA-1"];
|
||||
[auth setStringValue:[self clientMessage1]];
|
||||
|
||||
[xmppStream sendAuthElement:auth];
|
||||
self.awaitingChallenge = YES;
|
||||
|
||||
return YES;
|
||||
}
|
||||
else {
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
|
||||
- (XMPPHandleAuthResponse)handleAuth1:(NSXMLElement *)authResponse
|
||||
{
|
||||
XMPPLogTrace();
|
||||
|
||||
// We're expecting a challenge response.
|
||||
// If we get anything else we're going to assume it's some kind of failure response.
|
||||
|
||||
if (![[authResponse name] isEqualToString:@"challenge"])
|
||||
{
|
||||
return XMPP_AUTH_FAIL;
|
||||
}
|
||||
|
||||
NSDictionary *auth = [self dictionaryFromChallenge:authResponse];
|
||||
|
||||
NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
|
||||
[numberFormatter setNumberStyle:NSNumberFormatterDecimalStyle];
|
||||
|
||||
self.combinedNonce = auth[@"r"];
|
||||
self.salt = auth[@"s"];
|
||||
self.count = [numberFormatter numberFromString:auth[@"i"]];
|
||||
|
||||
//We have all the necessary information to calculate client proof and server signature
|
||||
if ([self calculateProofs]) {
|
||||
NSXMLElement *response = [NSXMLElement elementWithName:@"response" xmlns:@"urn:ietf:params:xml:ns:xmpp-sasl"];
|
||||
[response setStringValue:[self clientMessage2]];
|
||||
|
||||
[xmppStream sendAuthElement:response];
|
||||
self.awaitingChallenge = NO;
|
||||
|
||||
return XMPP_AUTH_CONTINUE;
|
||||
}
|
||||
else {
|
||||
return XMPP_AUTH_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
- (XMPPHandleAuthResponse)handleAuth2:(NSXMLElement *)authResponse
|
||||
{
|
||||
XMPPLogTrace();
|
||||
|
||||
NSDictionary *auth = [self dictionaryFromChallenge:authResponse];
|
||||
|
||||
if ([[authResponse name] isEqual:@"success"]) {
|
||||
NSString *receivedServerSignature = auth[@"v"];
|
||||
|
||||
if([self.serverSignatureData isEqualToData:[[receivedServerSignature dataUsingEncoding:NSUTF8StringEncoding] xmpp_base64Decoded]]){
|
||||
return XMPP_AUTH_SUCCESS;
|
||||
}
|
||||
else {
|
||||
return XMPP_AUTH_FAIL;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return XMPP_AUTH_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
- (XMPPHandleAuthResponse)handleAuth:(NSXMLElement *)auth
|
||||
{
|
||||
XMPPLogTrace();
|
||||
|
||||
if (self.awaitingChallenge) {
|
||||
return [self handleAuth1:auth];
|
||||
}
|
||||
else {
|
||||
return [self handleAuth2:auth];
|
||||
}
|
||||
}
|
||||
|
||||
- (NSString *)clientMessage1
|
||||
{
|
||||
self.clientNonce = [XMPPStream generateUUID];
|
||||
|
||||
self.clientFirstMessageBare = [NSString stringWithFormat:@"n=%@,r=%@",self.username,self.clientNonce];
|
||||
|
||||
NSData *message1Data = [[NSString stringWithFormat:@"n,,%@",self.clientFirstMessageBare] dataUsingEncoding:NSUTF8StringEncoding];
|
||||
|
||||
return [message1Data xmpp_base64Encoded];
|
||||
}
|
||||
|
||||
- (NSString *)clientMessage2
|
||||
{
|
||||
NSString *clientProofString = [self.clientProofData xmpp_base64Encoded];
|
||||
NSData *message2Data = [[NSString stringWithFormat:@"c=biws,r=%@,p=%@",self.combinedNonce,clientProofString] dataUsingEncoding:NSUTF8StringEncoding];
|
||||
|
||||
return [message2Data xmpp_base64Encoded];
|
||||
}
|
||||
|
||||
- (BOOL)calculateProofs
|
||||
{
|
||||
//Check to see that we have a password, salt and iteration count above 4096 (from RFC5802)
|
||||
if (!self.password.length || !self.salt.length || self.count.unsignedIntegerValue < 4096) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
NSData *passwordData = [self.password dataUsingEncoding:NSUTF8StringEncoding];
|
||||
NSData *saltData = [[self.salt dataUsingEncoding:NSUTF8StringEncoding] xmpp_base64Decoded];
|
||||
|
||||
NSData *saltedPasswordData = [self HashWithAlgorithm:self.hashAlgorithm password:passwordData salt:saltData iterations:[self.count unsignedIntValue]];
|
||||
|
||||
NSData *clientKeyData = [self HashWithAlgorithm:self.hashAlgorithm data:[@"Client Key" dataUsingEncoding:NSUTF8StringEncoding] key:saltedPasswordData];
|
||||
NSData *serverKeyData = [self HashWithAlgorithm:self.hashAlgorithm data:[@"Server Key" dataUsingEncoding:NSUTF8StringEncoding] key:saltedPasswordData];
|
||||
|
||||
NSData *storedKeyData = [clientKeyData xmpp_sha1Digest];
|
||||
|
||||
NSData *authMessageData = [[NSString stringWithFormat:@"%@,%@,c=biws,r=%@",self.clientFirstMessageBare,self.serverMessage1,self.combinedNonce] dataUsingEncoding:NSUTF8StringEncoding];
|
||||
|
||||
NSData *clientSignatureData = [self HashWithAlgorithm:self.hashAlgorithm data:authMessageData key:storedKeyData];
|
||||
|
||||
self.serverSignatureData = [self HashWithAlgorithm:self.hashAlgorithm data:authMessageData key:serverKeyData];
|
||||
self.clientProofData = [self xorData:clientKeyData withData:clientSignatureData];
|
||||
|
||||
//check to see that we caclulated some client proof and server signature
|
||||
if (self.clientProofData && self.serverSignatureData) {
|
||||
return YES;
|
||||
}
|
||||
else {
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
|
||||
- (NSData *)HashWithAlgorithm:(CCHmacAlgorithm) algorithm password:(NSData *)passwordData salt:(NSData *)saltData iterations:(NSUInteger)rounds
|
||||
{
|
||||
NSMutableData *mutableSaltData = [saltData mutableCopy];
|
||||
UInt8 zeroHex= 0x00;
|
||||
UInt8 oneHex= 0x01;
|
||||
NSData *zeroData = [[NSData alloc] initWithBytes:&zeroHex length:sizeof(zeroHex)];
|
||||
NSData *oneData = [[NSData alloc] initWithBytes:&oneHex length:sizeof(oneHex)];
|
||||
|
||||
[mutableSaltData appendData:zeroData];
|
||||
[mutableSaltData appendData:zeroData];
|
||||
[mutableSaltData appendData:zeroData];
|
||||
[mutableSaltData appendData:oneData];
|
||||
|
||||
NSData *result = [self HashWithAlgorithm:algorithm data:mutableSaltData key:passwordData];
|
||||
NSData *previous = [result copy];
|
||||
|
||||
for (int i = 1; i < rounds; i++) {
|
||||
previous = [self HashWithAlgorithm:algorithm data:previous key:passwordData];
|
||||
result = [self xorData:result withData:previous];
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
- (NSData *)HashWithAlgorithm:(CCHmacAlgorithm) algorithm data:(NSData *)data key:(NSData *)key
|
||||
{
|
||||
unsigned char cHMAC[CC_SHA1_DIGEST_LENGTH];
|
||||
|
||||
CCHmac(algorithm, [key bytes], [key length], [data bytes], [data length], cHMAC);
|
||||
|
||||
return [[NSData alloc] initWithBytes:cHMAC length:sizeof(cHMAC)];
|
||||
}
|
||||
|
||||
- (NSData *)xorData:(NSData *)data1 withData:(NSData *)data2
|
||||
{
|
||||
NSMutableData *result = data1.mutableCopy;
|
||||
|
||||
char *dataPtr = (char *)result.mutableBytes;
|
||||
|
||||
char *keyData = (char *)data2.bytes;
|
||||
|
||||
char *keyPtr = keyData;
|
||||
int keyIndex = 0;
|
||||
|
||||
for (int x = 0; x < data1.length; x++) {
|
||||
*dataPtr = *dataPtr ^ *keyPtr;
|
||||
dataPtr++;
|
||||
keyPtr++;
|
||||
|
||||
if (++keyIndex == data2.length) {
|
||||
keyIndex = 0;
|
||||
keyPtr = keyData;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
- (NSDictionary *)dictionaryFromChallenge:(NSXMLElement *)challenge
|
||||
{
|
||||
// The value of the challenge stanza is base 64 encoded.
|
||||
// Once "decoded", it's just a string of key=value pairs separated by commas.
|
||||
|
||||
NSData *base64Data = [[challenge stringValue] dataUsingEncoding:NSASCIIStringEncoding];
|
||||
NSData *decodedData = [base64Data xmpp_base64Decoded];
|
||||
|
||||
self.serverMessage1 = [[NSString alloc] initWithData:decodedData encoding:NSUTF8StringEncoding];
|
||||
|
||||
XMPPLogVerbose(@"%@: Decoded challenge: %@", THIS_FILE, self.serverMessage1);
|
||||
|
||||
NSArray *components = [self.serverMessage1 componentsSeparatedByString:@","];
|
||||
NSMutableDictionary *auth = [NSMutableDictionary dictionaryWithCapacity:5];
|
||||
|
||||
for (NSString *component in components)
|
||||
{
|
||||
NSRange separator = [component rangeOfString:@"="];
|
||||
if (separator.location != NSNotFound)
|
||||
{
|
||||
NSMutableString *key = [[component substringToIndex:separator.location] mutableCopy];
|
||||
NSMutableString *value = [[component substringFromIndex:separator.location+1] mutableCopy];
|
||||
|
||||
if(key) CFStringTrimWhitespace((__bridge CFMutableStringRef)key);
|
||||
if(value) CFStringTrimWhitespace((__bridge CFMutableStringRef)value);
|
||||
|
||||
if ([value hasPrefix:@"\""] && [value hasSuffix:@"\""] && [value length] > 2)
|
||||
{
|
||||
// Strip quotes from value
|
||||
[value deleteCharactersInRange:NSMakeRange(0, 1)];
|
||||
[value deleteCharactersInRange:NSMakeRange([value length]-1, 1)];
|
||||
}
|
||||
|
||||
if(key && value)
|
||||
{
|
||||
auth[key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return auth;
|
||||
}
|
||||
@end
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark -
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@implementation XMPPStream (XMPPSCRAMSHA1Authentication)
|
||||
|
||||
- (BOOL)supportsSCRAMSHA1Authentication
|
||||
{
|
||||
return [self supportsAuthenticationMechanism:[XMPPSCRAMSHA1Authentication mechanismName]];
|
||||
}
|
||||
|
||||
@end
|
||||
@ -0,0 +1,56 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "XMPPSASLAuthentication.h"
|
||||
#import "XMPPStream.h"
|
||||
|
||||
|
||||
@interface XMPPXFacebookPlatformAuthentication : NSObject <XMPPSASLAuthentication>
|
||||
|
||||
/**
|
||||
* You should use this init method (as opposed the one defined in the XMPPSASLAuthentication protocol).
|
||||
**/
|
||||
- (id)initWithStream:(XMPPStream *)stream appId:(NSString *)appId accessToken:(NSString *)accessToken;
|
||||
|
||||
@end
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark -
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@interface XMPPStream (XMPPXFacebookPlatformAuthentication)
|
||||
|
||||
/**
|
||||
* Facebook Chat X-FACEBOOK-PLATFORM SASL authentication initialization.
|
||||
* This is a convienence init method to help configure Facebook Chat.
|
||||
**/
|
||||
- (id)initWithFacebookAppId:(NSString *)fbAppId;
|
||||
|
||||
/**
|
||||
* The appId can be passed to custom authentication classes.
|
||||
* For example, the appId is used for Facebook Chat X-FACEBOOK-PLATFORM SASL authentication.
|
||||
**/
|
||||
@property (readwrite, copy) NSString *facebookAppId;
|
||||
|
||||
/**
|
||||
* Returns whether or not the server supports X-FACEBOOK-PLATFORM authentication.
|
||||
*
|
||||
* This information is available after the stream is connected.
|
||||
* In other words, after the delegate has received xmppStreamDidConnect: notification.
|
||||
**/
|
||||
- (BOOL)supportsXFacebookPlatformAuthentication;
|
||||
|
||||
/**
|
||||
* This method attempts to start the facebook oauth authentication process.
|
||||
*
|
||||
* This method is asynchronous.
|
||||
*
|
||||
* If there is something immediately wrong,
|
||||
* such as the stream is not connected or doesn't have a set appId or accessToken,
|
||||
* the method will return NO and set the error.
|
||||
* Otherwise the delegate callbacks are used to communicate auth success or failure.
|
||||
*
|
||||
* @see xmppStreamDidAuthenticate:
|
||||
* @see xmppStream:didNotAuthenticate:
|
||||
**/
|
||||
- (BOOL)authenticateWithFacebookAccessToken:(NSString *)accessToken error:(NSError **)errPtr;
|
||||
|
||||
@end
|
||||
@ -0,0 +1,327 @@
|
||||
#import "XMPPXFacebookPlatformAuthentication.h"
|
||||
#import "XMPP.h"
|
||||
#import "XMPPLogging.h"
|
||||
#import "XMPPInternal.h"
|
||||
#import "NSData+XMPP.h"
|
||||
|
||||
#import <objc/runtime.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_INFO; // | XMPP_LOG_FLAG_TRACE;
|
||||
#else
|
||||
static const int xmppLogLevel = XMPP_LOG_LEVEL_WARN;
|
||||
#endif
|
||||
|
||||
static NSString *const XMPPFacebookChatHostName = @"chat.facebook.com";
|
||||
|
||||
static char facebookAppIdKey;
|
||||
|
||||
@interface XMPPXFacebookPlatformAuthentication ()
|
||||
{
|
||||
#if __has_feature(objc_arc_weak)
|
||||
__weak XMPPStream *xmppStream;
|
||||
#else
|
||||
__unsafe_unretained XMPPStream *xmppStream;
|
||||
#endif
|
||||
|
||||
BOOL awaitingChallenge;
|
||||
|
||||
NSString *appId;
|
||||
NSString *accessToken;
|
||||
NSString *nonce;
|
||||
NSString *method;
|
||||
}
|
||||
|
||||
- (NSDictionary *)dictionaryFromChallenge:(NSXMLElement *)challenge;
|
||||
- (NSString *)base64EncodedFullResponse;
|
||||
|
||||
@end
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark -
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@implementation XMPPXFacebookPlatformAuthentication
|
||||
|
||||
+ (NSString *)mechanismName
|
||||
{
|
||||
return @"X-FACEBOOK-PLATFORM";
|
||||
}
|
||||
|
||||
- (id)initWithStream:(XMPPStream *)stream password:(NSString *)password
|
||||
{
|
||||
if ((self = [super init]))
|
||||
{
|
||||
xmppStream = stream;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)initWithStream:(XMPPStream *)stream appId:(NSString *)inAppId accessToken:(NSString *)inAccessToken
|
||||
{
|
||||
if ((self = [super init]))
|
||||
{
|
||||
xmppStream = stream;
|
||||
appId = inAppId;
|
||||
accessToken = inAccessToken;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (BOOL)start:(NSError **)errPtr
|
||||
{
|
||||
if (!appId || !accessToken)
|
||||
{
|
||||
NSString *errMsg = @"Missing facebook appId and/or accessToken.";
|
||||
NSDictionary *info = @{NSLocalizedDescriptionKey : errMsg};
|
||||
|
||||
NSError *err = [NSError errorWithDomain:XMPPStreamErrorDomain code:XMPPStreamInvalidState userInfo:info];
|
||||
|
||||
if (errPtr) *errPtr = err;
|
||||
return NO;
|
||||
}
|
||||
|
||||
// <auth xmlns="urn:ietf:params:xml:ns:xmpp-sasl" mechanism="X-FACEBOOK-PLATFORM" />
|
||||
|
||||
NSXMLElement *auth = [NSXMLElement elementWithName:@"auth" xmlns:@"urn:ietf:params:xml:ns:xmpp-sasl"];
|
||||
[auth addAttributeWithName:@"mechanism" stringValue:@"X-FACEBOOK-PLATFORM"];
|
||||
|
||||
[xmppStream sendAuthElement:auth];
|
||||
awaitingChallenge = YES;
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (XMPPHandleAuthResponse)handleAuth1:(NSXMLElement *)authResponse
|
||||
{
|
||||
XMPPLogTrace();
|
||||
|
||||
// We're expecting a challenge response.
|
||||
// If we get anything else we're going to assume it's some kind of failure response.
|
||||
|
||||
if (![[authResponse name] isEqualToString:@"challenge"])
|
||||
{
|
||||
return XMPP_AUTH_FAIL;
|
||||
}
|
||||
|
||||
// Extract components from incoming challenge
|
||||
|
||||
NSDictionary *auth = [self dictionaryFromChallenge:authResponse];
|
||||
|
||||
nonce = auth[@"nonce"];
|
||||
method = auth[@"method"];
|
||||
|
||||
// Create and send challenge response element
|
||||
|
||||
NSXMLElement *response = [NSXMLElement elementWithName:@"response" xmlns:@"urn:ietf:params:xml:ns:xmpp-sasl"];
|
||||
[response setStringValue:[self base64EncodedFullResponse]];
|
||||
|
||||
[xmppStream sendAuthElement:response];
|
||||
awaitingChallenge = NO;
|
||||
|
||||
return XMPP_AUTH_CONTINUE;
|
||||
}
|
||||
|
||||
- (XMPPHandleAuthResponse)handleAuth2:(NSXMLElement *)authResponse
|
||||
{
|
||||
XMPPLogTrace();
|
||||
|
||||
// We're expecting a success response.
|
||||
// If we get anything else we can safely assume it's the equivalent of a failure response.
|
||||
|
||||
if ([[authResponse name] isEqualToString:@"success"])
|
||||
{
|
||||
return XMPP_AUTH_SUCCESS;
|
||||
}
|
||||
else
|
||||
{
|
||||
return XMPP_AUTH_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
- (XMPPHandleAuthResponse)handleAuth:(NSXMLElement *)authResponse
|
||||
{
|
||||
if (awaitingChallenge)
|
||||
{
|
||||
return [self handleAuth1:authResponse];
|
||||
}
|
||||
else
|
||||
{
|
||||
return [self handleAuth2:authResponse];
|
||||
}
|
||||
}
|
||||
|
||||
- (NSDictionary *)dictionaryFromChallenge:(NSXMLElement *)challenge
|
||||
{
|
||||
// The value of the challenge stanza is base 64 encoded.
|
||||
// Once "decoded", it's just a string of key=value pairs separated by ampersands.
|
||||
|
||||
NSData *base64Data = [[challenge stringValue] dataUsingEncoding:NSASCIIStringEncoding];
|
||||
NSData *decodedData = [base64Data xmpp_base64Decoded];
|
||||
|
||||
NSString *authStr = [[NSString alloc] initWithData:decodedData encoding:NSUTF8StringEncoding];
|
||||
|
||||
XMPPLogVerbose(@"%@: decoded challenge: %@", THIS_FILE, authStr);
|
||||
|
||||
NSArray *components = [authStr componentsSeparatedByString:@"&"];
|
||||
NSMutableDictionary *auth = [NSMutableDictionary dictionaryWithCapacity:3];
|
||||
|
||||
for (NSString *component in components)
|
||||
{
|
||||
NSRange separator = [component rangeOfString:@"="];
|
||||
if (separator.location != NSNotFound)
|
||||
{
|
||||
NSString *key = [[component substringToIndex:separator.location]
|
||||
stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
|
||||
|
||||
NSString *value = [[component substringFromIndex:separator.location+1]
|
||||
stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
|
||||
|
||||
if ([value hasPrefix:@"\""] && [value hasSuffix:@"\""] && [value length] > 2)
|
||||
{
|
||||
// Strip quotes from value
|
||||
value = [value substringWithRange:NSMakeRange(1,([value length]-2))];
|
||||
}
|
||||
|
||||
auth[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
return auth;
|
||||
}
|
||||
|
||||
- (NSString *)base64EncodedFullResponse
|
||||
{
|
||||
if (!appId || !accessToken || !method || !nonce)
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
srand([[NSDate date] timeIntervalSince1970]);
|
||||
|
||||
NSMutableString *buffer = [NSMutableString stringWithCapacity:250];
|
||||
[buffer appendFormat:@"method=%@&", method];
|
||||
[buffer appendFormat:@"nonce=%@&", nonce];
|
||||
[buffer appendFormat:@"access_token=%@&", accessToken];
|
||||
[buffer appendFormat:@"api_key=%@&", appId];
|
||||
[buffer appendFormat:@"call_id=%d&", rand()];
|
||||
[buffer appendFormat:@"v=%@",@"1.0"];
|
||||
|
||||
XMPPLogVerbose(@"XMPPXFacebookPlatformAuthentication: response for facebook: %@", buffer);
|
||||
|
||||
NSData *utf8data = [buffer dataUsingEncoding:NSUTF8StringEncoding];
|
||||
|
||||
return [utf8data xmpp_base64Encoded];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark -
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@implementation XMPPStream (XMPPXFacebookPlatformAuthentication)
|
||||
|
||||
- (id)initWithFacebookAppId:(NSString *)fbAppId
|
||||
{
|
||||
if ((self = [self init])) // Note: Using [self init], NOT [super init]
|
||||
{
|
||||
self.facebookAppId = fbAppId;
|
||||
self.myJID = [XMPPJID jidWithString:XMPPFacebookChatHostName];
|
||||
|
||||
// As of October 8, 2011, Facebook doesn't have their XMPP SRV records set.
|
||||
// And, as per the XMPP specification, we MUST check the XMPP SRV records for an IP address,
|
||||
// before falling back to a traditional A record lookup.
|
||||
//
|
||||
// So we're setting the hostname as a minor optimization to avoid the SRV timeout delay.
|
||||
|
||||
self.hostName = XMPPFacebookChatHostName;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSString *)facebookAppId
|
||||
{
|
||||
__block NSString *result = nil;
|
||||
|
||||
dispatch_block_t block = ^{
|
||||
result = objc_getAssociatedObject(self, &facebookAppIdKey);
|
||||
};
|
||||
|
||||
if (dispatch_get_specific(self.xmppQueueTag))
|
||||
block();
|
||||
else
|
||||
dispatch_sync(self.xmppQueue, block);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
- (void)setFacebookAppId:(NSString *)inFacebookAppId
|
||||
{
|
||||
NSString *newFacebookAppId = [inFacebookAppId copy];
|
||||
|
||||
dispatch_block_t block = ^{
|
||||
objc_setAssociatedObject(self, &facebookAppIdKey, newFacebookAppId, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
|
||||
};
|
||||
|
||||
if (dispatch_get_specific(self.xmppQueueTag))
|
||||
block();
|
||||
else
|
||||
dispatch_async(self.xmppQueue, block);
|
||||
}
|
||||
|
||||
- (BOOL)supportsXFacebookPlatformAuthentication
|
||||
{
|
||||
return [self supportsAuthenticationMechanism:[XMPPXFacebookPlatformAuthentication mechanismName]];
|
||||
}
|
||||
|
||||
/**
|
||||
* This method attempts to connect to the Facebook Chat servers
|
||||
* using the Facebook OAuth token returned by the Facebook OAuth 2.0 authentication process.
|
||||
**/
|
||||
- (BOOL)authenticateWithFacebookAccessToken:(NSString *)accessToken error:(NSError **)errPtr
|
||||
{
|
||||
XMPPLogTrace();
|
||||
|
||||
__block BOOL result = YES;
|
||||
__block NSError *err = nil;
|
||||
|
||||
dispatch_block_t block = ^{ @autoreleasepool {
|
||||
|
||||
if ([self supportsXFacebookPlatformAuthentication])
|
||||
{
|
||||
XMPPXFacebookPlatformAuthentication *facebookAuth =
|
||||
[[XMPPXFacebookPlatformAuthentication alloc] initWithStream:self
|
||||
appId:self.facebookAppId
|
||||
accessToken:accessToken];
|
||||
|
||||
result = [self authenticate:facebookAuth error:&err];
|
||||
}
|
||||
else
|
||||
{
|
||||
NSString *errMsg = @"The server does not support X-FACEBOOK-PLATFORM authentication.";
|
||||
NSDictionary *info = @{NSLocalizedDescriptionKey : errMsg};
|
||||
|
||||
err = [NSError errorWithDomain:XMPPStreamErrorDomain code:XMPPStreamUnsupportedAction userInfo:info];
|
||||
|
||||
result = NO;
|
||||
}
|
||||
}};
|
||||
|
||||
if (dispatch_get_specific(self.xmppQueueTag))
|
||||
block();
|
||||
else
|
||||
dispatch_sync(self.xmppQueue, block);
|
||||
|
||||
if (errPtr)
|
||||
*errPtr = err;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@end
|
||||
28
Authentication/X-OAuth2-Google/XMPPXOAuth2Google.h
Normal file
28
Authentication/X-OAuth2-Google/XMPPXOAuth2Google.h
Normal file
@ -0,0 +1,28 @@
|
||||
//
|
||||
// XMPPXOAuth2Google.h
|
||||
// Off the Record
|
||||
//
|
||||
// Created by David Chiles on 9/13/13.
|
||||
// Copyright (c) 2013 Chris Ballinger. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "XMPPSASLAuthentication.h"
|
||||
#import "XMPPStream.h"
|
||||
|
||||
@interface XMPPXOAuth2Google : NSObject <XMPPSASLAuthentication>
|
||||
|
||||
-(id)initWithStream:(XMPPStream *)stream accessToken:(NSString *)accessToken;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
|
||||
@interface XMPPStream (XMPPXOAuth2Google)
|
||||
|
||||
|
||||
- (BOOL)supportsXOAuth2GoogleAuthentication;
|
||||
|
||||
- (BOOL)authenticateWithGoogleAccessToken:(NSString *)accessToken error:(NSError **)errPtr;
|
||||
|
||||
@end
|
||||
180
Authentication/X-OAuth2-Google/XMPPXOAuth2Google.m
Normal file
180
Authentication/X-OAuth2-Google/XMPPXOAuth2Google.m
Normal file
@ -0,0 +1,180 @@
|
||||
//
|
||||
// XMPPXOAuth2Google.m
|
||||
// Off the Record
|
||||
//
|
||||
// Created by David Chiles on 9/13/13.
|
||||
// Copyright (c) 2013 Chris Ballinger. All rights reserved.
|
||||
//
|
||||
|
||||
#import "XMPPXOAuth2Google.h"
|
||||
#import "XMPP.h"
|
||||
#import "XMPPLogging.h"
|
||||
#import "XMPPInternal.h"
|
||||
#import "NSData+XMPP.h"
|
||||
|
||||
#import <objc/runtime.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_INFO; // | XMPP_LOG_FLAG_TRACE;
|
||||
#else
|
||||
static const int xmppLogLevel = XMPP_LOG_LEVEL_WARN;
|
||||
#endif
|
||||
|
||||
static NSString *const XMPPGoogleTalkHostName = @"talk.google.com";
|
||||
|
||||
@interface XMPPXOAuth2Google ()
|
||||
{
|
||||
#if __has_feature(objc_arc_weak)
|
||||
__weak XMPPStream *xmppStream;
|
||||
#else
|
||||
__unsafe_unretained XMPPStream *xmppStream;
|
||||
#endif
|
||||
|
||||
//BOOL awaitingChallenge;
|
||||
|
||||
//NSString *appId;
|
||||
NSString *accessToken;
|
||||
//NSString *nonce;
|
||||
//NSString *method;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@end
|
||||
|
||||
@implementation XMPPXOAuth2Google
|
||||
|
||||
+ (NSString *)mechanismName
|
||||
{
|
||||
return @"X-OAUTH2";
|
||||
}
|
||||
|
||||
- (id)initWithStream:(XMPPStream *)stream password:(NSString *)password
|
||||
{
|
||||
if ((self = [super init]))
|
||||
{
|
||||
xmppStream = stream;
|
||||
xmppStream.hostName = XMPPGoogleTalkHostName;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
-(id)initWithStream:(XMPPStream *)stream accessToken:(NSString *)inAccessToken
|
||||
{
|
||||
if (self = [super init]) {
|
||||
xmppStream = stream;
|
||||
accessToken = inAccessToken;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (BOOL)start:(NSError **)errPtr
|
||||
{
|
||||
if (!accessToken)
|
||||
{
|
||||
NSString *errMsg = @"Missing facebook accessToken.";
|
||||
NSDictionary *info = @{NSLocalizedDescriptionKey : errMsg};
|
||||
|
||||
NSError *err = [NSError errorWithDomain:XMPPStreamErrorDomain code:XMPPStreamInvalidState userInfo:info];
|
||||
|
||||
if (errPtr) *errPtr = err;
|
||||
return NO;
|
||||
}
|
||||
XMPPLogTrace();
|
||||
|
||||
// From RFC 4616 - PLAIN SASL Mechanism:
|
||||
// [authzid] UTF8NUL authcid UTF8NUL passwd
|
||||
//
|
||||
// authzid: authorization identity
|
||||
// authcid: authentication identity (username)
|
||||
// passwd : password for authcid
|
||||
|
||||
NSString *username = [xmppStream.myJID user];
|
||||
|
||||
NSString *payload = [NSString stringWithFormat:@"\0%@\0%@", username, accessToken];
|
||||
NSString *base64 = [[payload dataUsingEncoding:NSUTF8StringEncoding] xmpp_base64Encoded];
|
||||
|
||||
// <auth xmlns="urn:ietf:params:xml:ns:xmpp-sasl" mechanism="PLAIN">Base-64-Info</auth>
|
||||
|
||||
NSXMLElement *auth = [NSXMLElement elementWithName:@"auth" xmlns:@"urn:ietf:params:xml:ns:xmpp-sasl"];
|
||||
[auth addAttributeWithName:@"mechanism" stringValue:@"X-OAUTH2"];
|
||||
[auth addAttributeWithName:@"auth:service" stringValue:@"oauth2"];
|
||||
[auth addAttributeWithName:@"xmlns:auth" stringValue:@"http://www.google.com/talk/protocol/auth"];
|
||||
[auth setStringValue:base64];
|
||||
|
||||
[xmppStream sendAuthElement:auth];
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (XMPPHandleAuthResponse)handleAuth:(NSXMLElement *)authResponse
|
||||
{
|
||||
XMPPLogTrace();
|
||||
|
||||
// We're expecting a success response.
|
||||
// If we get anything else we can safely assume it's the equivalent of a failure response.
|
||||
|
||||
if ([[authResponse name] isEqualToString:@"success"])
|
||||
{
|
||||
return XMPP_AUTH_SUCCESS;
|
||||
}
|
||||
else
|
||||
{
|
||||
return XMPP_AUTH_FAIL;
|
||||
}
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation XMPPStream (XMPPXOAuth2Google)
|
||||
|
||||
|
||||
|
||||
- (BOOL)supportsXOAuth2GoogleAuthentication
|
||||
{
|
||||
return [self supportsAuthenticationMechanism:[XMPPXOAuth2Google mechanismName]];
|
||||
}
|
||||
|
||||
- (BOOL)authenticateWithGoogleAccessToken:(NSString *)accessToken error:(NSError **)errPtr
|
||||
{
|
||||
XMPPLogTrace();
|
||||
|
||||
__block BOOL result = YES;
|
||||
__block NSError *err = nil;
|
||||
|
||||
dispatch_block_t block = ^{ @autoreleasepool {
|
||||
|
||||
if ([self supportsXOAuth2GoogleAuthentication])
|
||||
{
|
||||
XMPPXOAuth2Google * googleAuth = [[XMPPXOAuth2Google alloc] initWithStream:self
|
||||
accessToken:accessToken];
|
||||
|
||||
result = [self authenticate:googleAuth error:&err];
|
||||
}
|
||||
else
|
||||
{
|
||||
NSString *errMsg = @"The server does not support X-OATH2-GOOGLE authentication.";
|
||||
NSDictionary *info = @{NSLocalizedDescriptionKey : errMsg};
|
||||
|
||||
err = [NSError errorWithDomain:XMPPStreamErrorDomain code:XMPPStreamUnsupportedAction userInfo:info];
|
||||
|
||||
result = NO;
|
||||
}
|
||||
}};
|
||||
|
||||
if (dispatch_get_specific(self.xmppQueueTag))
|
||||
block();
|
||||
else
|
||||
dispatch_sync(self.xmppQueue, block);
|
||||
|
||||
if (errPtr)
|
||||
*errPtr = err;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@end
|
||||
93
Authentication/XMPPCustomBinding.h
Normal file
93
Authentication/XMPPCustomBinding.h
Normal file
@ -0,0 +1,93 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
#if TARGET_OS_IPHONE
|
||||
#import "DDXML.h"
|
||||
#endif
|
||||
|
||||
typedef NS_ENUM(NSInteger, XMPPBindResult) {
|
||||
|
||||
XMPP_BIND_CONTINUE, // The custom binding process is still ongoing.
|
||||
|
||||
XMPP_BIND_SUCCESS, // Custom binding succeeded.
|
||||
// The stream should continue normal post-binding operation.
|
||||
|
||||
XMPP_BIND_FAIL_FALLBACK, // Custom binding failed.
|
||||
// The stream should fallback to the standard binding protocol.
|
||||
|
||||
XMPP_BIND_FAIL_ABORT // Custom binding failed.
|
||||
// The stream must abort the binding process.
|
||||
// Further, because the stream is in a bad state (authenticated, but
|
||||
// unable to complete the full handshake) it must immediately disconnect.
|
||||
// The given NSError will be reported via xmppStreamDidDisconnect:withError:
|
||||
};
|
||||
|
||||
/**
|
||||
* Binding a JID resource is a standard part of the authentication process,
|
||||
* and occurs after SASL authentication completes (which generally authenticates the JID username).
|
||||
*
|
||||
* This protocol may be used if there is a need to customize the binding process.
|
||||
* For example:
|
||||
*
|
||||
* - Custom SASL authentication scheme required both username & resource
|
||||
* - Custom SASL authentication scheme provided required resource in server response
|
||||
* - Stream Management (XEP-0198) replaces binding with resumption from previously bound session
|
||||
*
|
||||
* A custom binding procedure may be plugged into an XMPPStream instance via the delegate method:
|
||||
* - (id <XMPPCustomBinding>)xmppStreamWillBind;
|
||||
**/
|
||||
@protocol XMPPCustomBinding <NSObject>
|
||||
@required
|
||||
|
||||
/**
|
||||
* Attempts to start the custom binding process.
|
||||
*
|
||||
* If it isn't possible to start the process (perhaps due to missing information),
|
||||
* this method should return XMPP_BIND_FAIL_FALLBACK or XMPP_BIND_FAIL_ABORT.
|
||||
*
|
||||
* (The error message is only used by xmppStream if this method returns XMPP_BIND_FAIL_ABORT.)
|
||||
*
|
||||
* If binding isn't needed (for example, because custom SASL authentication already handled it),
|
||||
* this method should return XMPP_BIND_SUCCESS.
|
||||
* In this case, xmppStream will immediately move to its post-binding operations.
|
||||
*
|
||||
* Otherwise this method should send whatever stanzas are needed to begin the binding process.
|
||||
* And then return XMPP_BIND_CONTINUE.
|
||||
*
|
||||
* This method is called by automatically XMPPStream.
|
||||
* You MUST NOT invoke this method manually.
|
||||
**/
|
||||
- (XMPPBindResult)start:(NSError **)errPtr;
|
||||
|
||||
/**
|
||||
* After the custom binding process has started, all incoming xmpp stanzas are routed to this method.
|
||||
* The method should process the stanza as appropriate, and return the coresponding result.
|
||||
* If the process is not yet complete, it should return XMPP_BIND_CONTINUE,
|
||||
* meaning the xmpp stream will continue to forward all incoming xmpp stanzas to this method.
|
||||
*
|
||||
* This method is called automatically by XMPPStream.
|
||||
* You MUST NOT invoke this method manually.
|
||||
**/
|
||||
- (XMPPBindResult)handleBind:(NSXMLElement *)auth withError:(NSError **)errPtr;
|
||||
|
||||
@optional
|
||||
|
||||
/**
|
||||
* Optionally implement this method to override the default behavior.
|
||||
* By default behavior, we mean the behavior normally taken by xmppStream, which is:
|
||||
*
|
||||
* - IF the server includes <session xmlns='urn:ietf:params:xml:ns:xmpp-session'/> in its stream:features
|
||||
* - AND xmppStream.skipStartSession property is NOT set
|
||||
* - THEN xmppStream will send the session start request, and await the response before transitioning to authenticated
|
||||
*
|
||||
* Thus if you implement this method and return YES, then xmppStream will skip starting a session,
|
||||
* regardless of the stream:features and the current xmppStream.skipStartSession property value.
|
||||
*
|
||||
* If you implement this method and return NO, then xmppStream will follow the default behavior detailed above.
|
||||
* This means that, even if this method returns NO, the xmppStream may still skip starting a session if
|
||||
* the server doesn't require it via its stream:features,
|
||||
* or if the user has explicitly forbidden it via the xmppStream.skipStartSession property.
|
||||
*
|
||||
* The default value is NO.
|
||||
**/
|
||||
- (BOOL)shouldSkipStartSessionAfterSuccessfulBinding;
|
||||
|
||||
@end
|
||||
102
Authentication/XMPPSASLAuthentication.h
Normal file
102
Authentication/XMPPSASLAuthentication.h
Normal file
@ -0,0 +1,102 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
#if TARGET_OS_IPHONE
|
||||
#import "DDXML.h"
|
||||
#endif
|
||||
|
||||
@class XMPPStream;
|
||||
|
||||
|
||||
typedef NS_ENUM(NSInteger, XMPPHandleAuthResponse) {
|
||||
|
||||
XMPP_AUTH_FAIL, // Authentication failed.
|
||||
// The delegate will be informed via xmppStream:didNotAuthenticate:
|
||||
|
||||
XMPP_AUTH_SUCCESS, // Authentication succeeded.
|
||||
// The delegate will be informed via xmppStreamDidAuthenticate:
|
||||
|
||||
XMPP_AUTH_CONTINUE, // The authentication process is still ongoing.
|
||||
};
|
||||
|
||||
|
||||
@protocol XMPPSASLAuthentication <NSObject>
|
||||
@required
|
||||
|
||||
/**
|
||||
* Returns the associated mechanism name.
|
||||
*
|
||||
* An xmpp server sends a list of supported authentication mechanisms during the xmpp handshake.
|
||||
* The list looks something like this:
|
||||
*
|
||||
* <stream:features>
|
||||
* <mechanisms xmlns="urn:ietf:params:xml:ns:xmpp-sasl">
|
||||
* <mechanism>DIGEST-MD5</mechanism>
|
||||
* <mechanism>X-FACEBOOK-PLATFORM</mechanism>
|
||||
* <mechanism>X-YOUR-CUSTOM-AUTH-SCHEME</mechanism>
|
||||
* </mechanisms>
|
||||
* </stream:features>
|
||||
*
|
||||
* The mechanismName returned should match the value inside the <mechanism>HERE</mechanism>.
|
||||
**/
|
||||
+ (NSString *)mechanismName;
|
||||
|
||||
/**
|
||||
* Standard init method.
|
||||
*
|
||||
* The XMPPStream class natively supports the standard authentication scheme (auth with password).
|
||||
* If that method is used, then xmppStream will automatically create an authentication instance via this method.
|
||||
* Which authentication class it chooses is based on the configured authentication priorities,
|
||||
* and the auth mechanisms supported by the server.
|
||||
*
|
||||
* Not all authentication mechanisms will use this init method.
|
||||
* For example:
|
||||
* - they require an appId and authToken
|
||||
* - they require a userName (not related to JID), privilegeLevel, and password
|
||||
* - they require an eyeScan and voiceFingerprint
|
||||
*
|
||||
* In this case, the authentication mechanism class should provide it's own custom init method.
|
||||
* However it should still implement this method, and then use the start method to notify of errors.
|
||||
**/
|
||||
- (id)initWithStream:(XMPPStream *)stream password:(NSString *)password;
|
||||
|
||||
|
||||
/**
|
||||
* Attempts to start the authentication process.
|
||||
* The auth mechanism should send whatever stanzas are needed to begin the authentication process.
|
||||
*
|
||||
* If it isn't possible to start the authentication process (perhaps due to missing information),
|
||||
* this method should return NO and set an appropriate error message.
|
||||
* For example: "X-Custom-Platform authentication requires authToken"
|
||||
* Otherwise this method should return YES.
|
||||
*
|
||||
* This method is called by automatically XMPPStream (via the authenticate: method).
|
||||
* You should NOT invoke this method manually.
|
||||
**/
|
||||
- (BOOL)start:(NSError **)errPtr;
|
||||
|
||||
/**
|
||||
* After the authentication process has started, all incoming xmpp stanzas are routed to this method.
|
||||
* The authentication mechanism should process the stanza as appropriate, and return the coresponding result.
|
||||
* If the authentication is not yet complete, it should return XMPP_AUTH_CONTINUE,
|
||||
* meaning the xmpp stream will continue to forward all incoming xmpp stanzas to this method.
|
||||
*
|
||||
* This method is called automatically by XMPPStream (via the authenticate: method).
|
||||
* You should NOT invoke this method manually.
|
||||
**/
|
||||
- (XMPPHandleAuthResponse)handleAuth:(NSXMLElement *)auth;
|
||||
|
||||
@optional
|
||||
|
||||
/**
|
||||
* Use this init method if the username used for authentication does not match the user part of the JID.
|
||||
* If username is nil, the user part of the JID will be used.
|
||||
* The standard init method uses this init method, passing nil for the username.
|
||||
**/
|
||||
- (id)initWithStream:(XMPPStream *)stream username:(NSString *)username password:(NSString *)password;
|
||||
|
||||
/**
|
||||
* Optionally implement this method to override the default behavior.
|
||||
* The default value is YES.
|
||||
**/
|
||||
- (BOOL)shouldResendOpeningNegotiationAfterSuccessfulAuthentication;
|
||||
|
||||
@end
|
||||
34
Categories/NSData+XMPP.h
Normal file
34
Categories/NSData+XMPP.h
Normal file
@ -0,0 +1,34 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface NSData (XMPP)
|
||||
|
||||
- (NSData *)xmpp_md5Digest;
|
||||
|
||||
- (NSData *)xmpp_sha1Digest;
|
||||
|
||||
- (NSString *)xmpp_hexStringValue;
|
||||
|
||||
- (NSString *)xmpp_base64Encoded;
|
||||
- (NSData *)xmpp_base64Decoded;
|
||||
|
||||
- (BOOL)xmpp_isJPEG;
|
||||
- (BOOL)xmpp_isPNG;
|
||||
- (NSString *)xmpp_imageType;
|
||||
|
||||
@end
|
||||
|
||||
#ifndef XMPP_EXCLUDE_DEPRECATED
|
||||
|
||||
#define XMPP_DEPRECATED($message) __attribute__((deprecated($message)))
|
||||
|
||||
@interface NSData (XMPPDeprecated)
|
||||
- (NSData *)md5Digest XMPP_DEPRECATED("Use -xmpp_md5Digest");
|
||||
- (NSData *)sha1Digest XMPP_DEPRECATED("Use -xmpp_sha1Digest");
|
||||
- (NSString *)hexStringValue XMPP_DEPRECATED("Use -xmpp_hexStringValue");
|
||||
- (NSString *)base64Encoded XMPP_DEPRECATED("Use -xmpp_base64Encoded");
|
||||
- (NSData *)base64Decoded XMPP_DEPRECATED("Use -xmpp_base64Decoded");
|
||||
@end
|
||||
|
||||
#undef XMPP_DEPRECATED
|
||||
|
||||
#endif
|
||||
240
Categories/NSData+XMPP.m
Normal file
240
Categories/NSData+XMPP.m
Normal file
@ -0,0 +1,240 @@
|
||||
#import "NSData+XMPP.h"
|
||||
#import <CommonCrypto/CommonDigest.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 NSData (XMPP)
|
||||
|
||||
static char encodingTable[64] = {
|
||||
'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
|
||||
'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f',
|
||||
'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',
|
||||
'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/' };
|
||||
|
||||
|
||||
- (NSData *)xmpp_md5Digest
|
||||
{
|
||||
unsigned char result[CC_MD5_DIGEST_LENGTH];
|
||||
|
||||
CC_MD5([self bytes], (CC_LONG)[self length], result);
|
||||
return [NSData dataWithBytes:result length:CC_MD5_DIGEST_LENGTH];
|
||||
}
|
||||
|
||||
- (NSData *)xmpp_sha1Digest
|
||||
{
|
||||
unsigned char result[CC_SHA1_DIGEST_LENGTH];
|
||||
|
||||
CC_SHA1([self bytes], (CC_LONG)[self length], result);
|
||||
return [NSData dataWithBytes:result length:CC_SHA1_DIGEST_LENGTH];
|
||||
}
|
||||
|
||||
- (NSString *)xmpp_hexStringValue
|
||||
{
|
||||
NSMutableString *stringBuffer = [NSMutableString stringWithCapacity:([self length] * 2)];
|
||||
|
||||
const unsigned char *dataBuffer = [self bytes];
|
||||
int i;
|
||||
|
||||
for (i = 0; i < [self length]; ++i)
|
||||
{
|
||||
[stringBuffer appendFormat:@"%02x", (unsigned int)dataBuffer[i]];
|
||||
}
|
||||
|
||||
return [stringBuffer copy];
|
||||
}
|
||||
|
||||
- (NSString *)xmpp_base64Encoded
|
||||
{
|
||||
const unsigned char *bytes = [self bytes];
|
||||
NSMutableString *result = [NSMutableString stringWithCapacity:[self length]];
|
||||
unsigned long ixtext = 0;
|
||||
unsigned long lentext = [self length];
|
||||
long ctremaining = 0;
|
||||
unsigned char inbuf[3], outbuf[4];
|
||||
unsigned short i = 0;
|
||||
unsigned short charsonline = 0, ctcopy = 0;
|
||||
unsigned long ix = 0;
|
||||
|
||||
while( YES )
|
||||
{
|
||||
ctremaining = lentext - ixtext;
|
||||
if( ctremaining <= 0 ) break;
|
||||
|
||||
for( i = 0; i < 3; i++ ) {
|
||||
ix = ixtext + i;
|
||||
if( ix < lentext ) inbuf[i] = bytes[ix];
|
||||
else inbuf [i] = 0;
|
||||
}
|
||||
|
||||
outbuf [0] = (inbuf [0] & 0xFC) >> 2;
|
||||
outbuf [1] = ((inbuf [0] & 0x03) << 4) | ((inbuf [1] & 0xF0) >> 4);
|
||||
outbuf [2] = ((inbuf [1] & 0x0F) << 2) | ((inbuf [2] & 0xC0) >> 6);
|
||||
outbuf [3] = inbuf [2] & 0x3F;
|
||||
ctcopy = 4;
|
||||
|
||||
switch( ctremaining )
|
||||
{
|
||||
case 1:
|
||||
ctcopy = 2;
|
||||
break;
|
||||
case 2:
|
||||
ctcopy = 3;
|
||||
break;
|
||||
}
|
||||
|
||||
for( i = 0; i < ctcopy; i++ )
|
||||
[result appendFormat:@"%c", encodingTable[outbuf[i]]];
|
||||
|
||||
for( i = ctcopy; i < 4; i++ )
|
||||
[result appendString:@"="];
|
||||
|
||||
ixtext += 3;
|
||||
charsonline += 4;
|
||||
}
|
||||
|
||||
return [NSString stringWithString:result];
|
||||
}
|
||||
|
||||
- (NSData *)xmpp_base64Decoded
|
||||
{
|
||||
const unsigned char *bytes = [self bytes];
|
||||
NSMutableData *result = [NSMutableData dataWithCapacity:[self length]];
|
||||
|
||||
unsigned long ixtext = 0;
|
||||
unsigned long lentext = [self length];
|
||||
unsigned char ch = 0;
|
||||
unsigned char inbuf[4] = {0, 0, 0, 0};
|
||||
unsigned char outbuf[3] = {0, 0, 0};
|
||||
short i = 0, ixinbuf = 0;
|
||||
BOOL flignore = NO;
|
||||
BOOL flendtext = NO;
|
||||
|
||||
while( YES )
|
||||
{
|
||||
if( ixtext >= lentext ) break;
|
||||
ch = bytes[ixtext++];
|
||||
flignore = NO;
|
||||
|
||||
if( ( ch >= 'A' ) && ( ch <= 'Z' ) ) ch = ch - 'A';
|
||||
else if( ( ch >= 'a' ) && ( ch <= 'z' ) ) ch = ch - 'a' + 26;
|
||||
else if( ( ch >= '0' ) && ( ch <= '9' ) ) ch = ch - '0' + 52;
|
||||
else if( ch == '+' ) ch = 62;
|
||||
else if( ch == '=' ) flendtext = YES;
|
||||
else if( ch == '/' ) ch = 63;
|
||||
else flignore = YES;
|
||||
|
||||
if( ! flignore )
|
||||
{
|
||||
short ctcharsinbuf = 3;
|
||||
BOOL flbreak = NO;
|
||||
|
||||
if( flendtext )
|
||||
{
|
||||
if( ! ixinbuf ) break;
|
||||
if( ( ixinbuf == 1 ) || ( ixinbuf == 2 ) ) ctcharsinbuf = 1;
|
||||
else ctcharsinbuf = 2;
|
||||
ixinbuf = 3;
|
||||
flbreak = YES;
|
||||
}
|
||||
|
||||
inbuf [ixinbuf++] = ch;
|
||||
|
||||
if( ixinbuf == 4 )
|
||||
{
|
||||
ixinbuf = 0;
|
||||
outbuf [0] = ( inbuf[0] << 2 ) | ( ( inbuf[1] & 0x30) >> 4 );
|
||||
outbuf [1] = ( ( inbuf[1] & 0x0F ) << 4 ) | ( ( inbuf[2] & 0x3C ) >> 2 );
|
||||
outbuf [2] = ( ( inbuf[2] & 0x03 ) << 6 ) | ( inbuf[3] & 0x3F );
|
||||
|
||||
for( i = 0; i < ctcharsinbuf; i++ )
|
||||
[result appendBytes:&outbuf[i] length:1];
|
||||
}
|
||||
|
||||
if( flbreak ) break;
|
||||
}
|
||||
}
|
||||
|
||||
return [NSData dataWithData:result];
|
||||
}
|
||||
|
||||
|
||||
- (BOOL)xmpp_isJPEG
|
||||
{
|
||||
if (self.length > 4)
|
||||
{
|
||||
unsigned char buffer[4];
|
||||
[self getBytes:&buffer length:4];
|
||||
|
||||
return buffer[0]==0xff &&
|
||||
buffer[1]==0xd8 &&
|
||||
buffer[2]==0xff &&
|
||||
buffer[3]==0xe0;
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (BOOL)xmpp_isPNG
|
||||
{
|
||||
if (self.length > 4)
|
||||
{
|
||||
unsigned char buffer[4];
|
||||
[self getBytes:&buffer length:4];
|
||||
|
||||
return buffer[0]==0x89 &&
|
||||
buffer[1]==0x50 &&
|
||||
buffer[2]==0x4e &&
|
||||
buffer[3]==0x47;
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (NSString *)xmpp_imageType
|
||||
{
|
||||
NSString *result = nil;
|
||||
|
||||
if([self xmpp_isPNG])
|
||||
{
|
||||
result = @"image/png";
|
||||
}
|
||||
else if([self xmpp_isJPEG])
|
||||
{
|
||||
result = @"image/jpeg";
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#ifndef XMPP_EXCLUDE_DEPRECATED
|
||||
|
||||
@implementation NSData (XMPPDeprecated)
|
||||
|
||||
- (NSData *)md5Digest {
|
||||
return [self xmpp_md5Digest];
|
||||
}
|
||||
|
||||
- (NSData *)sha1Digest {
|
||||
return [self xmpp_sha1Digest];
|
||||
}
|
||||
|
||||
- (NSString *)hexStringValue {
|
||||
return [self xmpp_hexStringValue];
|
||||
}
|
||||
|
||||
- (NSString *)base64Encoded {
|
||||
return [self xmpp_base64Encoded];
|
||||
}
|
||||
|
||||
- (NSData *)base64Decoded {
|
||||
return [self xmpp_base64Decoded];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#endif
|
||||
46
Categories/NSNumber+XMPP.h
Normal file
46
Categories/NSNumber+XMPP.h
Normal file
@ -0,0 +1,46 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
|
||||
@interface NSNumber (XMPP)
|
||||
|
||||
+ (NSNumber *)xmpp_numberWithPtr:(const void *)ptr;
|
||||
- (id)xmpp_initWithPtr:(const void *)ptr __attribute__((objc_method_family(init)));
|
||||
|
||||
+ (BOOL)xmpp_parseString:(NSString *)str intoInt32:(int32_t *)pNum;
|
||||
+ (BOOL)xmpp_parseString:(NSString *)str intoUInt32:(uint32_t *)pNum;
|
||||
|
||||
+ (BOOL)xmpp_parseString:(NSString *)str intoInt64:(int64_t *)pNum;
|
||||
+ (BOOL)xmpp_parseString:(NSString *)str intoUInt64:(uint64_t *)pNum;
|
||||
|
||||
+ (BOOL)xmpp_parseString:(NSString *)str intoNSInteger:(NSInteger *)pNum;
|
||||
+ (BOOL)xmpp_parseString:(NSString *)str intoNSUInteger:(NSUInteger *)pNum;
|
||||
|
||||
+ (UInt8)xmpp_extractUInt8FromData:(NSData *)data atOffset:(unsigned int)offset;
|
||||
|
||||
+ (UInt16)xmpp_extractUInt16FromData:(NSData *)data atOffset:(unsigned int)offset andConvertFromNetworkOrder:(BOOL)flag;
|
||||
|
||||
+ (UInt32)xmpp_extractUInt32FromData:(NSData *)data atOffset:(unsigned int)offset andConvertFromNetworkOrder:(BOOL)flag;
|
||||
|
||||
@end
|
||||
|
||||
#ifndef XMPP_EXCLUDE_DEPRECATED
|
||||
|
||||
#define XMPP_DEPRECATED($message) __attribute__((deprecated($message)))
|
||||
|
||||
@interface NSNumber (XMPPDeprecated)
|
||||
+ (NSNumber *)numberWithPtr:(const void *)ptr XMPP_DEPRECATED("Use +xmpp_numberWithPtr:");
|
||||
- (id)initWithPtr:(const void *)ptr XMPP_DEPRECATED("Use -xmpp_initWithPtr:");
|
||||
+ (BOOL)parseString:(NSString *)str intoInt32:(int32_t *)pNum XMPP_DEPRECATED("Use +xmpp_parseString:intoInt32:");
|
||||
+ (BOOL)parseString:(NSString *)str intoUInt32:(uint32_t *)pNum XMPP_DEPRECATED("Use +xmpp_parseString:intoUInt32:");
|
||||
+ (BOOL)parseString:(NSString *)str intoInt64:(int64_t *)pNum XMPP_DEPRECATED("Use +xmpp_parseString:intoInt64:");
|
||||
+ (BOOL)parseString:(NSString *)str intoUInt64:(uint64_t *)pNum XMPP_DEPRECATED("Use +xmpp_parseString:intoUInt64:");
|
||||
+ (BOOL)parseString:(NSString *)str intoNSInteger:(NSInteger *)pNum XMPP_DEPRECATED("Use +xmpp_parseString:intoNSInteger:");
|
||||
+ (BOOL)parseString:(NSString *)str intoNSUInteger:(NSUInteger *)pNum XMPP_DEPRECATED("Use +xmpp_parseString:intoNSUInteger:");
|
||||
+ (UInt8)extractUInt8FromData:(NSData *)data atOffset:(unsigned int)offset XMPP_DEPRECATED("Use +xmpp_extractUInt8FromData:atOffset:");
|
||||
+ (UInt16)extractUInt16FromData:(NSData *)data atOffset:(unsigned int)offset andConvertFromNetworkOrder:(BOOL)flag XMPP_DEPRECATED("Use +xmpp_extractUInt16FromData:atOffset:andConvertFromNetworkOrder:");
|
||||
+ (UInt32)extractUInt32FromData:(NSData *)data atOffset:(unsigned int)offset andConvertFromNetworkOrder:(BOOL)flag XMPP_DEPRECATED("Use +xmpp_extractUInt32FromData:atOffset:andConvertFromNetworkOrder:");
|
||||
@end
|
||||
|
||||
#undef XMPP_DEPRECATED
|
||||
|
||||
#endif
|
||||
265
Categories/NSNumber+XMPP.m
Normal file
265
Categories/NSNumber+XMPP.m
Normal file
@ -0,0 +1,265 @@
|
||||
#import "NSNumber+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 NSNumber (XMPP)
|
||||
|
||||
+ (NSNumber *)xmpp_numberWithPtr:(const void *)ptr
|
||||
{
|
||||
return [[NSNumber alloc] xmpp_initWithPtr:ptr];
|
||||
}
|
||||
|
||||
- (id)xmpp_initWithPtr:(const void *)ptr
|
||||
{
|
||||
return [self initWithLong:(long)ptr];
|
||||
}
|
||||
|
||||
+ (BOOL)xmpp_parseString:(NSString *)str intoInt32:(int32_t *)pNum
|
||||
{
|
||||
if (str == nil)
|
||||
{
|
||||
*pNum = (int32_t)0;
|
||||
return NO;
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
|
||||
long result = strtol([str UTF8String], NULL, 10);
|
||||
|
||||
if (LONG_BIT != 32)
|
||||
{
|
||||
if (result > INT32_MAX)
|
||||
{
|
||||
*pNum = INT32_MAX;
|
||||
return NO;
|
||||
}
|
||||
if (result < INT32_MIN)
|
||||
{
|
||||
*pNum = INT32_MIN;
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
|
||||
// From the manpage:
|
||||
//
|
||||
// If no conversion could be performed, 0 is returned and the global variable errno is set to EINVAL.
|
||||
// If an overflow or underflow occurs, errno is set to ERANGE and the function return value is clamped.
|
||||
//
|
||||
// Clamped means it will be TYPE_MAX or TYPE_MIN.
|
||||
// If overflow/underflow occurs, returning a clamped value is more accurate then returning zero.
|
||||
|
||||
*pNum = (int32_t)result;
|
||||
|
||||
if (errno != 0)
|
||||
return NO;
|
||||
else
|
||||
return YES;
|
||||
}
|
||||
|
||||
+ (BOOL)xmpp_parseString:(NSString *)str intoUInt32:(uint32_t *)pNum
|
||||
{
|
||||
if (str == nil)
|
||||
{
|
||||
*pNum = (uint32_t)0;
|
||||
return NO;
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
|
||||
unsigned long result = strtoul([str UTF8String], NULL, 10);
|
||||
|
||||
if (LONG_BIT != 32)
|
||||
{
|
||||
if (result > UINT32_MAX)
|
||||
{
|
||||
*pNum = UINT32_MAX;
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
|
||||
// From the manpage:
|
||||
//
|
||||
// If no conversion could be performed, 0 is returned and the global variable errno is set to EINVAL.
|
||||
// If an overflow or underflow occurs, errno is set to ERANGE and the function return value is clamped.
|
||||
//
|
||||
// Clamped means it will be TYPE_MAX or TYPE_MIN.
|
||||
// If overflow/underflow occurs, returning a clamped value is more accurate then returning zero.
|
||||
|
||||
*pNum = (uint32_t)result;
|
||||
|
||||
if (errno != 0)
|
||||
return NO;
|
||||
else
|
||||
return YES;
|
||||
}
|
||||
|
||||
+ (BOOL)xmpp_parseString:(NSString *)str intoInt64:(int64_t *)pNum
|
||||
{
|
||||
if (str == nil)
|
||||
{
|
||||
*pNum = (int64_t)0;
|
||||
return NO;
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
|
||||
// On both 32-bit and 64-bit machines, long long = 64 bit
|
||||
|
||||
*pNum = strtoll([str UTF8String], NULL, 10);
|
||||
|
||||
// From the manpage:
|
||||
//
|
||||
// If no conversion could be performed, 0 is returned and the global variable errno is set to EINVAL.
|
||||
// If an overflow or underflow occurs, errno is set to ERANGE and the function return value is clamped.
|
||||
//
|
||||
// Clamped means it will be TYPE_MAX or TYPE_MIN.
|
||||
// If overflow/underflow occurs, returning a clamped value is more accurate then returning zero.
|
||||
|
||||
if (errno != 0)
|
||||
return NO;
|
||||
else
|
||||
return YES;
|
||||
}
|
||||
|
||||
+ (BOOL)xmpp_parseString:(NSString *)str intoUInt64:(uint64_t *)pNum
|
||||
{
|
||||
if (str == nil)
|
||||
{
|
||||
*pNum = (uint64_t)0;
|
||||
return NO;
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
|
||||
// On both 32-bit and 64-bit machines, unsigned long long = 64 bit
|
||||
|
||||
*pNum = strtoull([str UTF8String], NULL, 10);
|
||||
|
||||
// From the manpage:
|
||||
//
|
||||
// If no conversion could be performed, 0 is returned and the global variable errno is set to EINVAL.
|
||||
// If an overflow or underflow occurs, errno is set to ERANGE and the function return value is clamped.
|
||||
//
|
||||
// Clamped means it will be TYPE_MAX or TYPE_MIN.
|
||||
// If overflow/underflow occurs, returning a clamped value is more accurate then returning zero.
|
||||
|
||||
if (errno != 0)
|
||||
return NO;
|
||||
else
|
||||
return YES;
|
||||
}
|
||||
|
||||
+ (BOOL)xmpp_parseString:(NSString *)str intoNSInteger:(NSInteger *)pNum
|
||||
{
|
||||
if (NSIntegerMax == INT32_MAX)
|
||||
return [self xmpp_parseString:str intoInt32:(int32_t *)pNum];
|
||||
else
|
||||
return [self xmpp_parseString:str intoInt64:(int64_t *)pNum];
|
||||
}
|
||||
|
||||
+ (BOOL)xmpp_parseString:(NSString *)str intoNSUInteger:(NSUInteger *)pNum
|
||||
{
|
||||
if (NSUIntegerMax == UINT32_MAX)
|
||||
return [self xmpp_parseString:str intoUInt32:(uint32_t *)pNum];
|
||||
else
|
||||
return [self xmpp_parseString:str intoUInt64:(uint64_t *)pNum];
|
||||
}
|
||||
|
||||
+ (UInt8)xmpp_extractUInt8FromData:(NSData *)data atOffset:(unsigned int)offset
|
||||
{
|
||||
// 8 bits = 1 byte
|
||||
|
||||
if([data length] < offset + 1) return 0;
|
||||
|
||||
UInt8 *pResult = (UInt8 *)([data bytes] + offset);
|
||||
UInt8 result = *pResult;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
+ (UInt16)xmpp_extractUInt16FromData:(NSData *)data atOffset:(unsigned int)offset andConvertFromNetworkOrder:(BOOL)flag
|
||||
{
|
||||
// 16 bits = 2 bytes
|
||||
|
||||
if([data length] < offset + 2) return 0;
|
||||
|
||||
UInt16 *pResult = (UInt16 *)([data bytes] + offset);
|
||||
UInt16 result = *pResult;
|
||||
|
||||
if(flag)
|
||||
return ntohs(result);
|
||||
else
|
||||
return result;
|
||||
}
|
||||
|
||||
+ (UInt32)xmpp_extractUInt32FromData:(NSData *)data atOffset:(unsigned int)offset andConvertFromNetworkOrder:(BOOL)flag
|
||||
{
|
||||
// 32 bits = 4 bytes
|
||||
|
||||
if([data length] < offset + 4) return 0;
|
||||
|
||||
UInt32 *pResult = (UInt32 *)([data bytes] + offset);
|
||||
UInt32 result = *pResult;
|
||||
|
||||
if(flag)
|
||||
return ntohl(result);
|
||||
else
|
||||
return result;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#ifndef XMPP_EXCLUDE_DEPRECATED
|
||||
|
||||
@implementation NSNumber (XMPPDeprecated)
|
||||
|
||||
+ (NSNumber *)numberWithPtr:(const void *)ptr {
|
||||
return [self xmpp_numberWithPtr:ptr];
|
||||
}
|
||||
|
||||
- (id)initWithPtr:(const void *)ptr {
|
||||
return [self xmpp_initWithPtr:ptr];
|
||||
}
|
||||
|
||||
+ (BOOL)parseString:(NSString *)str intoInt32:(int32_t *)pNum {
|
||||
return [self xmpp_parseString:str intoInt32:pNum];
|
||||
}
|
||||
|
||||
+ (BOOL)parseString:(NSString *)str intoUInt32:(uint32_t *)pNum {
|
||||
return [self xmpp_parseString:str intoUInt32:pNum];
|
||||
}
|
||||
|
||||
+ (BOOL)parseString:(NSString *)str intoInt64:(int64_t *)pNum {
|
||||
return [self xmpp_parseString:str intoInt64:pNum];
|
||||
}
|
||||
|
||||
+ (BOOL)parseString:(NSString *)str intoUInt64:(uint64_t *)pNum {
|
||||
return [self xmpp_parseString:str intoUInt64:pNum];
|
||||
}
|
||||
|
||||
+ (BOOL)parseString:(NSString *)str intoNSInteger:(NSInteger *)pNum {
|
||||
return [self xmpp_parseString:str intoNSInteger:pNum];
|
||||
}
|
||||
|
||||
+ (BOOL)parseString:(NSString *)str intoNSUInteger:(NSUInteger *)pNum {
|
||||
return [self xmpp_parseString:str intoNSUInteger:pNum];
|
||||
}
|
||||
|
||||
+ (UInt8)extractUInt8FromData:(NSData *)data atOffset:(unsigned int)offset {
|
||||
return [self xmpp_extractUInt8FromData:data atOffset:offset];
|
||||
}
|
||||
|
||||
+ (UInt16)extractUInt16FromData:(NSData *)data atOffset:(unsigned int)offset andConvertFromNetworkOrder:(BOOL)flag {
|
||||
return [self xmpp_extractUInt16FromData:data atOffset:offset andConvertFromNetworkOrder:flag];
|
||||
}
|
||||
|
||||
+ (UInt32)extractUInt32FromData:(NSData *)data atOffset:(unsigned int)offset andConvertFromNetworkOrder:(BOOL)flag {
|
||||
return [self xmpp_extractUInt32FromData:data atOffset:offset andConvertFromNetworkOrder:flag];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#endif
|
||||
157
Categories/NSXMLElement+XMPP.h
Normal file
157
Categories/NSXMLElement+XMPP.h
Normal file
@ -0,0 +1,157 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
#import "DDXML.h"
|
||||
#endif
|
||||
|
||||
|
||||
@interface NSXMLElement (XMPP)
|
||||
|
||||
/**
|
||||
* Convenience methods for Creating elements.
|
||||
**/
|
||||
|
||||
+ (NSXMLElement *)elementWithName:(NSString *)name numberValue:(NSNumber *)number;
|
||||
- (id)initWithName:(NSString *)name numberValue:(NSNumber *)number;
|
||||
|
||||
+ (NSXMLElement *)elementWithName:(NSString *)name objectValue:(id)objectValue;
|
||||
- (id)initWithName:(NSString *)name objectValue:(id)objectValue;
|
||||
|
||||
/**
|
||||
* Creating elements with explicit xmlns values.
|
||||
*
|
||||
* Use these instead of [NSXMLElement initWithName:URI:].
|
||||
* The category methods below are more readable, and they actually work.
|
||||
**/
|
||||
|
||||
+ (NSXMLElement *)elementWithName:(NSString *)name xmlns:(NSString *)ns;
|
||||
- (id)initWithName:(NSString *)name xmlns:(NSString *)ns;
|
||||
|
||||
/**
|
||||
* Extracting multiple elements.
|
||||
**/
|
||||
|
||||
- (NSArray *)elementsForXmlns:(NSString *)ns;
|
||||
- (NSArray *)elementsForXmlnsPrefix:(NSString *)nsPrefix;
|
||||
|
||||
/**
|
||||
* Extracting a single element.
|
||||
**/
|
||||
|
||||
- (NSXMLElement *)elementForName:(NSString *)name;
|
||||
- (NSXMLElement *)elementForName:(NSString *)name xmlns:(NSString *)xmlns;
|
||||
- (NSXMLElement *)elementForName:(NSString *)name xmlnsPrefix:(NSString *)xmlnsPrefix;
|
||||
|
||||
/**
|
||||
* Convenience methods for removing child elements.
|
||||
*
|
||||
* If the element doesn't exist, these methods do nothing.
|
||||
**/
|
||||
|
||||
- (void)removeElementForName:(NSString *)name;
|
||||
- (void)removeElementsForName:(NSString *)name;
|
||||
- (void)removeElementForName:(NSString *)name xmlns:(NSString *)xmlns;
|
||||
- (void)removeElementForName:(NSString *)name xmlnsPrefix:(NSString *)xmlnsPrefix;
|
||||
|
||||
/**
|
||||
* Working with the common xmpp xmlns value.
|
||||
*
|
||||
* Use these instead of getting/setting the URI.
|
||||
* The category methods below are more readable, and they actually work.
|
||||
**/
|
||||
|
||||
- (NSString *)xmlns;
|
||||
- (void)setXmlns:(NSString *)ns;
|
||||
|
||||
/**
|
||||
* Convenience methods for printing xml elements with different styles.
|
||||
**/
|
||||
|
||||
- (NSString *)prettyXMLString;
|
||||
- (NSString *)compactXMLString;
|
||||
|
||||
/**
|
||||
* Convenience methods for adding attributes.
|
||||
**/
|
||||
|
||||
- (void)addAttributeWithName:(NSString *)name intValue:(int)intValue;
|
||||
- (void)addAttributeWithName:(NSString *)name boolValue:(BOOL)boolValue;
|
||||
- (void)addAttributeWithName:(NSString *)name floatValue:(float)floatValue;
|
||||
- (void)addAttributeWithName:(NSString *)name doubleValue:(double)doubleValue;
|
||||
- (void)addAttributeWithName:(NSString *)name integerValue:(NSInteger)integerValue;
|
||||
- (void)addAttributeWithName:(NSString *)name unsignedIntegerValue:(NSUInteger)unsignedIntegerValue;
|
||||
- (void)addAttributeWithName:(NSString *)name stringValue:(NSString *)string;
|
||||
- (void)addAttributeWithName:(NSString *)name numberValue:(NSNumber *)number;
|
||||
- (void)addAttributeWithName:(NSString *)name objectValue:(id)objectValue;
|
||||
|
||||
/**
|
||||
* Convenience methods for extracting attribute values in different formats.
|
||||
*
|
||||
* E.g. <beer name="guinness" price="4.50"/> // float price = [beer attributeFloatValueForName:@"price"];
|
||||
**/
|
||||
|
||||
- (int)attributeIntValueForName:(NSString *)name;
|
||||
- (BOOL)attributeBoolValueForName:(NSString *)name;
|
||||
- (float)attributeFloatValueForName:(NSString *)name;
|
||||
- (double)attributeDoubleValueForName:(NSString *)name;
|
||||
- (int32_t)attributeInt32ValueForName:(NSString *)name;
|
||||
- (uint32_t)attributeUInt32ValueForName:(NSString *)name;
|
||||
- (int64_t)attributeInt64ValueForName:(NSString *)name;
|
||||
- (uint64_t)attributeUInt64ValueForName:(NSString *)name;
|
||||
- (NSInteger)attributeIntegerValueForName:(NSString *)name;
|
||||
- (NSUInteger)attributeUnsignedIntegerValueForName:(NSString *)name;
|
||||
- (NSString *)attributeStringValueForName:(NSString *)name;
|
||||
- (NSNumber *)attributeNumberIntValueForName:(NSString *)name;
|
||||
- (NSNumber *)attributeNumberBoolValueForName:(NSString *)name;
|
||||
- (NSNumber *)attributeNumberFloatValueForName:(NSString *)name;
|
||||
- (NSNumber *)attributeNumberDoubleValueForName:(NSString *)name;
|
||||
- (NSNumber *)attributeNumberInt32ValueForName:(NSString *)name;
|
||||
- (NSNumber *)attributeNumberUInt32ValueForName:(NSString *)name;
|
||||
- (NSNumber *)attributeNumberInt64ValueForName:(NSString *)name;
|
||||
- (NSNumber *)attributeNumberUInt64ValueForName:(NSString *)name;
|
||||
- (NSNumber *)attributeNumberIntegerValueForName:(NSString *)name;
|
||||
- (NSNumber *)attributeNumberUnsignedIntegerValueForName:(NSString *)name;
|
||||
|
||||
- (int)attributeIntValueForName:(NSString *)name withDefaultValue:(int)defaultValue;
|
||||
- (BOOL)attributeBoolValueForName:(NSString *)name withDefaultValue:(BOOL)defaultValue;
|
||||
- (float)attributeFloatValueForName:(NSString *)name withDefaultValue:(float)defaultValue;
|
||||
- (double)attributeDoubleValueForName:(NSString *)name withDefaultValue:(double)defaultValue;
|
||||
- (int32_t)attributeInt32ValueForName:(NSString *)name withDefaultValue:(int32_t)defaultValue;
|
||||
- (uint32_t)attributeUInt32ValueForName:(NSString *)name withDefaultValue:(uint32_t)defaultValue;
|
||||
- (int64_t)attributeInt64ValueForName:(NSString *)name withDefaultValue:(int64_t)defaultValue;
|
||||
- (uint64_t)attributeUInt64ValueForName:(NSString *)name withDefaultValue:(uint64_t)defaultValue;
|
||||
- (NSInteger)attributeIntegerValueForName:(NSString *)name withDefaultValue:(NSInteger)defaultValue;
|
||||
- (NSUInteger)attributeUnsignedIntegerValueForName:(NSString *)name withDefaultValue:(NSUInteger)defaultValue;
|
||||
- (NSString *)attributeStringValueForName:(NSString *)name withDefaultValue:(NSString *)defaultValue;
|
||||
- (NSNumber *)attributeNumberIntValueForName:(NSString *)name withDefaultValue:(int)defaultValue;
|
||||
- (NSNumber *)attributeNumberBoolValueForName:(NSString *)name withDefaultValue:(BOOL)defaultValue;
|
||||
|
||||
- (NSMutableDictionary *)attributesAsDictionary;
|
||||
|
||||
/**
|
||||
* Convenience methods for extracting element values in different formats.
|
||||
*
|
||||
* E.g. <price>9.99</price> // float price = [priceElement stringValueAsFloat];
|
||||
**/
|
||||
|
||||
- (int)stringValueAsInt;
|
||||
- (BOOL)stringValueAsBool;
|
||||
- (float)stringValueAsFloat;
|
||||
- (double)stringValueAsDouble;
|
||||
- (int32_t)stringValueAsInt32;
|
||||
- (uint32_t)stringValueAsUInt32;
|
||||
- (int64_t)stringValueAsInt64;
|
||||
- (uint64_t)stringValueAsUInt64;
|
||||
- (NSInteger)stringValueAsNSInteger;
|
||||
- (NSUInteger)stringValueAsNSUInteger;
|
||||
|
||||
/**
|
||||
* Working with namespaces.
|
||||
**/
|
||||
|
||||
- (void)addNamespaceWithPrefix:(NSString *)prefix stringValue:(NSString *)string;
|
||||
|
||||
- (NSString *)namespaceStringValueForPrefix:(NSString *)prefix;
|
||||
- (NSString *)namespaceStringValueForPrefix:(NSString *)prefix withDefaultValue:(NSString *)defaultValue;
|
||||
|
||||
@end
|
||||
663
Categories/NSXMLElement+XMPP.m
Normal file
663
Categories/NSXMLElement+XMPP.m
Normal file
@ -0,0 +1,663 @@
|
||||
#import "NSXMLElement+XMPP.h"
|
||||
#import "NSNumber+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 NSXMLElement (XMPP)
|
||||
|
||||
/**
|
||||
* Convenience methods for Creating elements.
|
||||
**/
|
||||
|
||||
+ (NSXMLElement *)elementWithName:(NSString *)name numberValue:(NSNumber *)number
|
||||
{
|
||||
return [self elementWithName:name stringValue:[number stringValue]];
|
||||
}
|
||||
|
||||
- (id)initWithName:(NSString *)name numberValue:(NSNumber *)number
|
||||
{
|
||||
return [self initWithName:name stringValue:[number stringValue]];
|
||||
}
|
||||
|
||||
+ (NSXMLElement *)elementWithName:(NSString *)name objectValue:(id)objectValue
|
||||
{
|
||||
if([objectValue isKindOfClass:[NSString class]])
|
||||
{
|
||||
return [self elementWithName:name stringValue:objectValue];
|
||||
}
|
||||
else if([objectValue isKindOfClass:[NSNumber class]])
|
||||
{
|
||||
return [self elementWithName:name numberValue:objectValue];
|
||||
}
|
||||
else if([objectValue respondsToSelector:@selector(stringValue)])
|
||||
{
|
||||
return [self elementWithName:name stringValue:[objectValue stringValue]];
|
||||
}
|
||||
else
|
||||
{
|
||||
return [self elementWithName:name];
|
||||
}
|
||||
}
|
||||
|
||||
- (id)initWithName:(NSString *)name objectValue:(id)objectValue
|
||||
{
|
||||
if([objectValue isKindOfClass:[NSString class]])
|
||||
{
|
||||
return [self initWithName:name stringValue:objectValue];
|
||||
}
|
||||
else if([objectValue isKindOfClass:[NSNumber class]])
|
||||
{
|
||||
return [self initWithName:name numberValue:objectValue];
|
||||
}
|
||||
else if([objectValue respondsToSelector:@selector(stringValue)])
|
||||
{
|
||||
return [self initWithName:name stringValue:[objectValue stringValue]];
|
||||
}
|
||||
else
|
||||
{
|
||||
return [self initWithName:name];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Quick method to create an element
|
||||
**/
|
||||
+ (NSXMLElement *)elementWithName:(NSString *)name xmlns:(NSString *)ns
|
||||
{
|
||||
NSXMLElement *element = [NSXMLElement elementWithName:name];
|
||||
[element setXmlns:ns];
|
||||
return element;
|
||||
}
|
||||
|
||||
- (id)initWithName:(NSString *)name xmlns:(NSString *)ns
|
||||
{
|
||||
if ((self = [self initWithName:name]))
|
||||
{
|
||||
[self setXmlns:ns];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSArray *)elementsForXmlns:(NSString *)ns
|
||||
{
|
||||
NSMutableArray *elements = [NSMutableArray array];
|
||||
|
||||
for (NSXMLNode *node in [self children])
|
||||
{
|
||||
if ([node isKindOfClass:[NSXMLElement class]])
|
||||
{
|
||||
NSXMLElement *element = (NSXMLElement *)node;
|
||||
|
||||
if ([[element xmlns] isEqual:ns])
|
||||
{
|
||||
[elements addObject:element];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return elements;
|
||||
}
|
||||
|
||||
- (NSArray *)elementsForXmlnsPrefix:(NSString *)nsPrefix
|
||||
{
|
||||
NSMutableArray *elements = [NSMutableArray array];
|
||||
|
||||
for (NSXMLNode *node in [self children])
|
||||
{
|
||||
if ([node isKindOfClass:[NSXMLElement class]])
|
||||
{
|
||||
NSXMLElement *element = (NSXMLElement *)node;
|
||||
|
||||
if ([[element xmlns] hasPrefix:nsPrefix])
|
||||
{
|
||||
[elements addObject:element];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return elements;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns the first child element for the given name (as an NSXMLElement).
|
||||
* If no child elements exist for the given name, nil is returned.
|
||||
**/
|
||||
- (NSXMLElement *)elementForName:(NSString *)name
|
||||
{
|
||||
NSArray *elements = [self elementsForName:name];
|
||||
if ([elements count] > 0)
|
||||
{
|
||||
return elements[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
// There is a bug in the NSXMLElement elementsForName: method.
|
||||
// Consider the following XML fragment:
|
||||
//
|
||||
// <query xmlns="jabber:iq:private">
|
||||
// <x xmlns="some:other:namespace"></x>
|
||||
// </query>
|
||||
//
|
||||
// Calling [query elementsForName:@"x"] results in an empty array!
|
||||
//
|
||||
// However, it will work properly if you use the following:
|
||||
// [query elementsForLocalName:@"x" URI:@"some:other:namespace"]
|
||||
//
|
||||
// The trouble with this is that we may not always know the xmlns in advance,
|
||||
// so in this particular case there is no way to access the element without looping through the children.
|
||||
//
|
||||
// This bug was submitted to apple on June 1st, 2007 and was classified as "serious".
|
||||
//
|
||||
// --!!-- This bug does NOT exist in DDXML --!!--
|
||||
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns the first child element for the given name and given xmlns (as an NSXMLElement).
|
||||
* If no child elements exist for the given name and given xmlns, nil is returned.
|
||||
**/
|
||||
- (NSXMLElement *)elementForName:(NSString *)name xmlns:(NSString *)xmlns
|
||||
{
|
||||
NSArray *elements = [self elementsForLocalName:name URI:xmlns];
|
||||
if ([elements count] > 0)
|
||||
{
|
||||
return elements[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (NSXMLElement *)elementForName:(NSString *)name xmlnsPrefix:(NSString *)xmlnsPrefix{
|
||||
|
||||
NSXMLElement *result = nil;
|
||||
|
||||
for (NSXMLNode *node in [self children])
|
||||
{
|
||||
if ([node isKindOfClass:[NSXMLElement class]])
|
||||
{
|
||||
NSXMLElement *element = (NSXMLElement *)node;
|
||||
|
||||
if ([[element name] isEqualToString:name] && [[element xmlns] hasPrefix:xmlnsPrefix])
|
||||
{
|
||||
result = element;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method removes the first child element for the given name.
|
||||
* If no child elements exist for the given name, this method does nothing.
|
||||
**/
|
||||
- (void)removeElementForName:(NSString *)name
|
||||
{
|
||||
NSXMLElement *element = [self elementForName:name];
|
||||
|
||||
if(element)
|
||||
{
|
||||
[self removeChildAtIndex:[[self children] indexOfObject:element]];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method removes the all child elements for the given name.
|
||||
* If no child elements exist for the given name, this method does nothing.
|
||||
**/
|
||||
- (void)removeElementsForName:(NSString *)name
|
||||
{
|
||||
NSArray *elements = [self elementsForName:name];
|
||||
|
||||
for(NSXMLElement *element in elements)
|
||||
{
|
||||
[self removeChildAtIndex:[[self children] indexOfObject:element]];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method removes the first child element for the given name and given xmlns.
|
||||
* If no child elements exist for the given name and given xmlns, this method does nothing.
|
||||
**/
|
||||
- (void)removeElementForName:(NSString *)name xmlns:(NSString *)xmlns
|
||||
{
|
||||
NSXMLElement *element = [self elementForName:name xmlns:xmlns];
|
||||
|
||||
if(element)
|
||||
{
|
||||
[self removeChildAtIndex:[[self children] indexOfObject:element]];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method removes the first child element for the given name and given xmlns prefix.
|
||||
* If no child elements exist for the given name and given xmlns prefix, this method does nothing.
|
||||
**/
|
||||
- (void)removeElementForName:(NSString *)name xmlnsPrefix:(NSString *)xmlnsPrefix
|
||||
{
|
||||
NSXMLElement *element = [self elementForName:name xmlnsPrefix:xmlnsPrefix];
|
||||
|
||||
if(element)
|
||||
{
|
||||
[self removeChildAtIndex:[[self children] indexOfObject:element]];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the common xmlns "attribute", which is only accessible via the namespace methods.
|
||||
* The xmlns value is often used in jabber elements.
|
||||
**/
|
||||
- (NSString *)xmlns
|
||||
{
|
||||
return [[self namespaceForPrefix:@""] stringValue];
|
||||
}
|
||||
|
||||
- (void)setXmlns:(NSString *)ns
|
||||
{
|
||||
// If we use setURI: then the xmlns won't be displayed in the XMLString.
|
||||
// Adding the namespace this way works properly.
|
||||
|
||||
[self addNamespace:[NSXMLNode namespaceWithName:@"" stringValue:ns]];
|
||||
}
|
||||
|
||||
/**
|
||||
* Shortcut to get a pretty (formatted) string representation of the element.
|
||||
**/
|
||||
- (NSString *)prettyXMLString
|
||||
{
|
||||
return [self XMLStringWithOptions:(NSXMLNodePrettyPrint | NSXMLNodeCompactEmptyElement)];
|
||||
}
|
||||
|
||||
/**
|
||||
* Shortcut to get a compact string representation of the element.
|
||||
**/
|
||||
- (NSString *)compactXMLString
|
||||
{
|
||||
return [self XMLStringWithOptions:NSXMLNodeCompactEmptyElement];
|
||||
}
|
||||
|
||||
/**
|
||||
* Shortcut to avoid having to use NSXMLNode everytime
|
||||
**/
|
||||
|
||||
- (void)addAttributeWithName:(NSString *)name intValue:(int)intValue
|
||||
{
|
||||
[self addAttributeWithName:name numberValue:@(intValue)];
|
||||
}
|
||||
|
||||
- (void)addAttributeWithName:(NSString *)name boolValue:(BOOL)boolValue
|
||||
{
|
||||
[self addAttributeWithName:name numberValue:@(boolValue)];
|
||||
}
|
||||
|
||||
- (void)addAttributeWithName:(NSString *)name floatValue:(float)floatValue
|
||||
{
|
||||
[self addAttributeWithName:name numberValue:@(floatValue)];
|
||||
}
|
||||
|
||||
- (void)addAttributeWithName:(NSString *)name doubleValue:(double)doubleValue
|
||||
{
|
||||
[self addAttributeWithName:name numberValue:@(doubleValue)];
|
||||
}
|
||||
|
||||
- (void)addAttributeWithName:(NSString *)name integerValue:(NSInteger)integerValue
|
||||
{
|
||||
[self addAttributeWithName:name numberValue:@(integerValue)];
|
||||
}
|
||||
|
||||
- (void)addAttributeWithName:(NSString *)name unsignedIntegerValue:(NSUInteger)unsignedIntegerValue
|
||||
{
|
||||
[self addAttributeWithName:name numberValue:@(unsignedIntegerValue)];
|
||||
}
|
||||
|
||||
- (void)addAttributeWithName:(NSString *)name stringValue:(NSString *)string
|
||||
{
|
||||
[self addAttribute:[NSXMLNode attributeWithName:name stringValue:string]];
|
||||
}
|
||||
|
||||
- (void)addAttributeWithName:(NSString *)name numberValue:(NSNumber *)number
|
||||
{
|
||||
[self addAttributeWithName:name stringValue:[number stringValue]];
|
||||
}
|
||||
|
||||
- (void)addAttributeWithName:(NSString *)name objectValue:(id)objectValue
|
||||
{
|
||||
if([objectValue isKindOfClass:[NSString class]])
|
||||
{
|
||||
[self addAttributeWithName:name stringValue:objectValue];
|
||||
}
|
||||
else if([objectValue isKindOfClass:[NSNumber class]])
|
||||
{
|
||||
[self addAttributeWithName:name numberValue:objectValue];
|
||||
}
|
||||
else if([objectValue respondsToSelector:@selector(stringValue)])
|
||||
{
|
||||
[self addAttributeWithName:name stringValue:[objectValue stringValue]];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The following methods return the corresponding value of the attribute with the given name.
|
||||
**/
|
||||
|
||||
- (int)attributeIntValueForName:(NSString *)name
|
||||
{
|
||||
return [[self attributeStringValueForName:name] intValue];
|
||||
}
|
||||
- (BOOL)attributeBoolValueForName:(NSString *)name
|
||||
{
|
||||
NSString *attributeStringValueForName = [self attributeStringValueForName:name];
|
||||
|
||||
BOOL result = NO;
|
||||
|
||||
// An XML boolean datatype can have the following legal literals: true, false, 1, 0
|
||||
|
||||
if ([attributeStringValueForName isEqualToString:@"true"] || [attributeStringValueForName isEqualToString:@"1"])
|
||||
{
|
||||
result = YES;
|
||||
}
|
||||
else if([attributeStringValueForName isEqualToString:@"false"] || [attributeStringValueForName isEqualToString:@"0"])
|
||||
{
|
||||
result = NO;
|
||||
}
|
||||
else
|
||||
{
|
||||
result = [attributeStringValueForName boolValue];
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
- (float)attributeFloatValueForName:(NSString *)name
|
||||
{
|
||||
return [[self attributeStringValueForName:name] floatValue];
|
||||
}
|
||||
- (double)attributeDoubleValueForName:(NSString *)name
|
||||
{
|
||||
return [[self attributeStringValueForName:name] doubleValue];
|
||||
}
|
||||
- (int32_t)attributeInt32ValueForName:(NSString *)name
|
||||
{
|
||||
int32_t result = 0;
|
||||
[NSNumber xmpp_parseString:[self attributeStringValueForName:name] intoInt32:&result];
|
||||
return result;
|
||||
}
|
||||
- (uint32_t)attributeUInt32ValueForName:(NSString *)name
|
||||
{
|
||||
uint32_t result = 0;
|
||||
[NSNumber xmpp_parseString:[self attributeStringValueForName:name] intoUInt32:&result];
|
||||
return result;
|
||||
}
|
||||
- (int64_t)attributeInt64ValueForName:(NSString *)name
|
||||
{
|
||||
int64_t result;
|
||||
[NSNumber xmpp_parseString:[self attributeStringValueForName:name] intoInt64:&result];
|
||||
return result;
|
||||
}
|
||||
- (uint64_t)attributeUInt64ValueForName:(NSString *)name
|
||||
{
|
||||
uint64_t result;
|
||||
[NSNumber xmpp_parseString:[self attributeStringValueForName:name] intoUInt64:&result];
|
||||
return result;
|
||||
}
|
||||
- (NSInteger)attributeIntegerValueForName:(NSString *)name
|
||||
{
|
||||
NSInteger result;
|
||||
[NSNumber xmpp_parseString:[self attributeStringValueForName:name] intoNSInteger:&result];
|
||||
return result;
|
||||
}
|
||||
- (NSUInteger)attributeUnsignedIntegerValueForName:(NSString *)name
|
||||
{
|
||||
NSUInteger result = 0;
|
||||
[NSNumber xmpp_parseString:[self attributeStringValueForName:name] intoNSUInteger:&result];
|
||||
return result;
|
||||
}
|
||||
- (NSString *)attributeStringValueForName:(NSString *)name
|
||||
{
|
||||
return [[self attributeForName:name] stringValue];
|
||||
}
|
||||
- (NSNumber *)attributeNumberIntValueForName:(NSString *)name
|
||||
{
|
||||
return @([self attributeIntValueForName:name]);
|
||||
}
|
||||
- (NSNumber *)attributeNumberBoolValueForName:(NSString *)name
|
||||
{
|
||||
return @([self attributeBoolValueForName:name]);
|
||||
}
|
||||
- (NSNumber *)attributeNumberFloatValueForName:(NSString *)name
|
||||
{
|
||||
return @([self attributeFloatValueForName:name]);
|
||||
}
|
||||
- (NSNumber *)attributeNumberDoubleValueForName:(NSString *)name
|
||||
{
|
||||
return @([self attributeDoubleValueForName:name]);
|
||||
}
|
||||
- (NSNumber *)attributeNumberInt32ValueForName:(NSString *)name
|
||||
{
|
||||
return @([self attributeInt32ValueForName:name]);
|
||||
}
|
||||
- (NSNumber *)attributeNumberUInt32ValueForName:(NSString *)name
|
||||
{
|
||||
return @([self attributeUInt32ValueForName:name]);
|
||||
}
|
||||
- (NSNumber *)attributeNumberInt64ValueForName:(NSString *)name
|
||||
{
|
||||
return @([self attributeInt64ValueForName:name]);
|
||||
}
|
||||
- (NSNumber *)attributeNumberUInt64ValueForName:(NSString *)name
|
||||
{
|
||||
return @([self attributeUInt64ValueForName:name]);
|
||||
}
|
||||
- (NSNumber *)attributeNumberIntegerValueForName:(NSString *)name
|
||||
{
|
||||
return @([self attributeIntegerValueForName:name]);
|
||||
}
|
||||
- (NSNumber *)attributeNumberUnsignedIntegerValueForName:(NSString *)name
|
||||
{
|
||||
return @([self attributeUnsignedIntegerValueForName:name]);
|
||||
}
|
||||
|
||||
/**
|
||||
* The following methods return the corresponding value of the attribute with the given name.
|
||||
* If the attribute does not exist, the given defaultValue is returned.
|
||||
**/
|
||||
|
||||
- (int)attributeIntValueForName:(NSString *)name withDefaultValue:(int)defaultValue
|
||||
{
|
||||
NSXMLNode *attr = [self attributeForName:name];
|
||||
return (attr) ? [[attr stringValue] intValue] : defaultValue;
|
||||
}
|
||||
- (BOOL)attributeBoolValueForName:(NSString *)name withDefaultValue:(BOOL)defaultValue
|
||||
{
|
||||
NSXMLNode *attr = [self attributeForName:name];
|
||||
return (attr) ? [[attr stringValue] boolValue] : defaultValue;
|
||||
}
|
||||
- (float)attributeFloatValueForName:(NSString *)name withDefaultValue:(float)defaultValue
|
||||
{
|
||||
NSXMLNode *attr = [self attributeForName:name];
|
||||
return (attr) ? [[attr stringValue] floatValue] : defaultValue;
|
||||
}
|
||||
- (double)attributeDoubleValueForName:(NSString *)name withDefaultValue:(double)defaultValue
|
||||
{
|
||||
NSXMLNode *attr = [self attributeForName:name];
|
||||
return (attr) ? [[attr stringValue] doubleValue] : defaultValue;
|
||||
}
|
||||
- (int32_t)attributeInt32ValueForName:(NSString *)name withDefaultValue:(int32_t)defaultValue
|
||||
{
|
||||
int32_t result = 0;
|
||||
if ([NSNumber xmpp_parseString:[self attributeStringValueForName:name] intoInt32:&result])
|
||||
return result;
|
||||
else
|
||||
return defaultValue;
|
||||
}
|
||||
- (uint32_t)attributeUInt32ValueForName:(NSString *)name withDefaultValue:(uint32_t)defaultValue
|
||||
{
|
||||
uint32_t result = 0;
|
||||
if ([NSNumber xmpp_parseString:[self attributeStringValueForName:name] intoUInt32:&result])
|
||||
return result;
|
||||
else
|
||||
return defaultValue;
|
||||
}
|
||||
- (int64_t)attributeInt64ValueForName:(NSString *)name withDefaultValue:(int64_t)defaultValue
|
||||
{
|
||||
int64_t result = 0;
|
||||
if ([NSNumber xmpp_parseString:[self attributeStringValueForName:name] intoInt64:&result])
|
||||
return result;
|
||||
else
|
||||
return defaultValue;
|
||||
}
|
||||
- (uint64_t)attributeUInt64ValueForName:(NSString *)name withDefaultValue:(uint64_t)defaultValue
|
||||
{
|
||||
uint64_t result = 0;
|
||||
if ([NSNumber xmpp_parseString:[self attributeStringValueForName:name] intoUInt64:&result])
|
||||
return result;
|
||||
else
|
||||
return defaultValue;
|
||||
}
|
||||
- (NSInteger)attributeIntegerValueForName:(NSString *)name withDefaultValue:(NSInteger)defaultValue
|
||||
{
|
||||
NSInteger result = 0;
|
||||
if ([NSNumber xmpp_parseString:[self attributeStringValueForName:name] intoNSInteger:&result])
|
||||
return result;
|
||||
else
|
||||
return defaultValue;
|
||||
}
|
||||
- (NSUInteger)attributeUnsignedIntegerValueForName:(NSString *)name withDefaultValue:(NSUInteger)defaultValue
|
||||
{
|
||||
NSUInteger result = 0;
|
||||
if ([NSNumber xmpp_parseString:[self attributeStringValueForName:name] intoNSUInteger:&result])
|
||||
return result;
|
||||
else
|
||||
return defaultValue;
|
||||
}
|
||||
- (NSString *)attributeStringValueForName:(NSString *)name withDefaultValue:(NSString *)defaultValue
|
||||
{
|
||||
NSXMLNode *attr = [self attributeForName:name];
|
||||
return (attr) ? [attr stringValue] : defaultValue;
|
||||
}
|
||||
- (NSNumber *)attributeNumberIntValueForName:(NSString *)name withDefaultValue:(int)defaultValue
|
||||
{
|
||||
return @([self attributeIntValueForName:name withDefaultValue:defaultValue]);
|
||||
}
|
||||
- (NSNumber *)attributeNumberBoolValueForName:(NSString *)name withDefaultValue:(BOOL)defaultValue
|
||||
{
|
||||
return @([self attributeBoolValueForName:name withDefaultValue:defaultValue]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all the attributes in a dictionary.
|
||||
**/
|
||||
- (NSMutableDictionary *)attributesAsDictionary
|
||||
{
|
||||
NSArray *attributes = [self attributes];
|
||||
NSMutableDictionary *result = [NSMutableDictionary dictionaryWithCapacity:[attributes count]];
|
||||
|
||||
NSUInteger i;
|
||||
for(i = 0; i < [attributes count]; i++)
|
||||
{
|
||||
NSXMLNode *node = attributes[i];
|
||||
|
||||
result[[node name]] = [node stringValue];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* The following methods return the corresponding value of the node.
|
||||
**/
|
||||
|
||||
- (int)stringValueAsInt
|
||||
{
|
||||
return [[self stringValue] intValue];
|
||||
}
|
||||
- (BOOL)stringValueAsBool
|
||||
{
|
||||
return [[self stringValue] boolValue];
|
||||
}
|
||||
- (float)stringValueAsFloat
|
||||
{
|
||||
return [[self stringValue] floatValue];
|
||||
}
|
||||
- (double)stringValueAsDouble
|
||||
{
|
||||
return [[self stringValue] doubleValue];
|
||||
}
|
||||
- (int32_t)stringValueAsInt32
|
||||
{
|
||||
int32_t result;
|
||||
if ([NSNumber xmpp_parseString:[self stringValue] intoInt32:&result])
|
||||
return result;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
- (uint32_t)stringValueAsUInt32
|
||||
{
|
||||
uint32_t result;
|
||||
if ([NSNumber xmpp_parseString:[self stringValue] intoUInt32:&result])
|
||||
return result;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
- (int64_t)stringValueAsInt64
|
||||
{
|
||||
int64_t result = 0;
|
||||
if ([NSNumber xmpp_parseString:[self stringValue] intoInt64:&result])
|
||||
return result;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
- (uint64_t)stringValueAsUInt64
|
||||
{
|
||||
uint64_t result = 0;
|
||||
if ([NSNumber xmpp_parseString:[self stringValue] intoUInt64:&result])
|
||||
return result;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
- (NSInteger)stringValueAsNSInteger
|
||||
{
|
||||
NSInteger result = 0;
|
||||
if ([NSNumber xmpp_parseString:[self stringValue] intoNSInteger:&result])
|
||||
return result;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
- (NSUInteger)stringValueAsNSUInteger
|
||||
{
|
||||
NSUInteger result = 0;
|
||||
if ([NSNumber xmpp_parseString:[self stringValue] intoNSUInteger:&result])
|
||||
return result;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shortcut to avoid having to use NSXMLNode everytime
|
||||
**/
|
||||
- (void)addNamespaceWithPrefix:(NSString *)prefix stringValue:(NSString *)string
|
||||
{
|
||||
[self addNamespace:[NSXMLNode namespaceWithName:prefix stringValue:string]];
|
||||
}
|
||||
|
||||
/**
|
||||
* Just to make your code look a little bit cleaner.
|
||||
**/
|
||||
|
||||
- (NSString *)namespaceStringValueForPrefix:(NSString *)prefix
|
||||
{
|
||||
return [[self namespaceForPrefix:prefix] stringValue];
|
||||
}
|
||||
|
||||
- (NSString *)namespaceStringValueForPrefix:(NSString *)prefix withDefaultValue:(NSString *)defaultValue
|
||||
{
|
||||
NSXMLNode *namespace = [self namespaceForPrefix:prefix];
|
||||
return (namespace) ? [namespace stringValue] : defaultValue;
|
||||
}
|
||||
|
||||
@end
|
||||
31
Core/XMPP.h
Normal file
31
Core/XMPP.h
Normal file
@ -0,0 +1,31 @@
|
||||
//
|
||||
// Core classes
|
||||
//
|
||||
|
||||
#import "XMPPJID.h"
|
||||
#import "XMPPStream.h"
|
||||
#import "XMPPElement.h"
|
||||
#import "XMPPIQ.h"
|
||||
#import "XMPPMessage.h"
|
||||
#import "XMPPPresence.h"
|
||||
#import "XMPPModule.h"
|
||||
|
||||
//
|
||||
// Authentication
|
||||
//
|
||||
|
||||
#import "XMPPSASLAuthentication.h"
|
||||
#import "XMPPCustomBinding.h"
|
||||
#import "XMPPDigestMD5Authentication.h"
|
||||
#import "XMPPSCRAMSHA1Authentication.h"
|
||||
#import "XMPPPlainAuthentication.h"
|
||||
#import "XMPPXFacebookPlatformAuthentication.h"
|
||||
#import "XMPPAnonymousAuthentication.h"
|
||||
#import "XMPPDeprecatedPlainAuthentication.h"
|
||||
#import "XMPPDeprecatedDigestAuthentication.h"
|
||||
|
||||
//
|
||||
// Categories
|
||||
//
|
||||
|
||||
#import "NSXMLElement+XMPP.h"
|
||||
16
Core/XMPPConstants.h
Normal file
16
Core/XMPPConstants.h
Normal file
@ -0,0 +1,16 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
/**
|
||||
* This class is provided to house various namespaces that are reused throughout
|
||||
* the project. Feel free to add to the constants as you see necessary. If a
|
||||
* particular namespace is only applicable to a particular extension, then it
|
||||
* should be inside that extension rather than here.
|
||||
*/
|
||||
|
||||
extern NSString *const XMPPSINamespace;
|
||||
extern NSString *const XMPPSIProfileFileTransferNamespace;
|
||||
extern NSString *const XMPPFeatureNegNamespace;
|
||||
extern NSString *const XMPPBytestreamsNamespace;
|
||||
extern NSString *const XMPPIBBNamespace;
|
||||
extern NSString *const XMPPDiscoItemsNamespace;
|
||||
extern NSString *const XMPPDiscoInfoNamespace;
|
||||
10
Core/XMPPConstants.m
Normal file
10
Core/XMPPConstants.m
Normal file
@ -0,0 +1,10 @@
|
||||
#import "XMPPConstants.h"
|
||||
|
||||
NSString *const XMPPSINamespace = @"http://jabber.org/protocol/si";
|
||||
NSString *const XMPPSIProfileFileTransferNamespace =
|
||||
@"http://jabber.org/protocol/si/profile/file-transfer";
|
||||
NSString *const XMPPFeatureNegNamespace = @"http://jabber.org/protocol/feature-neg";
|
||||
NSString *const XMPPBytestreamsNamespace = @"http://jabber.org/protocol/bytestreams";
|
||||
NSString *const XMPPIBBNamespace = @"http://jabber.org/protocol/ibb";
|
||||
NSString *const XMPPDiscoItemsNamespace = @"http://jabber.org/protocol/disco#items";
|
||||
NSString *const XMPPDiscoInfoNamespace = @"http://jabber.org/protocol/disco#info";
|
||||
44
Core/XMPPElement.h
Normal file
44
Core/XMPPElement.h
Normal file
@ -0,0 +1,44 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "XMPPJID.h"
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
#import "DDXML.h"
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
* The XMPPElement provides the base class for XMPPIQ, XMPPMessage & XMPPPresence.
|
||||
*
|
||||
* This class extends NSXMLElement.
|
||||
* The NSXML classes (NSXMLElement & NSXMLNode) provide a full-featured library for working with XML elements.
|
||||
*
|
||||
* On the iPhone, the KissXML library provides a drop-in replacement for Apple's NSXML classes.
|
||||
**/
|
||||
|
||||
@interface XMPPElement : NSXMLElement <NSSecureCoding, NSCopying>
|
||||
|
||||
#pragma mark Common Jabber Methods
|
||||
|
||||
- (NSString *)elementID;
|
||||
|
||||
- (XMPPJID *)to;
|
||||
- (XMPPJID *)from;
|
||||
|
||||
- (NSString *)toStr;
|
||||
- (NSString *)fromStr;
|
||||
|
||||
#pragma mark To and From Methods
|
||||
|
||||
- (BOOL)isTo:(XMPPJID *)to;
|
||||
- (BOOL)isTo:(XMPPJID *)to options:(XMPPJIDCompareOptions)mask;
|
||||
|
||||
- (BOOL)isFrom:(XMPPJID *)from;
|
||||
- (BOOL)isFrom:(XMPPJID *)from options:(XMPPJIDCompareOptions)mask;
|
||||
|
||||
- (BOOL)isToOrFrom:(XMPPJID *)toOrFrom;
|
||||
- (BOOL)isToOrFrom:(XMPPJID *)toOrFrom options:(XMPPJIDCompareOptions)mask;
|
||||
|
||||
- (BOOL)isTo:(XMPPJID *)to from:(XMPPJID *)from;
|
||||
- (BOOL)isTo:(XMPPJID *)to from:(XMPPJID *)from options:(XMPPJIDCompareOptions)mask;
|
||||
|
||||
@end
|
||||
192
Core/XMPPElement.m
Normal file
192
Core/XMPPElement.m
Normal file
@ -0,0 +1,192 @@
|
||||
#import "XMPPElement.h"
|
||||
#import "XMPPJID.h"
|
||||
#import "NSXMLElement+XMPP.h"
|
||||
|
||||
#import <objc/runtime.h>
|
||||
|
||||
|
||||
@implementation XMPPElement
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark Encoding, Decoding
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#if ! TARGET_OS_IPHONE
|
||||
- (id)replacementObjectForPortCoder:(NSPortCoder *)encoder
|
||||
{
|
||||
if([encoder isBycopy])
|
||||
return self;
|
||||
else
|
||||
return [super replacementObjectForPortCoder:encoder];
|
||||
// return [NSDistantObject proxyWithLocal:self connection:[encoder connection]];
|
||||
}
|
||||
#endif
|
||||
|
||||
- (id)initWithCoder:(NSCoder *)coder
|
||||
{
|
||||
NSString *xmlString;
|
||||
if([coder allowsKeyedCoding])
|
||||
{
|
||||
if([coder respondsToSelector:@selector(requiresSecureCoding)] &&
|
||||
[coder requiresSecureCoding])
|
||||
{
|
||||
xmlString = [coder decodeObjectOfClass:[NSString class] forKey:@"xmlString"];
|
||||
}
|
||||
else
|
||||
{
|
||||
xmlString = [coder decodeObjectForKey:@"xmlString"];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
xmlString = [coder decodeObject];
|
||||
}
|
||||
|
||||
// The method [super initWithXMLString:error:] may return a different self.
|
||||
// In other words, it may [self release], and alloc/init/return a new self.
|
||||
//
|
||||
// So to maintain the proper class (XMPPIQ, XMPPMessage, XMPPPresence, etc)
|
||||
// we need to get a reference to the class before invoking super.
|
||||
|
||||
Class selfClass = [self class];
|
||||
|
||||
if ((self = [super initWithXMLString:xmlString error:nil]))
|
||||
{
|
||||
object_setClass(self, selfClass);
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(NSCoder *)coder
|
||||
{
|
||||
NSString *xmlString = [self compactXMLString];
|
||||
|
||||
if([coder allowsKeyedCoding])
|
||||
{
|
||||
[coder encodeObject:xmlString forKey:@"xmlString"];
|
||||
}
|
||||
else
|
||||
{
|
||||
[coder encodeObject:xmlString];
|
||||
}
|
||||
}
|
||||
|
||||
+ (BOOL) supportsSecureCoding
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark Copying
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
- (id)copyWithZone:(NSZone *)zone
|
||||
{
|
||||
NSXMLElement *elementCopy = [super copyWithZone:zone];
|
||||
object_setClass(elementCopy, [self class]);
|
||||
|
||||
return elementCopy;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark Common Jabber Methods
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
- (NSString *)elementID
|
||||
{
|
||||
return [[self attributeForName:@"id"] stringValue];
|
||||
}
|
||||
|
||||
- (NSString *)toStr
|
||||
{
|
||||
return [[self attributeForName:@"to"] stringValue];
|
||||
}
|
||||
|
||||
- (NSString *)fromStr
|
||||
{
|
||||
return [[self attributeForName:@"from"] stringValue];
|
||||
}
|
||||
|
||||
- (XMPPJID *)to
|
||||
{
|
||||
return [XMPPJID jidWithString:[self toStr]];
|
||||
}
|
||||
|
||||
- (XMPPJID *)from
|
||||
{
|
||||
return [XMPPJID jidWithString:[self fromStr]];
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark To and From Methods
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
- (BOOL)isTo:(XMPPJID *)to
|
||||
{
|
||||
return [self.to isEqualToJID:to];
|
||||
}
|
||||
|
||||
- (BOOL)isTo:(XMPPJID *)to options:(XMPPJIDCompareOptions)mask
|
||||
{
|
||||
return [self.to isEqualToJID:to options:mask];
|
||||
}
|
||||
|
||||
- (BOOL)isFrom:(XMPPJID *)from
|
||||
{
|
||||
return [self.from isEqualToJID:from];
|
||||
}
|
||||
|
||||
- (BOOL)isFrom:(XMPPJID *)from options:(XMPPJIDCompareOptions)mask
|
||||
{
|
||||
return [self.from isEqualToJID:from options:mask];
|
||||
}
|
||||
|
||||
- (BOOL)isToOrFrom:(XMPPJID *)toOrFrom
|
||||
{
|
||||
if([self isTo:toOrFrom] || [self isFrom:toOrFrom])
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
else
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)isToOrFrom:(XMPPJID *)toOrFrom options:(XMPPJIDCompareOptions)mask
|
||||
{
|
||||
if([self isTo:toOrFrom options:mask] || [self isFrom:toOrFrom options:mask])
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
else
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)isTo:(XMPPJID *)to from:(XMPPJID *)from
|
||||
{
|
||||
if([self isTo:to] && [self isFrom:from])
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
else
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)isTo:(XMPPJID *)to from:(XMPPJID *)from options:(XMPPJIDCompareOptions)mask
|
||||
{
|
||||
if([self isTo:to options:mask] && [self isFrom:from options:mask])
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
else
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
83
Core/XMPPIQ.h
Normal file
83
Core/XMPPIQ.h
Normal file
@ -0,0 +1,83 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "XMPPElement.h"
|
||||
|
||||
/**
|
||||
* The XMPPIQ class represents an <iq> element.
|
||||
* It extends XMPPElement, which in turn extends NSXMLElement.
|
||||
* All <iq> elements that go in and out of the
|
||||
* xmpp stream will automatically be converted to XMPPIQ objects.
|
||||
*
|
||||
* This class exists to provide developers an easy way to add functionality to IQ processing.
|
||||
* Simply add your own category to XMPPIQ to extend it with your own custom methods.
|
||||
**/
|
||||
|
||||
@interface XMPPIQ : XMPPElement
|
||||
|
||||
/**
|
||||
* Converts an NSXMLElement to an XMPPIQ element in place (no memory allocations or copying)
|
||||
**/
|
||||
+ (XMPPIQ *)iqFromElement:(NSXMLElement *)element;
|
||||
|
||||
/**
|
||||
* Creates and returns a new autoreleased XMPPIQ element.
|
||||
* If the type or elementID parameters are nil, those attributes will not be added.
|
||||
**/
|
||||
+ (XMPPIQ *)iq;
|
||||
+ (XMPPIQ *)iqWithType:(NSString *)type;
|
||||
+ (XMPPIQ *)iqWithType:(NSString *)type to:(XMPPJID *)jid;
|
||||
+ (XMPPIQ *)iqWithType:(NSString *)type to:(XMPPJID *)jid elementID:(NSString *)eid;
|
||||
+ (XMPPIQ *)iqWithType:(NSString *)type to:(XMPPJID *)jid elementID:(NSString *)eid child:(NSXMLElement *)childElement;
|
||||
+ (XMPPIQ *)iqWithType:(NSString *)type elementID:(NSString *)eid;
|
||||
+ (XMPPIQ *)iqWithType:(NSString *)type elementID:(NSString *)eid child:(NSXMLElement *)childElement;
|
||||
+ (XMPPIQ *)iqWithType:(NSString *)type child:(NSXMLElement *)childElement;
|
||||
|
||||
/**
|
||||
* Creates and returns a new XMPPIQ element.
|
||||
* If the type or elementID parameters are nil, those attributes will not be added.
|
||||
**/
|
||||
- (id)init;
|
||||
- (id)initWithType:(NSString *)type;
|
||||
- (id)initWithType:(NSString *)type to:(XMPPJID *)jid;
|
||||
- (id)initWithType:(NSString *)type to:(XMPPJID *)jid elementID:(NSString *)eid;
|
||||
- (id)initWithType:(NSString *)type to:(XMPPJID *)jid elementID:(NSString *)eid child:(NSXMLElement *)childElement;
|
||||
- (id)initWithType:(NSString *)type elementID:(NSString *)eid;
|
||||
- (id)initWithType:(NSString *)type elementID:(NSString *)eid child:(NSXMLElement *)childElement;
|
||||
- (id)initWithType:(NSString *)type child:(NSXMLElement *)childElement;
|
||||
|
||||
/**
|
||||
* Returns the type attribute of the IQ.
|
||||
* According to the XMPP protocol, the type should be one of 'get', 'set', 'result' or 'error'.
|
||||
*
|
||||
* This method converts the attribute to lowercase so
|
||||
* case-sensitive string comparisons are safe (regardless of server treatment).
|
||||
**/
|
||||
- (NSString *)type;
|
||||
|
||||
/**
|
||||
* Convenience methods for determining the IQ type.
|
||||
**/
|
||||
- (BOOL)isGetIQ;
|
||||
- (BOOL)isSetIQ;
|
||||
- (BOOL)isResultIQ;
|
||||
- (BOOL)isErrorIQ;
|
||||
|
||||
/**
|
||||
* Convenience method for determining if the IQ is of type 'get' or 'set'.
|
||||
**/
|
||||
- (BOOL)requiresResponse;
|
||||
|
||||
/**
|
||||
* The XMPP RFC has various rules for the number of child elements an IQ is allowed to have:
|
||||
*
|
||||
* - An IQ stanza of type "get" or "set" MUST contain one and only one child element.
|
||||
* - An IQ stanza of type "result" MUST include zero or one child elements.
|
||||
* - An IQ stanza of type "error" SHOULD include the child element contained in the
|
||||
* associated "get" or "set" and MUST include an <error/> child.
|
||||
*
|
||||
* The childElement returns the single non-error element, if one exists, or nil.
|
||||
* The childErrorElement returns the error element, if one exists, or nil.
|
||||
**/
|
||||
- (NSXMLElement *)childElement;
|
||||
- (NSXMLElement *)childErrorElement;
|
||||
|
||||
@end
|
||||
222
Core/XMPPIQ.m
Normal file
222
Core/XMPPIQ.m
Normal file
@ -0,0 +1,222 @@
|
||||
#import "XMPPIQ.h"
|
||||
#import "XMPPJID.h"
|
||||
#import "NSXMLElement+XMPP.h"
|
||||
|
||||
#import <objc/runtime.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 XMPPIQ
|
||||
|
||||
#if DEBUG
|
||||
|
||||
+ (void)initialize
|
||||
{
|
||||
// We use the object_setClass method below to dynamically change the class from a standard NSXMLElement.
|
||||
// The size of the two classes is expected to be the same.
|
||||
//
|
||||
// If a developer adds instance methods to this class, bad things happen at runtime that are very hard to debug.
|
||||
// This check is here to aid future developers who may make this mistake.
|
||||
//
|
||||
// For Fearless And Experienced Objective-C Developers:
|
||||
// It may be possible to support adding instance variables to this class if you seriously need it.
|
||||
// To do so, try realloc'ing self after altering the class, and then initialize your variables.
|
||||
|
||||
size_t superSize = class_getInstanceSize([NSXMLElement class]);
|
||||
size_t ourSize = class_getInstanceSize([XMPPIQ class]);
|
||||
|
||||
if (superSize != ourSize)
|
||||
{
|
||||
NSLog(@"Adding instance variables to XMPPIQ is not currently supported!");
|
||||
exit(15);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
+ (XMPPIQ *)iqFromElement:(NSXMLElement *)element
|
||||
{
|
||||
object_setClass(element, [XMPPIQ class]);
|
||||
|
||||
return (XMPPIQ *)element;
|
||||
}
|
||||
|
||||
+ (XMPPIQ *)iq
|
||||
{
|
||||
return [[XMPPIQ alloc] initWithType:nil to:nil elementID:nil child:nil];
|
||||
}
|
||||
|
||||
+ (XMPPIQ *)iqWithType:(NSString *)type
|
||||
{
|
||||
return [[XMPPIQ alloc] initWithType:type to:nil elementID:nil child:nil];
|
||||
}
|
||||
|
||||
+ (XMPPIQ *)iqWithType:(NSString *)type to:(XMPPJID *)jid
|
||||
{
|
||||
return [[XMPPIQ alloc] initWithType:type to:jid elementID:nil child:nil];
|
||||
}
|
||||
|
||||
+ (XMPPIQ *)iqWithType:(NSString *)type to:(XMPPJID *)jid elementID:(NSString *)eid
|
||||
{
|
||||
return [[XMPPIQ alloc] initWithType:type to:jid elementID:eid child:nil];
|
||||
}
|
||||
|
||||
+ (XMPPIQ *)iqWithType:(NSString *)type to:(XMPPJID *)jid elementID:(NSString *)eid child:(NSXMLElement *)childElement
|
||||
{
|
||||
return [[XMPPIQ alloc] initWithType:type to:jid elementID:eid child:childElement];
|
||||
}
|
||||
|
||||
+ (XMPPIQ *)iqWithType:(NSString *)type elementID:(NSString *)eid
|
||||
{
|
||||
return [[XMPPIQ alloc] initWithType:type to:nil elementID:eid child:nil];
|
||||
}
|
||||
|
||||
+ (XMPPIQ *)iqWithType:(NSString *)type elementID:(NSString *)eid child:(NSXMLElement *)childElement
|
||||
{
|
||||
return [[XMPPIQ alloc] initWithType:type to:nil elementID:eid child:childElement];
|
||||
}
|
||||
|
||||
+ (XMPPIQ *)iqWithType:(NSString *)type child:(NSXMLElement *)childElement
|
||||
{
|
||||
return [[XMPPIQ alloc] initWithType:type to:nil elementID:nil child:childElement];
|
||||
}
|
||||
|
||||
- (id)init
|
||||
{
|
||||
return [self initWithType:nil to:nil elementID:nil child:nil];
|
||||
}
|
||||
|
||||
- (id)initWithType:(NSString *)type
|
||||
{
|
||||
return [self initWithType:type to:nil elementID:nil child:nil];
|
||||
}
|
||||
|
||||
- (id)initWithType:(NSString *)type to:(XMPPJID *)jid
|
||||
{
|
||||
return [self initWithType:type to:jid elementID:nil child:nil];
|
||||
}
|
||||
|
||||
- (id)initWithType:(NSString *)type to:(XMPPJID *)jid elementID:(NSString *)eid
|
||||
{
|
||||
return [self initWithType:type to:jid elementID:eid child:nil];
|
||||
}
|
||||
|
||||
- (id)initWithType:(NSString *)type to:(XMPPJID *)jid elementID:(NSString *)eid child:(NSXMLElement *)childElement
|
||||
{
|
||||
if ((self = [super initWithName:@"iq"]))
|
||||
{
|
||||
if (type)
|
||||
[self addAttributeWithName:@"type" stringValue:type];
|
||||
|
||||
if (jid)
|
||||
[self addAttributeWithName:@"to" stringValue:[jid full]];
|
||||
|
||||
if (eid)
|
||||
[self addAttributeWithName:@"id" stringValue:eid];
|
||||
|
||||
if (childElement)
|
||||
[self addChild:childElement];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)initWithType:(NSString *)type elementID:(NSString *)eid
|
||||
{
|
||||
return [self initWithType:type to:nil elementID:eid child:nil];
|
||||
}
|
||||
|
||||
- (id)initWithType:(NSString *)type elementID:(NSString *)eid child:(NSXMLElement *)childElement
|
||||
{
|
||||
return [self initWithType:type to:nil elementID:eid child:childElement];
|
||||
}
|
||||
|
||||
- (id)initWithType:(NSString *)type child:(NSXMLElement *)childElement
|
||||
{
|
||||
return [self initWithType:type to:nil elementID:nil child:childElement];
|
||||
}
|
||||
|
||||
- (id)initWithXMLString:(NSString *)string error:(NSError *__autoreleasing *)error
|
||||
{
|
||||
if((self = [super initWithXMLString:string error:error])){
|
||||
self = [XMPPIQ iqFromElement:self];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)copyWithZone:(NSZone *)zone
|
||||
{
|
||||
NSXMLElement *element = [super copyWithZone:zone];
|
||||
return [XMPPIQ iqFromElement:element];
|
||||
}
|
||||
|
||||
- (NSString *)type
|
||||
{
|
||||
return [[self attributeStringValueForName:@"type"] lowercaseString];
|
||||
}
|
||||
|
||||
- (BOOL)isGetIQ
|
||||
{
|
||||
return [[self type] isEqualToString:@"get"];
|
||||
}
|
||||
|
||||
- (BOOL)isSetIQ
|
||||
{
|
||||
return [[self type] isEqualToString:@"set"];
|
||||
}
|
||||
|
||||
- (BOOL)isResultIQ
|
||||
{
|
||||
return [[self type] isEqualToString:@"result"];
|
||||
}
|
||||
|
||||
- (BOOL)isErrorIQ
|
||||
{
|
||||
return [[self type] isEqualToString:@"error"];
|
||||
}
|
||||
|
||||
- (BOOL)requiresResponse
|
||||
{
|
||||
// An entity that receives an IQ request of type "get" or "set" MUST reply with an IQ response
|
||||
// of type "result" or "error" (the response MUST preserve the 'id' attribute of the request).
|
||||
|
||||
return [self isGetIQ] || [self isSetIQ];
|
||||
}
|
||||
|
||||
- (NSXMLElement *)childElement
|
||||
{
|
||||
NSArray *children = [self children];
|
||||
for (NSXMLElement *child in children)
|
||||
{
|
||||
// Careful: NSOrderedSame == 0
|
||||
|
||||
NSString *childName = [child name];
|
||||
if (childName && ([childName caseInsensitiveCompare:@"error"] != NSOrderedSame))
|
||||
{
|
||||
return child;
|
||||
}
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (NSXMLElement *)childErrorElement
|
||||
{
|
||||
NSArray *children = [self children];
|
||||
for (NSXMLElement *child in children)
|
||||
{
|
||||
// Careful: NSOrderedSame == 0
|
||||
|
||||
NSString *childName = [child name];
|
||||
if (childName && ([childName caseInsensitiveCompare:@"error"] == NSOrderedSame))
|
||||
{
|
||||
return child;
|
||||
}
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
@end
|
||||
118
Core/XMPPInternal.h
Normal file
118
Core/XMPPInternal.h
Normal file
@ -0,0 +1,118 @@
|
||||
//
|
||||
// This file is for XMPPStream and various internal components.
|
||||
//
|
||||
|
||||
#import "XMPPStream.h"
|
||||
#import "XMPPModule.h"
|
||||
|
||||
// Define the various states we'll use to track our progress
|
||||
typedef NS_ENUM(NSInteger, XMPPStreamState) {
|
||||
STATE_XMPP_DISCONNECTED,
|
||||
STATE_XMPP_RESOLVING_SRV,
|
||||
STATE_XMPP_CONNECTING,
|
||||
STATE_XMPP_OPENING,
|
||||
STATE_XMPP_NEGOTIATING,
|
||||
STATE_XMPP_STARTTLS_1,
|
||||
STATE_XMPP_STARTTLS_2,
|
||||
STATE_XMPP_POST_NEGOTIATION,
|
||||
STATE_XMPP_REGISTERING,
|
||||
STATE_XMPP_AUTH,
|
||||
STATE_XMPP_BINDING,
|
||||
STATE_XMPP_START_SESSION,
|
||||
STATE_XMPP_CONNECTED,
|
||||
};
|
||||
|
||||
/**
|
||||
* It is recommended that storage classes cache a stream's myJID.
|
||||
* This prevents them from constantly querying the property from the xmppStream instance,
|
||||
* as doing so goes through xmppStream's dispatch queue.
|
||||
* Caching the stream's myJID frees the dispatch queue to handle xmpp processing tasks.
|
||||
*
|
||||
* The object of the notification will be the XMPPStream instance.
|
||||
*
|
||||
* Note: We're not using the typical MulticastDelegate paradigm for this task as
|
||||
* storage classes are not typically added as a delegate of the xmppStream.
|
||||
**/
|
||||
extern NSString *const XMPPStreamDidChangeMyJIDNotification;
|
||||
|
||||
@interface XMPPStream (/* Internal */)
|
||||
|
||||
/**
|
||||
* XMPPStream maintains thread safety by dispatching through the internal serial xmppQueue.
|
||||
* Subclasses of XMPPStream MUST follow the same technique:
|
||||
*
|
||||
* dispatch_block_t block = ^{
|
||||
* // Code goes here
|
||||
* };
|
||||
*
|
||||
* if (dispatch_get_specific(xmppQueueTag))
|
||||
* block();
|
||||
* else
|
||||
* dispatch_sync(xmppQueue, block);
|
||||
*
|
||||
* Category methods may or may not need to dispatch through the xmppQueue.
|
||||
* It depends entirely on what properties of xmppStream the category method needs to access.
|
||||
* For example, if a category only accesses a single property, such as the rootElement,
|
||||
* then it can simply fetch the atomic property, inspect it, and complete its job.
|
||||
* However, if the category needs to fetch multiple properties, then it likely needs to fetch all such
|
||||
* properties in an atomic fashion. In this case, the category should likely go through the xmppQueue,
|
||||
* to ensure that it gets an atomic state of the xmppStream in order to complete its job.
|
||||
**/
|
||||
@property (nonatomic, readonly) dispatch_queue_t xmppQueue;
|
||||
@property (nonatomic, readonly) void *xmppQueueTag;
|
||||
|
||||
/**
|
||||
* Returns the current state of the xmppStream.
|
||||
**/
|
||||
@property (atomic, readonly) XMPPStreamState state;
|
||||
|
||||
/**
|
||||
* This method is for use by xmpp authentication mechanism classes.
|
||||
* They should send elements using this method instead of the public sendElement methods,
|
||||
* as those methods don't send the elements while authentication is in progress.
|
||||
*
|
||||
* @see XMPPSASLAuthentication
|
||||
**/
|
||||
- (void)sendAuthElement:(NSXMLElement *)element;
|
||||
|
||||
/**
|
||||
* This method is for use by xmpp custom binding classes.
|
||||
* They should send elements using this method instead of the public sendElement methods,
|
||||
* as those methods don't send the elements while authentication/binding is in progress.
|
||||
*
|
||||
* @see XMPPCustomBinding
|
||||
**/
|
||||
- (void)sendBindElement:(NSXMLElement *)element;
|
||||
|
||||
/**
|
||||
* This method allows you to inject an element into the stream as if it was received on the socket.
|
||||
* This is an advanced technique, but makes for some interesting possibilities.
|
||||
**/
|
||||
- (void)injectElement:(NSXMLElement *)element;
|
||||
|
||||
/**
|
||||
* The XMPP standard only supports <iq>, <message> and <presence> stanzas (excluding session setup stuff).
|
||||
* But some extensions use non-standard element types.
|
||||
* The standard example is XEP-0198, which uses <r> & <a> elements.
|
||||
*
|
||||
* XMPPStream will assume that any non-standard element types are errors, unless you register them.
|
||||
* Once registered the stream can recognize them, and will use the following delegate methods:
|
||||
*
|
||||
* xmppStream:didSendCustomElement:
|
||||
* xmppStream:didReceiveCustomElement:
|
||||
**/
|
||||
- (void)registerCustomElementNames:(NSSet *)names;
|
||||
- (void)unregisterCustomElementNames:(NSSet *)names;
|
||||
|
||||
@end
|
||||
|
||||
@interface XMPPModule (/* Internal */)
|
||||
|
||||
/**
|
||||
* Used internally by methods like XMPPStream's unregisterModule:.
|
||||
* Normally removing a delegate is a synchronous operation, but due to multiple dispatch_sync operations,
|
||||
* it must occasionally be done asynchronously to avoid deadlock.
|
||||
**/
|
||||
- (void)removeDelegate:(id)delegate delegateQueue:(dispatch_queue_t)delegateQueue synchronously:(BOOL)synchronously;
|
||||
|
||||
@end
|
||||
74
Core/XMPPJID.h
Normal file
74
Core/XMPPJID.h
Normal file
@ -0,0 +1,74 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
enum XMPPJIDCompareOptions
|
||||
{
|
||||
XMPPJIDCompareUser = 1, // 001
|
||||
XMPPJIDCompareDomain = 2, // 010
|
||||
XMPPJIDCompareResource = 4, // 100
|
||||
|
||||
XMPPJIDCompareBare = 3, // 011
|
||||
XMPPJIDCompareFull = 7, // 111
|
||||
};
|
||||
typedef enum XMPPJIDCompareOptions XMPPJIDCompareOptions;
|
||||
|
||||
|
||||
@interface XMPPJID : NSObject <NSSecureCoding, NSCopying>
|
||||
{
|
||||
__strong NSString *user;
|
||||
__strong NSString *domain;
|
||||
__strong NSString *resource;
|
||||
}
|
||||
|
||||
+ (XMPPJID *)jidWithString:(NSString *)jidStr;
|
||||
+ (XMPPJID *)jidWithString:(NSString *)jidStr resource:(NSString *)resource;
|
||||
+ (XMPPJID *)jidWithUser:(NSString *)user domain:(NSString *)domain resource:(NSString *)resource;
|
||||
|
||||
@property (strong, readonly) NSString *user;
|
||||
@property (strong, readonly) NSString *domain;
|
||||
@property (strong, readonly) NSString *resource;
|
||||
|
||||
/**
|
||||
* Terminology (from RFC 6120):
|
||||
*
|
||||
* The term "bare JID" refers to an XMPP address of the form <localpart@domainpart> (for an account at a server)
|
||||
* or of the form <domainpart> (for a server).
|
||||
*
|
||||
* The term "full JID" refers to an XMPP address of the form
|
||||
* <localpart@domainpart/resourcepart> (for a particular authorized client or device associated with an account)
|
||||
* or of the form <domainpart/resourcepart> (for a particular resource or script associated with a server).
|
||||
*
|
||||
* Thus a bareJID is one that does not have a resource.
|
||||
* And a fullJID is one that does have a resource.
|
||||
*
|
||||
* For convenience, there are also methods that that check for a user component as well.
|
||||
**/
|
||||
|
||||
- (XMPPJID *)bareJID;
|
||||
- (XMPPJID *)domainJID;
|
||||
|
||||
- (NSString *)bare;
|
||||
- (NSString *)full;
|
||||
|
||||
- (BOOL)isBare;
|
||||
- (BOOL)isBareWithUser;
|
||||
|
||||
- (BOOL)isFull;
|
||||
- (BOOL)isFullWithUser;
|
||||
|
||||
/**
|
||||
* A server JID does not have a user component.
|
||||
**/
|
||||
- (BOOL)isServer;
|
||||
|
||||
/**
|
||||
* Returns a new jid with the given resource.
|
||||
**/
|
||||
- (XMPPJID *)jidWithNewResource:(NSString *)resource;
|
||||
|
||||
/**
|
||||
* When you know both objects are JIDs, this method is a faster way to check equality than isEqual:.
|
||||
**/
|
||||
- (BOOL)isEqualToJID:(XMPPJID *)aJID;
|
||||
- (BOOL)isEqualToJID:(XMPPJID *)aJID options:(XMPPJIDCompareOptions)mask;
|
||||
|
||||
@end
|
||||
603
Core/XMPPJID.m
Normal file
603
Core/XMPPJID.m
Normal file
@ -0,0 +1,603 @@
|
||||
#import "XMPPJID.h"
|
||||
#import "XMPPStringPrep.h"
|
||||
|
||||
#if ! __has_feature(objc_arc)
|
||||
#warning This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC).
|
||||
#endif
|
||||
|
||||
|
||||
@implementation XMPPJID
|
||||
|
||||
+ (BOOL)validateDomain:(NSString *)domain
|
||||
{
|
||||
// Domain is the only required part of a JID
|
||||
if ((domain == nil) || ([domain length] == 0))
|
||||
return NO;
|
||||
|
||||
// If there's an @ symbol in the domain it probably means user put @ in their username
|
||||
NSRange invalidAtRange = [domain rangeOfString:@"@"];
|
||||
if (invalidAtRange.location != NSNotFound)
|
||||
return NO;
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
+ (BOOL)validateResource:(NSString *)resource
|
||||
{
|
||||
// Can't use an empty string resource name
|
||||
if ((resource != nil) && ([resource length] == 0))
|
||||
return NO;
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
+ (BOOL)validateUser:(NSString *)user domain:(NSString *)domain resource:(NSString *)resource
|
||||
{
|
||||
if (![self validateDomain:domain])
|
||||
return NO;
|
||||
|
||||
if (![self validateResource:resource])
|
||||
return NO;
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
+ (BOOL)parse:(NSString *)jidStr
|
||||
outUser:(NSString **)user
|
||||
outDomain:(NSString **)domain
|
||||
outResource:(NSString **)resource
|
||||
{
|
||||
if(user) *user = nil;
|
||||
if(domain) *domain = nil;
|
||||
if(resource) *resource = nil;
|
||||
|
||||
if(jidStr == nil) return NO;
|
||||
|
||||
NSString *rawUser = nil;
|
||||
NSString *rawDomain = nil;
|
||||
NSString *rawResource = nil;
|
||||
|
||||
NSRange atRange = [jidStr rangeOfString:@"@"];
|
||||
|
||||
if (atRange.location != NSNotFound)
|
||||
{
|
||||
rawUser = [jidStr substringToIndex:atRange.location];
|
||||
|
||||
NSString *minusUser = [jidStr substringFromIndex:atRange.location+1];
|
||||
|
||||
NSRange slashRange = [minusUser rangeOfString:@"/"];
|
||||
|
||||
if (slashRange.location != NSNotFound)
|
||||
{
|
||||
rawDomain = [minusUser substringToIndex:slashRange.location];
|
||||
rawResource = [minusUser substringFromIndex:slashRange.location+1];
|
||||
}
|
||||
else
|
||||
{
|
||||
rawDomain = minusUser;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
NSRange slashRange = [jidStr rangeOfString:@"/"];
|
||||
|
||||
if (slashRange.location != NSNotFound)
|
||||
{
|
||||
rawDomain = [jidStr substringToIndex:slashRange.location];
|
||||
rawResource = [jidStr substringFromIndex:slashRange.location+1];
|
||||
}
|
||||
else
|
||||
{
|
||||
rawDomain = jidStr;
|
||||
}
|
||||
}
|
||||
|
||||
NSString *prepUser = [XMPPStringPrep prepNode:rawUser];
|
||||
NSString *prepDomain = [XMPPStringPrep prepDomain:rawDomain];
|
||||
NSString *prepResource = [XMPPStringPrep prepResource:rawResource];
|
||||
|
||||
if ([XMPPJID validateUser:prepUser domain:prepDomain resource:prepResource])
|
||||
{
|
||||
if(user) *user = prepUser;
|
||||
if(domain) *domain = prepDomain;
|
||||
if(resource) *resource = prepResource;
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
+ (XMPPJID *)jidWithString:(NSString *)jidStr
|
||||
{
|
||||
NSString *user;
|
||||
NSString *domain;
|
||||
NSString *resource;
|
||||
|
||||
if ([XMPPJID parse:jidStr outUser:&user outDomain:&domain outResource:&resource])
|
||||
{
|
||||
XMPPJID *jid = [[XMPPJID alloc] init];
|
||||
jid->user = [user copy];
|
||||
jid->domain = [domain copy];
|
||||
jid->resource = [resource copy];
|
||||
|
||||
return jid;
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
+ (XMPPJID *)jidWithString:(NSString *)jidStr resource:(NSString *)resource
|
||||
{
|
||||
NSString *prepResource = [XMPPStringPrep prepResource:resource];
|
||||
if (![self validateResource:prepResource]) return nil;
|
||||
|
||||
NSString *user;
|
||||
NSString *domain;
|
||||
|
||||
if ([XMPPJID parse:jidStr outUser:&user outDomain:&domain outResource:nil])
|
||||
{
|
||||
XMPPJID *jid = [[XMPPJID alloc] init];
|
||||
jid->user = [user copy];
|
||||
jid->domain = [domain copy];
|
||||
jid->resource = [prepResource copy];
|
||||
|
||||
return jid;
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
+ (XMPPJID *)jidWithUser:(NSString *)user domain:(NSString *)domain resource:(NSString *)resource
|
||||
{
|
||||
NSString *prepUser = [XMPPStringPrep prepNode:user];
|
||||
NSString *prepDomain = [XMPPStringPrep prepDomain:domain];
|
||||
NSString *prepResource = [XMPPStringPrep prepResource:resource];
|
||||
|
||||
if ([XMPPJID validateUser:prepUser domain:prepDomain resource:prepResource])
|
||||
{
|
||||
XMPPJID *jid = [[XMPPJID alloc] init];
|
||||
jid->user = [prepUser copy];
|
||||
jid->domain = [prepDomain copy];
|
||||
jid->resource = [prepResource copy];
|
||||
|
||||
return jid;
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
+ (XMPPJID *)jidWithPrevalidatedUser:(NSString *)user
|
||||
prevalidatedDomain:(NSString *)domain
|
||||
prevalidatedResource:(NSString *)resource
|
||||
{
|
||||
XMPPJID *jid = [[XMPPJID alloc] init];
|
||||
jid->user = [user copy];
|
||||
jid->domain = [domain copy];
|
||||
jid->resource = [resource copy];
|
||||
|
||||
return jid;
|
||||
}
|
||||
|
||||
+ (XMPPJID *)jidWithPrevalidatedUser:(NSString *)user
|
||||
prevalidatedDomain:(NSString *)domain
|
||||
resource:(NSString *)resource
|
||||
{
|
||||
NSString *prepResource = [XMPPStringPrep prepResource:resource];
|
||||
if (![self validateResource:prepResource]) return nil;
|
||||
|
||||
XMPPJID *jid = [[XMPPJID alloc] init];
|
||||
jid->user = [user copy];
|
||||
jid->domain = [domain copy];
|
||||
jid->resource = [prepResource copy];
|
||||
|
||||
return jid;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark Encoding, Decoding:
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#if ! TARGET_OS_IPHONE
|
||||
- (id)replacementObjectForPortCoder:(NSPortCoder *)encoder
|
||||
{
|
||||
if([encoder isBycopy])
|
||||
return self;
|
||||
else
|
||||
return [super replacementObjectForPortCoder:encoder];
|
||||
// return [NSDistantObject proxyWithLocal:self connection:[encoder connection]];
|
||||
}
|
||||
#endif
|
||||
|
||||
- (id)initWithCoder:(NSCoder *)coder
|
||||
{
|
||||
if ((self = [super init]))
|
||||
{
|
||||
if ([coder allowsKeyedCoding])
|
||||
{
|
||||
if([coder respondsToSelector:@selector(requiresSecureCoding)] &&
|
||||
[coder requiresSecureCoding])
|
||||
{
|
||||
user = [[coder decodeObjectOfClass:[NSString class] forKey:@"user"] copy];
|
||||
domain = [[coder decodeObjectOfClass:[NSString class] forKey:@"domain"] copy];
|
||||
resource = [[coder decodeObjectOfClass:[NSString class] forKey:@"resource"] copy];
|
||||
}
|
||||
else
|
||||
{
|
||||
user = [[coder decodeObjectForKey:@"user"] copy];
|
||||
domain = [[coder decodeObjectForKey:@"domain"] copy];
|
||||
resource = [[coder decodeObjectForKey:@"resource"] copy];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
user = [[coder decodeObject] copy];
|
||||
domain = [[coder decodeObject] copy];
|
||||
resource = [[coder decodeObject] copy];
|
||||
}
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(NSCoder *)coder
|
||||
{
|
||||
if ([coder allowsKeyedCoding])
|
||||
{
|
||||
[coder encodeObject:user forKey:@"user"];
|
||||
[coder encodeObject:domain forKey:@"domain"];
|
||||
[coder encodeObject:resource forKey:@"resource"];
|
||||
}
|
||||
else
|
||||
{
|
||||
[coder encodeObject:user];
|
||||
[coder encodeObject:domain];
|
||||
[coder encodeObject:resource];
|
||||
}
|
||||
}
|
||||
|
||||
+ (BOOL) supportsSecureCoding
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark Copying:
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
- (id)copyWithZone:(NSZone *)zone
|
||||
{
|
||||
// This class is immutable
|
||||
return self;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark Normal Methods:
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Why didn't we just synthesize these properties?
|
||||
//
|
||||
// Since these variables are readonly within the class,
|
||||
// we want the synthesized methods to work like a nonatomic property.
|
||||
// In order to do this, we have to mark the properties as nonatomic in the header.
|
||||
// However we don't like marking the property as nonatomic in the header because
|
||||
// then people might think it's not thread-safe when in fact it is.
|
||||
|
||||
- (NSString *)user
|
||||
{
|
||||
return user; // Why didn't we just synthesize this? See comment above.
|
||||
}
|
||||
|
||||
- (NSString *)domain
|
||||
{
|
||||
return domain; // Why didn't we just synthesize this? See comment above.
|
||||
}
|
||||
|
||||
- (NSString *)resource
|
||||
{
|
||||
return resource; // Why didn't we just synthesize this? See comment above.
|
||||
}
|
||||
|
||||
- (XMPPJID *)bareJID
|
||||
{
|
||||
if (resource == nil)
|
||||
{
|
||||
return self;
|
||||
}
|
||||
else
|
||||
{
|
||||
return [XMPPJID jidWithPrevalidatedUser:user prevalidatedDomain:domain prevalidatedResource:nil];
|
||||
}
|
||||
}
|
||||
|
||||
- (XMPPJID *)domainJID
|
||||
{
|
||||
if (user == nil && resource == nil)
|
||||
{
|
||||
return self;
|
||||
}
|
||||
else
|
||||
{
|
||||
return [XMPPJID jidWithPrevalidatedUser:nil prevalidatedDomain:domain prevalidatedResource:nil];
|
||||
}
|
||||
}
|
||||
|
||||
- (NSString *)bare
|
||||
{
|
||||
if (user)
|
||||
return [NSString stringWithFormat:@"%@@%@", user, domain];
|
||||
else
|
||||
return domain;
|
||||
}
|
||||
|
||||
- (NSString *)full
|
||||
{
|
||||
if (user)
|
||||
{
|
||||
if (resource)
|
||||
return [NSString stringWithFormat:@"%@@%@/%@", user, domain, resource];
|
||||
else
|
||||
return [NSString stringWithFormat:@"%@@%@", user, domain];
|
||||
}
|
||||
else
|
||||
{
|
||||
if (resource)
|
||||
return [NSString stringWithFormat:@"%@/%@", domain, resource];
|
||||
else
|
||||
return domain;
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)isBare
|
||||
{
|
||||
// From RFC 6120 Terminology:
|
||||
//
|
||||
// The term "bare JID" refers to an XMPP address of the form <localpart@domainpart> (for an account at a server)
|
||||
// or of the form <domainpart> (for a server).
|
||||
|
||||
return (resource == nil);
|
||||
}
|
||||
|
||||
- (BOOL)isBareWithUser
|
||||
{
|
||||
return (user != nil && resource == nil);
|
||||
}
|
||||
|
||||
- (BOOL)isFull
|
||||
{
|
||||
// From RFC 6120 Terminology:
|
||||
//
|
||||
// The term "full JID" refers to an XMPP address of the form
|
||||
// <localpart@domainpart/resourcepart> (for a particular authorized client or device associated with an account)
|
||||
// or of the form <domainpart/resourcepart> (for a particular resource or script associated with a server).
|
||||
|
||||
return (resource != nil);
|
||||
}
|
||||
|
||||
- (BOOL)isFullWithUser
|
||||
{
|
||||
return (user != nil && resource != nil);
|
||||
}
|
||||
|
||||
- (BOOL)isServer
|
||||
{
|
||||
return (user == nil);
|
||||
}
|
||||
|
||||
- (XMPPJID *)jidWithNewResource:(NSString *)newResource
|
||||
{
|
||||
return [XMPPJID jidWithPrevalidatedUser:user prevalidatedDomain:domain resource:newResource];
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark NSObject Methods:
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
- (NSUInteger)hash
|
||||
{
|
||||
// We used to do this:
|
||||
// return [[self full] hash];
|
||||
//
|
||||
// It was functional but less than optimal because it required the creation of a new NSString everytime.
|
||||
// Now the hashing of a string itself is extremely fast,
|
||||
// so combining 3 hashes is much faster than creating a new string.
|
||||
// To accomplish this we use the murmur hashing algorithm.
|
||||
//
|
||||
// MurmurHash2 was written by Austin Appleby, and is placed in the public domain.
|
||||
// http://code.google.com/p/smhasher
|
||||
|
||||
NSUInteger uhash = [user hash];
|
||||
NSUInteger dhash = [domain hash];
|
||||
NSUInteger rhash = [resource hash];
|
||||
|
||||
if (NSUIntegerMax == UINT32_MAX) // Should be optimized out via compiler since these are constants
|
||||
{
|
||||
// MurmurHash2 (32-bit)
|
||||
//
|
||||
// uint32_t MurmurHash2 ( const void * key, int len, uint32_t seed )
|
||||
//
|
||||
// Normally one would pass a chunk of data ('key') and associated data chunk length ('len').
|
||||
// Instead we're going to use our 3 hashes.
|
||||
// And we're going to randomly make up a 'seed'.
|
||||
|
||||
const uint32_t seed = 0xa2f1b6f; // Some random value I made up
|
||||
const uint32_t len = 12; // 3 hashes, each 4 bytes = 12 bytes
|
||||
|
||||
// 'm' and 'r' are mixing constants generated offline.
|
||||
// They're not really 'magic', they just happen to work well.
|
||||
|
||||
const uint32_t m = 0x5bd1e995;
|
||||
const int r = 24;
|
||||
|
||||
// Initialize the hash to a 'random' value
|
||||
|
||||
uint32_t h = seed ^ len;
|
||||
uint32_t k;
|
||||
|
||||
// Mix uhash
|
||||
|
||||
k = uhash;
|
||||
|
||||
k *= m;
|
||||
k ^= k >> r;
|
||||
k *= m;
|
||||
|
||||
h *= m;
|
||||
h ^= k;
|
||||
|
||||
// Mix dhash
|
||||
|
||||
k = dhash;
|
||||
|
||||
k *= m;
|
||||
k ^= k >> r;
|
||||
k *= m;
|
||||
|
||||
h *= m;
|
||||
h ^= k;
|
||||
|
||||
// Mix rhash
|
||||
|
||||
k = rhash;
|
||||
|
||||
k *= m;
|
||||
k ^= k >> r;
|
||||
k *= m;
|
||||
|
||||
h *= m;
|
||||
h ^= k;
|
||||
|
||||
// Do a few final mixes of the hash to ensure the last few
|
||||
// bytes are well-incorporated.
|
||||
|
||||
h ^= h >> 13;
|
||||
h *= m;
|
||||
h ^= h >> 15;
|
||||
|
||||
return (NSUInteger)h;
|
||||
}
|
||||
else
|
||||
{
|
||||
// MurmurHash2 (64-bit)
|
||||
//
|
||||
// uint64_t MurmurHash64A ( const void * key, int len, uint64_t seed )
|
||||
//
|
||||
// Normally one would pass a chunk of data ('key') and associated data chunk length ('len').
|
||||
// Instead we're going to use our 3 hashes.
|
||||
// And we're going to randomly make up a 'seed'.
|
||||
|
||||
const uint32_t seed = 0xa2f1b6f; // Some random value I made up
|
||||
const uint32_t len = 24; // 3 hashes, each 8 bytes = 24 bytes
|
||||
|
||||
// 'm' and 'r' are mixing constants generated offline.
|
||||
// They're not really 'magic', they just happen to work well.
|
||||
|
||||
const uint64_t m = 0xc6a4a7935bd1e995LLU;
|
||||
const int r = 47;
|
||||
|
||||
// Initialize the hash to a 'random' value
|
||||
|
||||
uint64_t h = seed ^ (len * m);
|
||||
uint64_t k;
|
||||
|
||||
// Mix uhash
|
||||
|
||||
k = uhash;
|
||||
|
||||
k *= m;
|
||||
k ^= k >> r;
|
||||
k *= m;
|
||||
|
||||
h ^= k;
|
||||
h *= m;
|
||||
|
||||
// Mix dhash
|
||||
|
||||
k = dhash;
|
||||
|
||||
k *= m;
|
||||
k ^= k >> r;
|
||||
k *= m;
|
||||
|
||||
h ^= k;
|
||||
h *= m;
|
||||
|
||||
// Mix rhash
|
||||
|
||||
k = rhash;
|
||||
|
||||
k *= m;
|
||||
k ^= k >> r;
|
||||
k *= m;
|
||||
|
||||
h ^= k;
|
||||
h *= m;
|
||||
|
||||
// Do a few final mixes of the hash to ensure the last few
|
||||
// bytes are well-incorporated.
|
||||
|
||||
h ^= h >> r;
|
||||
h *= m;
|
||||
h ^= h >> r;
|
||||
|
||||
return (NSUInteger)h;
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)isEqual:(id)anObject
|
||||
{
|
||||
if ([anObject isMemberOfClass:[self class]])
|
||||
{
|
||||
return [self isEqualToJID:(XMPPJID *)anObject options:XMPPJIDCompareFull];
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (BOOL)isEqualToJID:(XMPPJID *)aJID
|
||||
{
|
||||
return [self isEqualToJID:aJID options:XMPPJIDCompareFull];
|
||||
}
|
||||
|
||||
- (BOOL)isEqualToJID:(XMPPJID *)aJID options:(XMPPJIDCompareOptions)mask
|
||||
{
|
||||
if (aJID == nil) return NO;
|
||||
|
||||
if (mask & XMPPJIDCompareUser)
|
||||
{
|
||||
if (user) {
|
||||
if (![user isEqualToString:aJID->user]) return NO;
|
||||
}
|
||||
else {
|
||||
if (aJID->user) return NO;
|
||||
}
|
||||
}
|
||||
|
||||
if (mask & XMPPJIDCompareDomain)
|
||||
{
|
||||
if (domain) {
|
||||
if (![domain isEqualToString:aJID->domain]) return NO;
|
||||
}
|
||||
else {
|
||||
if (aJID->domain) return NO;
|
||||
}
|
||||
}
|
||||
|
||||
if (mask & XMPPJIDCompareResource)
|
||||
{
|
||||
if (resource) {
|
||||
if (![resource isEqualToString:aJID->resource]) return NO;
|
||||
}
|
||||
else {
|
||||
if (aJID->resource) return NO;
|
||||
}
|
||||
}
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (NSString *)description
|
||||
{
|
||||
return [self full];
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
191
Core/XMPPLogging.h
Normal file
191
Core/XMPPLogging.h
Normal file
@ -0,0 +1,191 @@
|
||||
/**
|
||||
* In order to provide fast and flexible logging, this project uses Cocoa Lumberjack.
|
||||
*
|
||||
* The GitHub project page has a wealth of documentation if you have any questions.
|
||||
* https://github.com/robbiehanson/CocoaLumberjack
|
||||
*
|
||||
* Here's what you need to know concerning how logging is setup for XMPPFramework:
|
||||
*
|
||||
* There are 4 log levels:
|
||||
* - Error
|
||||
* - Warning
|
||||
* - Info
|
||||
* - Verbose
|
||||
*
|
||||
* In addition to this, there is a Trace flag that can be enabled.
|
||||
* When tracing is enabled, it spits out the methods that are being called.
|
||||
*
|
||||
* Please note that tracing is separate from the log levels.
|
||||
* For example, one could set the log level to warning, and enable tracing.
|
||||
*
|
||||
* All logging is asynchronous, except errors.
|
||||
* To use logging within your own custom files, follow the steps below.
|
||||
*
|
||||
* Step 1:
|
||||
* Import this header in your implementation file:
|
||||
*
|
||||
* #import "XMPPLogging.h"
|
||||
*
|
||||
* Step 2:
|
||||
* Define your logging level in your implementation file:
|
||||
*
|
||||
* // Log levels: off, error, warn, info, verbose
|
||||
* static const int xmppLogLevel = XMPP_LOG_LEVEL_VERBOSE;
|
||||
*
|
||||
* If you wish to enable tracing, you could do something like this:
|
||||
*
|
||||
* // Log levels: off, error, warn, info, verbose
|
||||
* static const int xmppLogLevel = XMPP_LOG_LEVEL_INFO | XMPP_LOG_FLAG_TRACE;
|
||||
*
|
||||
* Step 3:
|
||||
* Replace your NSLog statements with XMPPLog statements according to the severity of the message.
|
||||
*
|
||||
* NSLog(@"Fatal error, no dohickey found!"); -> XMPPLogError(@"Fatal error, no dohickey found!");
|
||||
*
|
||||
* XMPPLog has the same syntax as NSLog.
|
||||
* This means you can pass it multiple variables just like NSLog.
|
||||
*
|
||||
* You may optionally choose to define different log levels for debug and release builds.
|
||||
* You can do so like this:
|
||||
*
|
||||
* // Log levels: off, error, warn, info, verbose
|
||||
* #if DEBUG
|
||||
* static const int xmppLogLevel = XMPP_LOG_LEVEL_VERBOSE;
|
||||
* #else
|
||||
* static const int xmppLogLevel = XMPP_LOG_LEVEL_WARN;
|
||||
* #endif
|
||||
*
|
||||
* Xcode projects created with Xcode 4 automatically define DEBUG via the project's preprocessor macros.
|
||||
* If you created your project with a previous version of Xcode, you may need to add the DEBUG macro manually.
|
||||
**/
|
||||
|
||||
#import "CocoaLumberJack/DDLog.h"
|
||||
|
||||
// Global flag to enable/disable logging throughout the entire xmpp framework.
|
||||
|
||||
#ifndef XMPP_LOGGING_ENABLED
|
||||
#define XMPP_LOGGING_ENABLED 1
|
||||
#endif
|
||||
|
||||
// Define logging context for every log message coming from the XMPP framework.
|
||||
// The logging context can be extracted from the DDLogMessage from within the logging framework.
|
||||
// This gives loggers, formatters, and filters the ability to optionally process them differently.
|
||||
|
||||
#define XMPP_LOG_CONTEXT 5222
|
||||
|
||||
// Configure log levels.
|
||||
|
||||
#define XMPP_LOG_FLAG_ERROR (1 << 0) // 0...00001
|
||||
#define XMPP_LOG_FLAG_WARN (1 << 1) // 0...00010
|
||||
#define XMPP_LOG_FLAG_INFO (1 << 2) // 0...00100
|
||||
#define XMPP_LOG_FLAG_VERBOSE (1 << 3) // 0...01000
|
||||
|
||||
#define XMPP_LOG_LEVEL_OFF 0 // 0...00000
|
||||
#define XMPP_LOG_LEVEL_ERROR (XMPP_LOG_LEVEL_OFF | XMPP_LOG_FLAG_ERROR) // 0...00001
|
||||
#define XMPP_LOG_LEVEL_WARN (XMPP_LOG_LEVEL_ERROR | XMPP_LOG_FLAG_WARN) // 0...00011
|
||||
#define XMPP_LOG_LEVEL_INFO (XMPP_LOG_LEVEL_WARN | XMPP_LOG_FLAG_INFO) // 0...00111
|
||||
#define XMPP_LOG_LEVEL_VERBOSE (XMPP_LOG_LEVEL_INFO | XMPP_LOG_FLAG_VERBOSE) // 0...01111
|
||||
|
||||
// Setup fine grained logging.
|
||||
// The first 4 bits are being used by the standard log levels (0 - 3)
|
||||
//
|
||||
// We're going to add tracing, but NOT as a log level.
|
||||
// Tracing can be turned on and off independently of log level.
|
||||
|
||||
#define XMPP_LOG_FLAG_TRACE (1 << 4) // 0...10000
|
||||
|
||||
// Setup the usual boolean macros.
|
||||
|
||||
#define XMPP_LOG_ERROR (xmppLogLevel & XMPP_LOG_FLAG_ERROR)
|
||||
#define XMPP_LOG_WARN (xmppLogLevel & XMPP_LOG_FLAG_WARN)
|
||||
#define XMPP_LOG_INFO (xmppLogLevel & XMPP_LOG_FLAG_INFO)
|
||||
#define XMPP_LOG_VERBOSE (xmppLogLevel & XMPP_LOG_FLAG_VERBOSE)
|
||||
#define XMPP_LOG_TRACE (xmppLogLevel & XMPP_LOG_FLAG_TRACE)
|
||||
|
||||
// Configure asynchronous logging.
|
||||
// We follow the default configuration,
|
||||
// but we reserve a special macro to easily disable asynchronous logging for debugging purposes.
|
||||
|
||||
#if DEBUG
|
||||
#define XMPP_LOG_ASYNC_ENABLED NO
|
||||
#else
|
||||
#define XMPP_LOG_ASYNC_ENABLED YES
|
||||
#endif
|
||||
|
||||
#define XMPP_LOG_ASYNC_ERROR ( NO && XMPP_LOG_ASYNC_ENABLED)
|
||||
#define XMPP_LOG_ASYNC_WARN (YES && XMPP_LOG_ASYNC_ENABLED)
|
||||
#define XMPP_LOG_ASYNC_INFO (YES && XMPP_LOG_ASYNC_ENABLED)
|
||||
#define XMPP_LOG_ASYNC_VERBOSE (YES && XMPP_LOG_ASYNC_ENABLED)
|
||||
#define XMPP_LOG_ASYNC_TRACE (YES && XMPP_LOG_ASYNC_ENABLED)
|
||||
|
||||
// Define logging primitives.
|
||||
// These are primarily wrappers around the macros defined in Lumberjack's DDLog.h header file.
|
||||
|
||||
#define XMPP_LOG_OBJC_MAYBE(async, lvl, flg, ctx, frmt, ...) \
|
||||
do{ if(XMPP_LOGGING_ENABLED) LOG_MAYBE(async, lvl, flg, ctx, sel_getName(_cmd), frmt, ##__VA_ARGS__); } while(0)
|
||||
|
||||
#define XMPP_LOG_C_MAYBE(async, lvl, flg, ctx, frmt, ...) \
|
||||
do{ if(XMPP_LOGGING_ENABLED) LOG_MAYBE(async, lvl, flg, ctx, __FUNCTION__, frmt, ##__VA_ARGS__); } while(0)
|
||||
|
||||
|
||||
#define XMPPLogError(frmt, ...) XMPP_LOG_OBJC_MAYBE(XMPP_LOG_ASYNC_ERROR, xmppLogLevel, XMPP_LOG_FLAG_ERROR, \
|
||||
XMPP_LOG_CONTEXT, frmt, ##__VA_ARGS__)
|
||||
|
||||
#define XMPPLogWarn(frmt, ...) XMPP_LOG_OBJC_MAYBE(XMPP_LOG_ASYNC_WARN, xmppLogLevel, XMPP_LOG_FLAG_WARN, \
|
||||
XMPP_LOG_CONTEXT, frmt, ##__VA_ARGS__)
|
||||
|
||||
#define XMPPLogInfo(frmt, ...) XMPP_LOG_OBJC_MAYBE(XMPP_LOG_ASYNC_INFO, xmppLogLevel, XMPP_LOG_FLAG_INFO, \
|
||||
XMPP_LOG_CONTEXT, frmt, ##__VA_ARGS__)
|
||||
|
||||
#define XMPPLogVerbose(frmt, ...) XMPP_LOG_OBJC_MAYBE(XMPP_LOG_ASYNC_VERBOSE, xmppLogLevel, XMPP_LOG_FLAG_VERBOSE, \
|
||||
XMPP_LOG_CONTEXT, frmt, ##__VA_ARGS__)
|
||||
|
||||
#define XMPPLogTrace() XMPP_LOG_OBJC_MAYBE(XMPP_LOG_ASYNC_TRACE, xmppLogLevel, XMPP_LOG_FLAG_TRACE, \
|
||||
XMPP_LOG_CONTEXT, @"%@: %@", THIS_FILE, THIS_METHOD)
|
||||
|
||||
#define XMPPLogTrace2(frmt, ...) XMPP_LOG_OBJC_MAYBE(XMPP_LOG_ASYNC_TRACE, xmppLogLevel, XMPP_LOG_FLAG_TRACE, \
|
||||
XMPP_LOG_CONTEXT, frmt, ##__VA_ARGS__)
|
||||
|
||||
|
||||
#define XMPPLogCError(frmt, ...) XMPP_LOG_C_MAYBE(XMPP_LOG_ASYNC_ERROR, xmppLogLevel, XMPP_LOG_FLAG_ERROR, \
|
||||
XMPP_LOG_CONTEXT, frmt, ##__VA_ARGS__)
|
||||
|
||||
#define XMPPLogCWarn(frmt, ...) XMPP_LOG_C_MAYBE(XMPP_LOG_ASYNC_WARN, xmppLogLevel, XMPP_LOG_FLAG_WARN, \
|
||||
XMPP_LOG_CONTEXT, frmt, ##__VA_ARGS__)
|
||||
|
||||
#define XMPPLogCInfo(frmt, ...) XMPP_LOG_C_MAYBE(XMPP_LOG_ASYNC_INFO, xmppLogLevel, XMPP_LOG_FLAG_INFO, \
|
||||
XMPP_LOG_CONTEXT, frmt, ##__VA_ARGS__)
|
||||
|
||||
#define XMPPLogCVerbose(frmt, ...) XMPP_LOG_C_MAYBE(XMPP_LOG_ASYNC_VERBOSE, xmppLogLevel, XMPP_LOG_FLAG_VERBOSE, \
|
||||
XMPP_LOG_CONTEXT, frmt, ##__VA_ARGS__)
|
||||
|
||||
#define XMPPLogCTrace() XMPP_LOG_C_MAYBE(XMPP_LOG_ASYNC_TRACE, xmppLogLevel, XMPP_LOG_FLAG_TRACE, \
|
||||
XMPP_LOG_CONTEXT, @"%@: %s", THIS_FILE, __FUNCTION__)
|
||||
|
||||
#define XMPPLogCTrace2(frmt, ...) XMPP_LOG_C_MAYBE(XMPP_LOG_ASYNC_TRACE, xmppLogLevel, XMPP_LOG_FLAG_TRACE, \
|
||||
XMPP_LOG_CONTEXT, frmt, ##__VA_ARGS__)
|
||||
|
||||
// Setup logging for XMPPStream (and subclasses such as XMPPStreamFacebook)
|
||||
|
||||
#define XMPP_LOG_FLAG_SEND (1 << 5)
|
||||
#define XMPP_LOG_FLAG_RECV_PRE (1 << 6) // Prints data before it goes to the parser
|
||||
#define XMPP_LOG_FLAG_RECV_POST (1 << 7) // Prints data as it comes out of the parser
|
||||
|
||||
#define XMPP_LOG_FLAG_SEND_RECV (XMPP_LOG_FLAG_SEND | XMPP_LOG_FLAG_RECV_POST)
|
||||
|
||||
#define XMPP_LOG_SEND (xmppLogLevel & XMPP_LOG_FLAG_SEND)
|
||||
#define XMPP_LOG_RECV_PRE (xmppLogLevel & XMPP_LOG_FLAG_RECV_PRE)
|
||||
#define XMPP_LOG_RECV_POST (xmppLogLevel & XMPP_LOG_FLAG_RECV_POST)
|
||||
|
||||
#define XMPP_LOG_ASYNC_SEND (YES && XMPP_LOG_ASYNC_ENABLED)
|
||||
#define XMPP_LOG_ASYNC_RECV_PRE (YES && XMPP_LOG_ASYNC_ENABLED)
|
||||
#define XMPP_LOG_ASYNC_RECV_POST (YES && XMPP_LOG_ASYNC_ENABLED)
|
||||
|
||||
#define XMPPLogSend(format, ...) XMPP_LOG_OBJC_MAYBE(XMPP_LOG_ASYNC_SEND, xmppLogLevel, \
|
||||
XMPP_LOG_FLAG_SEND, XMPP_LOG_CONTEXT, format, ##__VA_ARGS__)
|
||||
|
||||
#define XMPPLogRecvPre(format, ...) XMPP_LOG_OBJC_MAYBE(XMPP_LOG_ASYNC_RECV_PRE, xmppLogLevel, \
|
||||
XMPP_LOG_FLAG_RECV_PRE, XMPP_LOG_CONTEXT, format, ##__VA_ARGS__)
|
||||
|
||||
#define XMPPLogRecvPost(format, ...) XMPP_LOG_OBJC_MAYBE(XMPP_LOG_ASYNC_RECV_POST, xmppLogLevel, \
|
||||
XMPP_LOG_FLAG_RECV_POST, XMPP_LOG_CONTEXT, format, ##__VA_ARGS__)
|
||||
55
Core/XMPPMessage.h
Normal file
55
Core/XMPPMessage.h
Normal file
@ -0,0 +1,55 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "XMPPElement.h"
|
||||
|
||||
/**
|
||||
* The XMPPMessage class represents a <message> element.
|
||||
* It extends XMPPElement, which in turn extends NSXMLElement.
|
||||
* All <message> elements that go in and out of the
|
||||
* xmpp stream will automatically be converted to XMPPMessage objects.
|
||||
*
|
||||
* This class exists to provide developers an easy way to add functionality to message processing.
|
||||
* Simply add your own category to XMPPMessage to extend it with your own custom methods.
|
||||
**/
|
||||
|
||||
@interface XMPPMessage : XMPPElement
|
||||
|
||||
// Converts an NSXMLElement to an XMPPMessage element in place (no memory allocations or copying)
|
||||
+ (XMPPMessage *)messageFromElement:(NSXMLElement *)element;
|
||||
|
||||
+ (XMPPMessage *)message;
|
||||
+ (XMPPMessage *)messageWithType:(NSString *)type;
|
||||
+ (XMPPMessage *)messageWithType:(NSString *)type to:(XMPPJID *)to;
|
||||
+ (XMPPMessage *)messageWithType:(NSString *)type to:(XMPPJID *)jid elementID:(NSString *)eid;
|
||||
+ (XMPPMessage *)messageWithType:(NSString *)type to:(XMPPJID *)jid elementID:(NSString *)eid child:(NSXMLElement *)childElement;
|
||||
+ (XMPPMessage *)messageWithType:(NSString *)type elementID:(NSString *)eid;
|
||||
+ (XMPPMessage *)messageWithType:(NSString *)type elementID:(NSString *)eid child:(NSXMLElement *)childElement;
|
||||
+ (XMPPMessage *)messageWithType:(NSString *)type child:(NSXMLElement *)childElement;
|
||||
|
||||
- (id)init;
|
||||
- (id)initWithType:(NSString *)type;
|
||||
- (id)initWithType:(NSString *)type to:(XMPPJID *)to;
|
||||
- (id)initWithType:(NSString *)type to:(XMPPJID *)jid elementID:(NSString *)eid;
|
||||
- (id)initWithType:(NSString *)type to:(XMPPJID *)jid elementID:(NSString *)eid child:(NSXMLElement *)childElement;
|
||||
- (id)initWithType:(NSString *)type elementID:(NSString *)eid;
|
||||
- (id)initWithType:(NSString *)type elementID:(NSString *)eid child:(NSXMLElement *)childElement;
|
||||
- (id)initWithType:(NSString *)type child:(NSXMLElement *)childElement;
|
||||
|
||||
- (NSString *)type;
|
||||
- (NSString *)subject;
|
||||
- (NSString *)body;
|
||||
- (NSString *)bodyForLanguage:(NSString *)language;
|
||||
- (NSString *)thread;
|
||||
|
||||
- (void)addSubject:(NSString *)subject;
|
||||
- (void)addBody:(NSString *)body;
|
||||
- (void)addBody:(NSString *)body withLanguage:(NSString *)language;
|
||||
- (void)addThread:(NSString *)thread;
|
||||
|
||||
- (BOOL)isChatMessage;
|
||||
- (BOOL)isChatMessageWithBody;
|
||||
- (BOOL)isErrorMessage;
|
||||
- (BOOL)isMessageWithBody;
|
||||
|
||||
- (NSError *)errorMessage;
|
||||
|
||||
@end
|
||||
267
Core/XMPPMessage.m
Normal file
267
Core/XMPPMessage.m
Normal file
@ -0,0 +1,267 @@
|
||||
#import "XMPPMessage.h"
|
||||
#import "XMPPJID.h"
|
||||
#import "NSXMLElement+XMPP.h"
|
||||
|
||||
#import <objc/runtime.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 XMPPMessage
|
||||
|
||||
#if DEBUG
|
||||
|
||||
+ (void)initialize
|
||||
{
|
||||
// We use the object_setClass method below to dynamically change the class from a standard NSXMLElement.
|
||||
// The size of the two classes is expected to be the same.
|
||||
//
|
||||
// If a developer adds instance methods to this class, bad things happen at runtime that are very hard to debug.
|
||||
// This check is here to aid future developers who may make this mistake.
|
||||
//
|
||||
// For Fearless And Experienced Objective-C Developers:
|
||||
// It may be possible to support adding instance variables to this class if you seriously need it.
|
||||
// To do so, try realloc'ing self after altering the class, and then initialize your variables.
|
||||
|
||||
size_t superSize = class_getInstanceSize([NSXMLElement class]);
|
||||
size_t ourSize = class_getInstanceSize([XMPPMessage class]);
|
||||
|
||||
if (superSize != ourSize)
|
||||
{
|
||||
NSLog(@"Adding instance variables to XMPPMessage is not currently supported!");
|
||||
exit(15);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
+ (XMPPMessage *)messageFromElement:(NSXMLElement *)element
|
||||
{
|
||||
object_setClass(element, [XMPPMessage class]);
|
||||
|
||||
return (XMPPMessage *)element;
|
||||
}
|
||||
|
||||
+ (XMPPMessage *)message
|
||||
{
|
||||
return [[XMPPMessage alloc] init];
|
||||
}
|
||||
|
||||
+ (XMPPMessage *)messageWithType:(NSString *)type
|
||||
{
|
||||
return [[XMPPMessage alloc] initWithType:type to:nil];
|
||||
}
|
||||
|
||||
+ (XMPPMessage *)messageWithType:(NSString *)type to:(XMPPJID *)to
|
||||
{
|
||||
return [[XMPPMessage alloc] initWithType:type to:to];
|
||||
}
|
||||
|
||||
+ (XMPPMessage *)messageWithType:(NSString *)type to:(XMPPJID *)jid elementID:(NSString *)eid
|
||||
{
|
||||
return [[XMPPMessage alloc] initWithType:type to:jid elementID:eid];
|
||||
}
|
||||
|
||||
+ (XMPPMessage *)messageWithType:(NSString *)type to:(XMPPJID *)jid elementID:(NSString *)eid child:(NSXMLElement *)childElement
|
||||
{
|
||||
return [[XMPPMessage alloc] initWithType:type to:jid elementID:eid child:childElement];
|
||||
}
|
||||
|
||||
+ (XMPPMessage *)messageWithType:(NSString *)type elementID:(NSString *)eid
|
||||
{
|
||||
return [[XMPPMessage alloc] initWithType:type elementID:eid];
|
||||
}
|
||||
|
||||
+ (XMPPMessage *)messageWithType:(NSString *)type elementID:(NSString *)eid child:(NSXMLElement *)childElement
|
||||
{
|
||||
return [[XMPPMessage alloc] initWithType:type elementID:eid child:childElement];
|
||||
}
|
||||
|
||||
+ (XMPPMessage *)messageWithType:(NSString *)type child:(NSXMLElement *)childElement
|
||||
{
|
||||
return [[XMPPMessage alloc] initWithType:type child:childElement];
|
||||
}
|
||||
|
||||
- (id)init
|
||||
{
|
||||
return [self initWithType:nil to:nil elementID:nil child:nil];
|
||||
}
|
||||
|
||||
- (id)initWithType:(NSString *)type
|
||||
{
|
||||
return [self initWithType:type to:nil elementID:nil child:nil];
|
||||
}
|
||||
|
||||
- (id)initWithType:(NSString *)type to:(XMPPJID *)jid
|
||||
{
|
||||
return [self initWithType:type to:jid elementID:nil child:nil];
|
||||
}
|
||||
|
||||
- (id)initWithType:(NSString *)type to:(XMPPJID *)jid elementID:(NSString *)eid
|
||||
{
|
||||
return [self initWithType:type to:jid elementID:eid child:nil];
|
||||
}
|
||||
|
||||
- (id)initWithType:(NSString *)type to:(XMPPJID *)jid elementID:(NSString *)eid child:(NSXMLElement *)childElement
|
||||
{
|
||||
if ((self = [super initWithName:@"message"]))
|
||||
{
|
||||
if (type)
|
||||
[self addAttributeWithName:@"type" stringValue:type];
|
||||
|
||||
if (jid)
|
||||
[self addAttributeWithName:@"to" stringValue:[jid full]];
|
||||
|
||||
if (eid)
|
||||
[self addAttributeWithName:@"id" stringValue:eid];
|
||||
|
||||
if (childElement)
|
||||
[self addChild:childElement];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)initWithType:(NSString *)type elementID:(NSString *)eid
|
||||
{
|
||||
return [self initWithType:type to:nil elementID:eid child:nil];
|
||||
}
|
||||
|
||||
- (id)initWithType:(NSString *)type elementID:(NSString *)eid child:(NSXMLElement *)childElement
|
||||
{
|
||||
return [self initWithType:type to:nil elementID:eid child:childElement];
|
||||
}
|
||||
|
||||
- (id)initWithType:(NSString *)type child:(NSXMLElement *)childElement
|
||||
{
|
||||
return [self initWithType:type to:nil elementID:nil child:childElement];
|
||||
}
|
||||
|
||||
- (id)initWithXMLString:(NSString *)string error:(NSError *__autoreleasing *)error
|
||||
{
|
||||
if((self = [super initWithXMLString:string error:error])){
|
||||
self = [XMPPMessage messageFromElement:self];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)copyWithZone:(NSZone *)zone
|
||||
{
|
||||
NSXMLElement *element = [super copyWithZone:zone];
|
||||
return [XMPPMessage messageFromElement:element];
|
||||
}
|
||||
|
||||
- (NSString *)type
|
||||
{
|
||||
return [[self attributeForName:@"type"] stringValue];
|
||||
}
|
||||
|
||||
- (NSString *)subject
|
||||
{
|
||||
return [[self elementForName:@"subject"] stringValue];
|
||||
}
|
||||
|
||||
- (NSString *)body
|
||||
{
|
||||
return [[self elementForName:@"body"] stringValue];
|
||||
}
|
||||
|
||||
- (NSString *)bodyForLanguage:(NSString *)language
|
||||
{
|
||||
NSString *bodyForLanguage = nil;
|
||||
|
||||
for (NSXMLElement *bodyElement in [self elementsForName:@"body"])
|
||||
{
|
||||
NSString *lang = [[bodyElement attributeForName:@"xml:lang"] stringValue];
|
||||
|
||||
// Openfire strips off the xml prefix
|
||||
if (lang == nil)
|
||||
{
|
||||
lang = [[bodyElement attributeForName:@"lang"] stringValue];
|
||||
}
|
||||
|
||||
if ([language isEqualToString:lang] || ([language length] == 0 && [lang length] == 0))
|
||||
{
|
||||
bodyForLanguage = [bodyElement stringValue];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return bodyForLanguage;
|
||||
}
|
||||
|
||||
- (NSString *)thread
|
||||
{
|
||||
return [[self elementForName:@"thread"] stringValue];
|
||||
}
|
||||
|
||||
- (void)addSubject:(NSString *)subject
|
||||
{
|
||||
NSXMLElement *subjectElement = [NSXMLElement elementWithName:@"subject" stringValue:subject];
|
||||
[self addChild:subjectElement];
|
||||
}
|
||||
|
||||
- (void)addBody:(NSString *)body
|
||||
{
|
||||
NSXMLElement *bodyElement = [NSXMLElement elementWithName:@"body" stringValue:body];
|
||||
[self addChild:bodyElement];
|
||||
}
|
||||
|
||||
- (void)addBody:(NSString *)body withLanguage:(NSString *)language
|
||||
{
|
||||
NSXMLElement *bodyElement = [NSXMLElement elementWithName:@"body" stringValue:body];
|
||||
|
||||
if ([language length])
|
||||
{
|
||||
[bodyElement addAttributeWithName:@"xml:lang" stringValue:language];
|
||||
}
|
||||
|
||||
[self addChild:bodyElement];
|
||||
}
|
||||
|
||||
- (void)addThread:(NSString *)thread
|
||||
{
|
||||
NSXMLElement *threadElement = [NSXMLElement elementWithName:@"thread" stringValue:thread];
|
||||
[self addChild:threadElement];
|
||||
}
|
||||
|
||||
- (BOOL)isChatMessage
|
||||
{
|
||||
return [[[self attributeForName:@"type"] stringValue] isEqualToString:@"chat"];
|
||||
}
|
||||
|
||||
- (BOOL)isChatMessageWithBody
|
||||
{
|
||||
if ([self isChatMessage])
|
||||
{
|
||||
return [self isMessageWithBody];
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (BOOL)isErrorMessage
|
||||
{
|
||||
return [[[self attributeForName:@"type"] stringValue] isEqualToString:@"error"];
|
||||
}
|
||||
|
||||
- (NSError *)errorMessage
|
||||
{
|
||||
if (![self isErrorMessage]) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSXMLElement *error = [self elementForName:@"error"];
|
||||
return [NSError errorWithDomain:@"urn:ietf:params:xml:ns:xmpp-stanzas"
|
||||
code:[error attributeIntValueForName:@"code"]
|
||||
userInfo:@{NSLocalizedDescriptionKey : [error compactXMLString]}];
|
||||
|
||||
}
|
||||
|
||||
- (BOOL)isMessageWithBody
|
||||
{
|
||||
return ([self elementForName:@"body"] != nil);
|
||||
}
|
||||
|
||||
@end
|
||||
43
Core/XMPPModule.h
Normal file
43
Core/XMPPModule.h
Normal file
@ -0,0 +1,43 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "GCDMulticastDelegate.h"
|
||||
|
||||
@class XMPPStream;
|
||||
|
||||
/**
|
||||
* XMPPModule is the base class that all extensions/modules inherit.
|
||||
* They automatically get:
|
||||
*
|
||||
* - A dispatch queue.
|
||||
* - A multicast delegate that automatically invokes added delegates.
|
||||
*
|
||||
* The module also automatically registers/unregisters itself with the
|
||||
* xmpp stream during the activate/deactive methods.
|
||||
**/
|
||||
@interface XMPPModule : NSObject
|
||||
{
|
||||
XMPPStream *xmppStream;
|
||||
|
||||
dispatch_queue_t moduleQueue;
|
||||
void *moduleQueueTag;
|
||||
|
||||
id multicastDelegate;
|
||||
}
|
||||
|
||||
@property (readonly) dispatch_queue_t moduleQueue;
|
||||
@property (readonly) void *moduleQueueTag;
|
||||
|
||||
@property (strong, readonly) XMPPStream *xmppStream;
|
||||
|
||||
- (id)init;
|
||||
- (id)initWithDispatchQueue:(dispatch_queue_t)queue;
|
||||
|
||||
- (BOOL)activate:(XMPPStream *)aXmppStream;
|
||||
- (void)deactivate;
|
||||
|
||||
- (void)addDelegate:(id)delegate delegateQueue:(dispatch_queue_t)delegateQueue;
|
||||
- (void)removeDelegate:(id)delegate delegateQueue:(dispatch_queue_t)delegateQueue;
|
||||
- (void)removeDelegate:(id)delegate;
|
||||
|
||||
- (NSString *)moduleName;
|
||||
|
||||
@end
|
||||
224
Core/XMPPModule.m
Normal file
224
Core/XMPPModule.m
Normal file
@ -0,0 +1,224 @@
|
||||
#import "XMPPModule.h"
|
||||
#import "XMPPStream.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
|
||||
|
||||
|
||||
@implementation XMPPModule
|
||||
|
||||
/**
|
||||
* Standard init method.
|
||||
**/
|
||||
- (id)init
|
||||
{
|
||||
return [self initWithDispatchQueue:NULL];
|
||||
}
|
||||
|
||||
/**
|
||||
* Designated initializer.
|
||||
**/
|
||||
- (id)initWithDispatchQueue:(dispatch_queue_t)queue
|
||||
{
|
||||
if ((self = [super init]))
|
||||
{
|
||||
if (queue)
|
||||
{
|
||||
moduleQueue = queue;
|
||||
#if !OS_OBJECT_USE_OBJC
|
||||
dispatch_retain(moduleQueue);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
const char *moduleQueueName = [[self moduleName] UTF8String];
|
||||
moduleQueue = dispatch_queue_create(moduleQueueName, NULL);
|
||||
}
|
||||
|
||||
moduleQueueTag = &moduleQueueTag;
|
||||
dispatch_queue_set_specific(moduleQueue, moduleQueueTag, moduleQueueTag, NULL);
|
||||
|
||||
multicastDelegate = [[GCDMulticastDelegate alloc] init];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
#if !OS_OBJECT_USE_OBJC
|
||||
dispatch_release(moduleQueue);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* The activate method is the point at which the module gets plugged into the xmpp stream.
|
||||
*
|
||||
* It is recommended that subclasses override didActivate, instead of this method,
|
||||
* to perform any custom actions upon activation.
|
||||
**/
|
||||
- (BOOL)activate:(XMPPStream *)aXmppStream
|
||||
{
|
||||
__block BOOL result = YES;
|
||||
|
||||
dispatch_block_t block = ^{
|
||||
|
||||
if (xmppStream != nil)
|
||||
{
|
||||
result = NO;
|
||||
}
|
||||
else
|
||||
{
|
||||
xmppStream = aXmppStream;
|
||||
|
||||
[xmppStream addDelegate:self delegateQueue:moduleQueue];
|
||||
[xmppStream registerModule:self];
|
||||
|
||||
[self didActivate];
|
||||
}
|
||||
};
|
||||
|
||||
if (dispatch_get_specific(moduleQueueTag))
|
||||
block();
|
||||
else
|
||||
dispatch_sync(moduleQueue, block);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* It is recommended that subclasses override this method (instead of activate:)
|
||||
* to perform tasks after the module has been activated.
|
||||
*
|
||||
* This method is only invoked if the module is successfully activated.
|
||||
* This method is always invoked on the moduleQueue.
|
||||
**/
|
||||
- (void)didActivate
|
||||
{
|
||||
// Override me to do custom work after the module is activated
|
||||
}
|
||||
|
||||
/**
|
||||
* The deactivate method unplugs a module from the xmpp stream.
|
||||
* When this method returns, no further delegate methods on this module will be dispatched.
|
||||
* However, there may be delegate methods that have already been dispatched.
|
||||
* If this is the case, the module will be properly retained until the delegate methods have completed.
|
||||
* If your custom module requires that delegate methods are not run after the deactivate method has been run,
|
||||
* then simply check the xmppStream variable in your delegate methods.
|
||||
*
|
||||
* It is recommended that subclasses override didDeactivate, instead of this method,
|
||||
* to perform any custom actions upon deactivation.
|
||||
**/
|
||||
- (void)deactivate
|
||||
{
|
||||
dispatch_block_t block = ^{
|
||||
|
||||
if (xmppStream)
|
||||
{
|
||||
[self willDeactivate];
|
||||
|
||||
[xmppStream removeDelegate:self delegateQueue:moduleQueue];
|
||||
[xmppStream unregisterModule:self];
|
||||
|
||||
xmppStream = nil;
|
||||
}
|
||||
};
|
||||
|
||||
if (dispatch_get_specific(moduleQueueTag))
|
||||
block();
|
||||
else
|
||||
dispatch_sync(moduleQueue, block);
|
||||
}
|
||||
|
||||
/**
|
||||
* It is recommended that subclasses override this method (instead of deactivate:)
|
||||
* to perform tasks after the module has been deactivated.
|
||||
*
|
||||
* This method is only invoked if the module is transitioning from activated to deactivated.
|
||||
* This method is always invoked on the moduleQueue.
|
||||
**/
|
||||
- (void)willDeactivate
|
||||
{
|
||||
// Override me to do custom work after the module is deactivated
|
||||
}
|
||||
|
||||
- (dispatch_queue_t)moduleQueue
|
||||
{
|
||||
return moduleQueue;
|
||||
}
|
||||
|
||||
- (void *)moduleQueueTag
|
||||
{
|
||||
return moduleQueueTag;
|
||||
}
|
||||
|
||||
- (XMPPStream *)xmppStream
|
||||
{
|
||||
if (dispatch_get_specific(moduleQueueTag))
|
||||
{
|
||||
return xmppStream;
|
||||
}
|
||||
else
|
||||
{
|
||||
__block XMPPStream *result;
|
||||
|
||||
dispatch_sync(moduleQueue, ^{
|
||||
result = xmppStream;
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)addDelegate:(id)delegate delegateQueue:(dispatch_queue_t)delegateQueue
|
||||
{
|
||||
// Asynchronous operation (if outside xmppQueue)
|
||||
|
||||
dispatch_block_t block = ^{
|
||||
[multicastDelegate addDelegate:delegate delegateQueue:delegateQueue];
|
||||
};
|
||||
|
||||
if (dispatch_get_specific(moduleQueueTag))
|
||||
block();
|
||||
else
|
||||
dispatch_async(moduleQueue, block);
|
||||
}
|
||||
|
||||
- (void)removeDelegate:(id)delegate delegateQueue:(dispatch_queue_t)delegateQueue synchronously:(BOOL)synchronously
|
||||
{
|
||||
dispatch_block_t block = ^{
|
||||
[multicastDelegate removeDelegate:delegate delegateQueue:delegateQueue];
|
||||
};
|
||||
|
||||
if (dispatch_get_specific(moduleQueueTag))
|
||||
block();
|
||||
else if (synchronously)
|
||||
dispatch_sync(moduleQueue, block);
|
||||
else
|
||||
dispatch_async(moduleQueue, block);
|
||||
|
||||
}
|
||||
- (void)removeDelegate:(id)delegate delegateQueue:(dispatch_queue_t)delegateQueue
|
||||
{
|
||||
// Synchronous operation (common-case default)
|
||||
|
||||
[self removeDelegate:delegate delegateQueue:delegateQueue synchronously:YES];
|
||||
}
|
||||
|
||||
- (void)removeDelegate:(id)delegate
|
||||
{
|
||||
// Synchronous operation (common-case default)
|
||||
|
||||
[self removeDelegate:delegate delegateQueue:NULL synchronously:YES];
|
||||
}
|
||||
|
||||
- (NSString *)moduleName
|
||||
{
|
||||
// Override me (if needed) to provide a customized module name.
|
||||
// This name is used as the name of the dispatch_queue which could aid in debugging.
|
||||
|
||||
return NSStringFromClass([self class]);
|
||||
}
|
||||
|
||||
@end
|
||||
40
Core/XMPPParser.h
Normal file
40
Core/XMPPParser.h
Normal file
@ -0,0 +1,40 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
#import "DDXML.h"
|
||||
#endif
|
||||
|
||||
|
||||
@interface XMPPParser : NSObject
|
||||
|
||||
- (id)initWithDelegate:(id)delegate delegateQueue:(dispatch_queue_t)dq;
|
||||
- (id)initWithDelegate:(id)delegate delegateQueue:(dispatch_queue_t)dq parserQueue:(dispatch_queue_t)pq;
|
||||
|
||||
- (void)setDelegate:(id)delegate delegateQueue:(dispatch_queue_t)delegateQueue;
|
||||
|
||||
/**
|
||||
* Asynchronously parses the given data.
|
||||
* The delegate methods will be dispatch_async'd as events occur.
|
||||
**/
|
||||
- (void)parseData:(NSData *)data;
|
||||
|
||||
@end
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark -
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@protocol XMPPParserDelegate
|
||||
@optional
|
||||
|
||||
- (void)xmppParser:(XMPPParser *)sender didReadRoot:(NSXMLElement *)root;
|
||||
|
||||
- (void)xmppParser:(XMPPParser *)sender didReadElement:(NSXMLElement *)element;
|
||||
|
||||
- (void)xmppParserDidEnd:(XMPPParser *)sender;
|
||||
|
||||
- (void)xmppParser:(XMPPParser *)sender didFail:(NSError *)error;
|
||||
|
||||
- (void)xmppParserDidParseData:(XMPPParser *)sender;
|
||||
|
||||
@end
|
||||
850
Core/XMPPParser.m
Normal file
850
Core/XMPPParser.m
Normal file
@ -0,0 +1,850 @@
|
||||
#import "XMPPParser.h"
|
||||
#import "XMPPLogging.h"
|
||||
#import <libxml/parser.h>
|
||||
#import <libxml/parserInternals.h>
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
#import "DDXMLPrivate.h"
|
||||
#endif
|
||||
|
||||
#if ! __has_feature(objc_arc)
|
||||
#warning This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC).
|
||||
#endif
|
||||
|
||||
|
||||
#define CHECK_FOR_NULL(value) \
|
||||
do { \
|
||||
if (value == NULL) { \
|
||||
xmpp_xmlAbortDueToMemoryShortage(ctxt); \
|
||||
return; \
|
||||
} \
|
||||
} while(false)
|
||||
|
||||
#if !TARGET_OS_IPHONE
|
||||
static void xmpp_recursiveAddChild(NSXMLElement *parent, xmlNodePtr childNode);
|
||||
#endif
|
||||
|
||||
@implementation XMPPParser
|
||||
{
|
||||
#if __has_feature(objc_arc_weak)
|
||||
__weak id delegate;
|
||||
#else
|
||||
__unsafe_unretained id delegate;
|
||||
#endif
|
||||
dispatch_queue_t delegateQueue;
|
||||
|
||||
dispatch_queue_t parserQueue;
|
||||
void *xmppParserQueueTag;
|
||||
|
||||
BOOL hasReportedRoot;
|
||||
unsigned depth;
|
||||
|
||||
xmlParserCtxt *parserCtxt;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark iPhone
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
|
||||
static void xmpp_onDidReadRoot(XMPPParser *parser, xmlNodePtr root)
|
||||
{
|
||||
if (parser->delegateQueue && [parser->delegate respondsToSelector:@selector(xmppParser:didReadRoot:)])
|
||||
{
|
||||
// We first copy the root node.
|
||||
// We do this to allow the delegate to retain and make changes to the reported root
|
||||
// without affecting the underlying xmpp parser.
|
||||
|
||||
// xmlCopyNode(const xmlNodePtr node, int extended)
|
||||
//
|
||||
// node:
|
||||
// the node to copy
|
||||
// extended:
|
||||
// if 1 do a recursive copy (properties, namespaces and children when applicable)
|
||||
// if 2 copy properties and namespaces (when applicable)
|
||||
|
||||
xmlNodePtr rootCopy = xmlCopyNode(root, 2);
|
||||
DDXMLElement *rootCopyWrapper = [DDXMLElement nodeWithElementPrimitive:rootCopy owner:nil];
|
||||
|
||||
__strong id theDelegate = parser->delegate;
|
||||
|
||||
dispatch_async(parser->delegateQueue, ^{ @autoreleasepool {
|
||||
|
||||
[theDelegate xmppParser:parser didReadRoot:rootCopyWrapper];
|
||||
}});
|
||||
|
||||
// Note: DDXMLElement will properly free the rootCopy when it's deallocated.
|
||||
}
|
||||
}
|
||||
|
||||
static void xmpp_onDidReadElement(XMPPParser *parser, xmlNodePtr child)
|
||||
{
|
||||
// Detach the child from the xml tree.
|
||||
//
|
||||
// clean: Nullify next, prev, parent and doc pointers of child.
|
||||
// fixNamespaces: Recurse through subtree, and ensure no namespaces are pointing to xmlNs nodes outside the tree.
|
||||
// E.G. in a parent node that will no longer be available after the child is detached.
|
||||
//
|
||||
// We don't need to fix namespaces since we used xmpp_xmlSearchNs() to ensure we never created any
|
||||
// namespaces outside the subtree of the child in the first place.
|
||||
|
||||
[DDXMLNode detachChild:child andClean:YES andFixNamespaces:NO];
|
||||
|
||||
DDXMLElement *childWrapper = [DDXMLElement nodeWithElementPrimitive:child owner:nil];
|
||||
|
||||
// Note: We want to detach the child from the root even if the delegate method isn't setup.
|
||||
// This prevents the doc from growing infinitely large.
|
||||
|
||||
if (parser->delegateQueue && [parser->delegate respondsToSelector:@selector(xmppParser:didReadElement:)])
|
||||
{
|
||||
__strong id theDelegate = parser->delegate;
|
||||
|
||||
dispatch_async(parser->delegateQueue, ^{ @autoreleasepool {
|
||||
|
||||
[theDelegate xmppParser:parser didReadElement:childWrapper];
|
||||
}});
|
||||
}
|
||||
|
||||
// Note: DDXMLElement will properly free the child when it's deallocated.
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark Mac
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#else
|
||||
|
||||
static void xmpp_setName(NSXMLElement *element, xmlNodePtr node)
|
||||
{
|
||||
// Remember: The NSString initWithUTF8String raises an exception if passed NULL
|
||||
|
||||
if (node->name == NULL)
|
||||
{
|
||||
[element setName:@""];
|
||||
return;
|
||||
}
|
||||
|
||||
if ((node->ns != NULL) && (node->ns->prefix != NULL))
|
||||
{
|
||||
// E.g: <deusty:element xmlns:deusty="deusty.com"/>
|
||||
|
||||
NSString *prefix = [[NSString alloc] initWithUTF8String:(const char *)node->ns->prefix];
|
||||
NSString *name = [[NSString alloc] initWithUTF8String:(const char *)node->name];
|
||||
|
||||
NSString *elementName = [[NSString alloc] initWithFormat:@"%@:%@", prefix, name];
|
||||
[element setName:elementName];
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
NSString *elementName = [[NSString alloc] initWithUTF8String:(const char *)node->name];
|
||||
[element setName:elementName];
|
||||
}
|
||||
}
|
||||
|
||||
static void xmpp_addNamespaces(NSXMLElement *element, xmlNodePtr node)
|
||||
{
|
||||
// Remember: The NSString initWithUTF8String raises an exception if passed NULL
|
||||
|
||||
xmlNsPtr nsNode = node->nsDef;
|
||||
while (nsNode != NULL)
|
||||
{
|
||||
if (nsNode->href == NULL)
|
||||
{
|
||||
// Namespace doesn't have a value!
|
||||
}
|
||||
else
|
||||
{
|
||||
NSXMLNode *ns = [[NSXMLNode alloc] initWithKind:NSXMLNamespaceKind];
|
||||
|
||||
if (nsNode->prefix != NULL)
|
||||
{
|
||||
NSString *nsName = [[NSString alloc] initWithUTF8String:(const char *)nsNode->prefix];
|
||||
[ns setName:nsName];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Default namespace.
|
||||
// E.g: xmlns="deusty.com"
|
||||
|
||||
[ns setName:@""];
|
||||
}
|
||||
|
||||
NSString *nsValue = [[NSString alloc] initWithUTF8String:(const char *)nsNode->href];
|
||||
[ns setStringValue:nsValue];
|
||||
|
||||
[element addNamespace:ns];
|
||||
}
|
||||
|
||||
nsNode = nsNode->next;
|
||||
}
|
||||
}
|
||||
|
||||
static void xmpp_addChildren(NSXMLElement *element, xmlNodePtr node)
|
||||
{
|
||||
// Remember: The NSString initWithUTF8String raises an exception if passed NULL
|
||||
|
||||
xmlNodePtr childNode = node->children;
|
||||
while (childNode != NULL)
|
||||
{
|
||||
if (childNode->type == XML_ELEMENT_NODE)
|
||||
{
|
||||
xmpp_recursiveAddChild(element, childNode);
|
||||
}
|
||||
else if (childNode->type == XML_TEXT_NODE)
|
||||
{
|
||||
if (childNode->content != NULL)
|
||||
{
|
||||
NSString *value = [[NSString alloc] initWithUTF8String:(const char *)childNode->content];
|
||||
[element setStringValue:value];
|
||||
}
|
||||
}
|
||||
|
||||
childNode = childNode->next;
|
||||
}
|
||||
}
|
||||
|
||||
static void xmpp_addAttributes(NSXMLElement *element, xmlNodePtr node)
|
||||
{
|
||||
// Remember: The NSString initWithUTF8String raises an exception if passed NULL
|
||||
|
||||
xmlAttrPtr attrNode = node->properties;
|
||||
while (attrNode != NULL)
|
||||
{
|
||||
if (attrNode->name == NULL)
|
||||
{
|
||||
// Attribute doesn't have a name!
|
||||
}
|
||||
else if (attrNode->children == NULL)
|
||||
{
|
||||
// Attribute doesn't have a value node!
|
||||
}
|
||||
else if (attrNode->children->content == NULL)
|
||||
{
|
||||
// Attribute doesn't have a value!
|
||||
}
|
||||
else
|
||||
{
|
||||
NSXMLNode *attr = [[NSXMLNode alloc] initWithKind:NSXMLAttributeKind];
|
||||
|
||||
if ((attrNode->ns != NULL) && (attrNode->ns->prefix != NULL))
|
||||
{
|
||||
// E.g: <element xmlns:deusty="deusty.com" deusty:attr="value"/>
|
||||
|
||||
NSString *prefix = [[NSString alloc] initWithUTF8String:(const char *)attrNode->ns->prefix];
|
||||
NSString *name = [[NSString alloc] initWithUTF8String:(const char *)attrNode->name];
|
||||
|
||||
NSString *attrName = [[NSString alloc] initWithFormat:@"%@:%@", prefix, name];
|
||||
[attr setName:attrName];
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
NSString *attrName = [[NSString alloc] initWithUTF8String:(const char *)attrNode->name];
|
||||
[attr setName:attrName];
|
||||
}
|
||||
|
||||
NSString *attrValue = [[NSString alloc] initWithUTF8String:(const char *)attrNode->children->content];
|
||||
[attr setStringValue:attrValue];
|
||||
|
||||
[element addAttribute:attr];
|
||||
}
|
||||
|
||||
attrNode = attrNode->next;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively adds all the child elements to the given parent.
|
||||
*
|
||||
* Note: This method is almost the same as xmpp_nsxmlFromLibxml, with one important difference.
|
||||
* It doen't add any objects to the autorelease pool (xmpp_nsxmlFromLibXml has return value).
|
||||
**/
|
||||
static void xmpp_recursiveAddChild(NSXMLElement *parent, xmlNodePtr childNode)
|
||||
{
|
||||
// Remember: The NSString initWithUTF8String raises an exception if passed NULL
|
||||
|
||||
NSXMLElement *child = [[NSXMLElement alloc] initWithKind:NSXMLElementKind];
|
||||
|
||||
xmpp_setName(child, childNode);
|
||||
|
||||
xmpp_addNamespaces(child, childNode);
|
||||
|
||||
xmpp_addChildren(child, childNode);
|
||||
xmpp_addAttributes(child, childNode);
|
||||
|
||||
[parent addChild:child];
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and returns an NSXMLElement from the given node.
|
||||
* Use this method after finding the root element, or root.child element.
|
||||
**/
|
||||
static NSXMLElement* xmpp_nsxmlFromLibxml(xmlNodePtr rootNode)
|
||||
{
|
||||
// Remember: The NSString initWithUTF8String raises an exception if passed NULL
|
||||
|
||||
NSXMLElement *root = [[NSXMLElement alloc] initWithKind:NSXMLElementKind];
|
||||
|
||||
xmpp_setName(root, rootNode);
|
||||
|
||||
xmpp_addNamespaces(root, rootNode);
|
||||
|
||||
xmpp_addChildren(root, rootNode);
|
||||
xmpp_addAttributes(root, rootNode);
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
static void xmpp_onDidReadRoot(XMPPParser *parser, xmlNodePtr root)
|
||||
{
|
||||
if (parser->delegateQueue && [parser->delegate respondsToSelector:@selector(xmppParser:didReadRoot:)])
|
||||
{
|
||||
NSXMLElement *nsRoot = xmpp_nsxmlFromLibxml(root);
|
||||
|
||||
__strong id theDelegate = parser->delegate;
|
||||
|
||||
dispatch_async(parser->delegateQueue, ^{ @autoreleasepool {
|
||||
|
||||
[theDelegate xmppParser:parser didReadRoot:nsRoot];
|
||||
}});
|
||||
}
|
||||
}
|
||||
|
||||
static void xmpp_onDidReadElement(XMPPParser *parser, xmlNodePtr child)
|
||||
{
|
||||
if (parser->delegateQueue && [parser->delegate respondsToSelector:@selector(xmppParser:didReadElement:)])
|
||||
{
|
||||
NSXMLElement *nsChild = xmpp_nsxmlFromLibxml(child);
|
||||
|
||||
__strong id theDelegate = parser->delegate;
|
||||
|
||||
dispatch_async(parser->delegateQueue, ^{ @autoreleasepool {
|
||||
|
||||
[theDelegate xmppParser:parser didReadElement:nsChild];
|
||||
}});
|
||||
}
|
||||
|
||||
// Note: We want to detach the child from the root even if the delegate method isn't setup.
|
||||
// This prevents the doc from growing infinitely large.
|
||||
|
||||
// Detach and free child to keep memory footprint small
|
||||
xmlUnlinkNode(child);
|
||||
xmlFreeNode(child);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark Common
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* This method is called at the end of the xmlStartElement method.
|
||||
* This allows us to inspect the parser and xml tree, and determine if we need to invoke any delegate methods.
|
||||
**/
|
||||
static void xmpp_postStartElement(xmlParserCtxt *ctxt)
|
||||
{
|
||||
XMPPParser *parser = (__bridge XMPPParser *)ctxt->_private;
|
||||
parser->depth++;
|
||||
|
||||
if (!(parser->hasReportedRoot) && (parser->depth == 1))
|
||||
{
|
||||
// We've received the full root - report it to the delegate
|
||||
|
||||
if (ctxt->myDoc)
|
||||
{
|
||||
xmlNodePtr root = xmlDocGetRootElement(ctxt->myDoc);
|
||||
if (root)
|
||||
{
|
||||
xmpp_onDidReadRoot(parser, root);
|
||||
|
||||
parser->hasReportedRoot = YES;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called at the end of the xmlEndElement method.
|
||||
* This allows us to inspect the parser and xml tree, and determine if we need to invoke any delegate methods.
|
||||
**/
|
||||
static void xmpp_postEndElement(xmlParserCtxt *ctxt)
|
||||
{
|
||||
XMPPParser *parser = (__bridge XMPPParser *)ctxt->_private;
|
||||
parser->depth--;
|
||||
|
||||
if (parser->depth == 1)
|
||||
{
|
||||
// End of full xmpp element.
|
||||
// That is, a child of the root element.
|
||||
// Extract the child, and pass it to the delegate.
|
||||
|
||||
xmlDocPtr doc = ctxt->myDoc;
|
||||
xmlNodePtr root = xmlDocGetRootElement(doc);
|
||||
|
||||
xmlNodePtr child = root->children;
|
||||
while (child != NULL)
|
||||
{
|
||||
if (child->type == XML_ELEMENT_NODE)
|
||||
{
|
||||
xmpp_onDidReadElement(parser, child);
|
||||
|
||||
// Exit while loop
|
||||
break;
|
||||
}
|
||||
|
||||
child = child->next;
|
||||
}
|
||||
}
|
||||
else if (parser->depth == 0)
|
||||
{
|
||||
// End of the root element
|
||||
|
||||
if (parser->delegateQueue && [parser->delegate respondsToSelector:@selector(xmppParserDidEnd:)])
|
||||
{
|
||||
__strong id theDelegate = parser->delegate;
|
||||
|
||||
dispatch_async(parser->delegateQueue, ^{ @autoreleasepool {
|
||||
|
||||
[theDelegate xmppParserDidEnd:parser];
|
||||
}});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* We're screwed...
|
||||
**/
|
||||
static void xmpp_xmlAbortDueToMemoryShortage(xmlParserCtxt *ctxt)
|
||||
{
|
||||
XMPPParser *parser = (__bridge XMPPParser *)ctxt->_private;
|
||||
|
||||
xmlStopParser(ctxt);
|
||||
|
||||
if (parser->delegateQueue && [parser->delegate respondsToSelector:@selector(xmppParser:didFail:)])
|
||||
{
|
||||
NSString *errMsg = @"Unable to allocate memory in xmpp parser";
|
||||
NSDictionary *info = @{NSLocalizedDescriptionKey : errMsg};
|
||||
|
||||
NSError *error = [NSError errorWithDomain:@"libxmlErrorDomain" code:1001 userInfo:info];
|
||||
|
||||
__strong id theDelegate = parser->delegate;
|
||||
|
||||
dispatch_async(parser->delegateQueue, ^{ @autoreleasepool {
|
||||
|
||||
[theDelegate xmppParser:parser didFail:error];
|
||||
}});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* (Similar to the libxml "xmlSearchNs" method, with one very important difference.)
|
||||
*
|
||||
* This method searches for an existing xmlNsPtr in the given node,
|
||||
* recursing on the parents but stopping before it reaches the root node of the document.
|
||||
*
|
||||
* Why do we skip the root node?
|
||||
* Because all nodes are going to be detached from the root node.
|
||||
* So it makes no sense to allow them to reference namespaces stored in the root node,
|
||||
* since the detach algorithm will be forced to copy the namespaces later anyway.
|
||||
**/
|
||||
static xmlNsPtr xmpp_xmlSearchNs(xmlDocPtr doc, xmlNodePtr node, const xmlChar *nameSpace)
|
||||
{
|
||||
xmlNodePtr rootNode = xmlDocGetRootElement(doc);
|
||||
|
||||
xmlNodePtr currentNode = node;
|
||||
while (currentNode && currentNode != rootNode)
|
||||
{
|
||||
xmlNsPtr currentNs = currentNode->nsDef;
|
||||
while (currentNs)
|
||||
{
|
||||
if (currentNs->href != NULL)
|
||||
{
|
||||
if ((currentNs->prefix == NULL) && (nameSpace == NULL))
|
||||
{
|
||||
return currentNs;
|
||||
}
|
||||
if ((currentNs->prefix != NULL) && (nameSpace != NULL))
|
||||
{
|
||||
if (xmlStrEqual(currentNs->prefix, nameSpace))
|
||||
return currentNs;
|
||||
}
|
||||
}
|
||||
|
||||
currentNs = currentNs->next;
|
||||
}
|
||||
|
||||
currentNode = currentNode->parent;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* SAX parser C-style callback.
|
||||
* Invoked when a new node element is started.
|
||||
**/
|
||||
static void xmpp_xmlStartElement(void *ctx, const xmlChar *nodeName,
|
||||
const xmlChar *nodePrefix,
|
||||
const xmlChar *nodeUri,
|
||||
int nb_namespaces,
|
||||
const xmlChar **namespaces,
|
||||
int nb_attributes,
|
||||
int nb_defaulted,
|
||||
const xmlChar **attributes)
|
||||
{
|
||||
int i, j;
|
||||
xmlNsPtr lastAddedNs = NULL;
|
||||
|
||||
xmlParserCtxt *ctxt = (xmlParserCtxt *)ctx;
|
||||
|
||||
// We store the parent node in the context's node pointer.
|
||||
// We keep this updated by "pushing" the node in the startElement method,
|
||||
// and "popping" the node in the endElement method.
|
||||
xmlNodePtr parent = ctxt->node;
|
||||
|
||||
// Create the node
|
||||
xmlNodePtr newNode = xmlNewDocNode(ctxt->myDoc, NULL, nodeName, NULL);
|
||||
CHECK_FOR_NULL(newNode);
|
||||
|
||||
// Add the node to the tree
|
||||
if (parent == NULL)
|
||||
{
|
||||
// Root node
|
||||
xmlAddChild((xmlNodePtr)ctxt->myDoc, newNode);
|
||||
}
|
||||
else
|
||||
{
|
||||
xmlAddChild(parent, newNode);
|
||||
}
|
||||
|
||||
// Process the namespaces
|
||||
for (i = 0, j = 0; j < nb_namespaces; j++)
|
||||
{
|
||||
// Extract namespace prefix and uri
|
||||
const xmlChar *nsPrefix = namespaces[i++];
|
||||
const xmlChar *nsUri = namespaces[i++];
|
||||
|
||||
// Create the namespace
|
||||
xmlNsPtr newNs = xmlNewNs(NULL, nsUri, nsPrefix);
|
||||
CHECK_FOR_NULL(newNs);
|
||||
|
||||
// Add namespace to node.
|
||||
// Each node has a linked list of nodes (in the nsDef variable).
|
||||
// The linked list is forward only.
|
||||
// In other words, each ns has a next, but not a prev pointer.
|
||||
|
||||
if (newNode->nsDef == NULL)
|
||||
{
|
||||
newNode->nsDef = newNs;
|
||||
lastAddedNs = newNs;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(lastAddedNs != NULL)
|
||||
{
|
||||
lastAddedNs->next = newNs;
|
||||
}
|
||||
|
||||
lastAddedNs = newNs;
|
||||
}
|
||||
|
||||
// Is this the namespace for the node?
|
||||
|
||||
if (nodeUri && (nodePrefix == nsPrefix))
|
||||
{
|
||||
// Ex 1: node == <stream:stream xmlns:stream="url"> && newNs == stream:url
|
||||
// Ex 2: node == <starttls xmlns="url"> && newNs == null:url
|
||||
|
||||
newNode->ns = newNs;
|
||||
}
|
||||
}
|
||||
|
||||
// Search for the node's namespace if it wasn't already found
|
||||
if ((nodeUri) && (newNode->ns == NULL))
|
||||
{
|
||||
newNode->ns = xmpp_xmlSearchNs(ctxt->myDoc, newNode, nodePrefix);
|
||||
|
||||
if (newNode->ns == NULL)
|
||||
{
|
||||
// We use href==NULL in the case of an element creation where the namespace was not defined.
|
||||
//
|
||||
// We do NOT use xmlNewNs(newNode, nodeUri, nodePrefix) because that method doesn't properly add
|
||||
// the namespace to BOTH nsDef and ns.
|
||||
|
||||
xmlNsPtr newNs = xmlNewNs(NULL, nodeUri, nodePrefix);
|
||||
CHECK_FOR_NULL(newNs);
|
||||
|
||||
if (newNode->nsDef == NULL)
|
||||
{
|
||||
newNode->nsDef = newNs;
|
||||
}
|
||||
else if(lastAddedNs != NULL)
|
||||
{
|
||||
lastAddedNs->next = newNs;
|
||||
}
|
||||
|
||||
newNode->ns = newNs;
|
||||
}
|
||||
}
|
||||
|
||||
// Process all the attributes
|
||||
for (i = 0, j = 0; j < nb_attributes; j++)
|
||||
{
|
||||
const xmlChar *attrName = attributes[i++];
|
||||
const xmlChar *attrPrefix = attributes[i++];
|
||||
const xmlChar *attrUri = attributes[i++];
|
||||
const xmlChar *valueBegin = attributes[i++];
|
||||
const xmlChar *valueEnd = attributes[i++];
|
||||
|
||||
// The attribute value might contain character references which need to be decoded.
|
||||
//
|
||||
// "Franks & Beans" -> "Franks & Beans"
|
||||
|
||||
xmlChar *value = xmlStringLenDecodeEntities(ctxt, // the parser context
|
||||
valueBegin, // the input string
|
||||
(int)(valueEnd - valueBegin), // the input string length
|
||||
(XML_SUBSTITUTE_REF), // what to substitue
|
||||
0, 0, 0); // end markers, 0 if none
|
||||
CHECK_FOR_NULL(value);
|
||||
|
||||
if ((attrPrefix == NULL) && (attrUri == NULL))
|
||||
{
|
||||
// Normal attribute - no associated namespace
|
||||
xmlAttrPtr newAttr = xmlNewProp(newNode, attrName, value);
|
||||
CHECK_FOR_NULL(newAttr);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Find the namespace for the attribute
|
||||
xmlNsPtr attrNs = xmpp_xmlSearchNs(ctxt->myDoc, newNode, attrPrefix);
|
||||
|
||||
if (attrNs != NULL)
|
||||
{
|
||||
xmlAttrPtr newAttr = xmlNewNsProp(newNode, attrNs, attrName, value);
|
||||
CHECK_FOR_NULL(newAttr);
|
||||
}
|
||||
else
|
||||
{
|
||||
attrNs = xmlNewNs(NULL, NULL, nodePrefix);
|
||||
CHECK_FOR_NULL(attrNs);
|
||||
|
||||
xmlAttrPtr newAttr = xmlNewNsProp(newNode, attrNs, attrName, value);
|
||||
CHECK_FOR_NULL(newAttr);
|
||||
}
|
||||
}
|
||||
|
||||
xmlFree(value);
|
||||
}
|
||||
|
||||
// Update our parent node pointer
|
||||
ctxt->node = newNode;
|
||||
|
||||
// Invoke delegate methods if needed
|
||||
xmpp_postStartElement(ctxt);
|
||||
}
|
||||
|
||||
/**
|
||||
* SAX parser C-style callback.
|
||||
* Invoked when characters are found within a node.
|
||||
**/
|
||||
static void xmpp_xmlCharacters(void *ctx, const xmlChar *ch, int len)
|
||||
{
|
||||
xmlParserCtxt *ctxt = (xmlParserCtxt *)ctx;
|
||||
|
||||
if (ctxt->node != NULL)
|
||||
{
|
||||
xmlNodePtr textNode = xmlNewTextLen(ch, len);
|
||||
|
||||
// xmlAddChild(xmlNodePtr parent, xmlNodePtr cur)
|
||||
//
|
||||
// Add a new node to @parent, at the end of the child list
|
||||
// merging adjacent TEXT nodes (in which case @cur is freed).
|
||||
|
||||
xmlAddChild(ctxt->node, textNode);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* SAX parser C-style callback.
|
||||
* Invoked when a new node element is ended.
|
||||
**/
|
||||
static void xmpp_xmlEndElement(void *ctx, const xmlChar *localname,
|
||||
const xmlChar *prefix,
|
||||
const xmlChar *URI)
|
||||
{
|
||||
xmlParserCtxt *ctxt = (xmlParserCtxt *)ctx;
|
||||
|
||||
// Update our parent node pointer
|
||||
if (ctxt->node != NULL)
|
||||
ctxt->node = ctxt->node->parent;
|
||||
|
||||
// Invoke delegate methods if needed
|
||||
xmpp_postEndElement(ctxt);
|
||||
}
|
||||
|
||||
- (id)initWithDelegate:(id)aDelegate delegateQueue:(dispatch_queue_t)dq
|
||||
{
|
||||
return [self initWithDelegate:aDelegate delegateQueue:dq parserQueue:NULL];
|
||||
}
|
||||
|
||||
- (id)initWithDelegate:(id)aDelegate delegateQueue:(dispatch_queue_t)dq parserQueue:(dispatch_queue_t)pq
|
||||
{
|
||||
if ((self = [super init]))
|
||||
{
|
||||
delegate = aDelegate;
|
||||
delegateQueue = dq;
|
||||
|
||||
#if !OS_OBJECT_USE_OBJC
|
||||
if (delegateQueue)
|
||||
dispatch_retain(delegateQueue);
|
||||
#endif
|
||||
|
||||
if (pq) {
|
||||
parserQueue = pq;
|
||||
|
||||
#if !OS_OBJECT_USE_OBJC
|
||||
dispatch_retain(parserQueue);
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
parserQueue = dispatch_queue_create("xmpp.parser", NULL);
|
||||
}
|
||||
|
||||
xmppParserQueueTag = &xmppParserQueueTag;
|
||||
dispatch_queue_set_specific(parserQueue, xmppParserQueueTag, xmppParserQueueTag, NULL);
|
||||
|
||||
hasReportedRoot = NO;
|
||||
depth = 0;
|
||||
|
||||
// Create SAX handler
|
||||
xmlSAXHandler saxHandler;
|
||||
memset(&saxHandler, 0, sizeof(xmlSAXHandler));
|
||||
|
||||
saxHandler.initialized = XML_SAX2_MAGIC;
|
||||
saxHandler.startElementNs = xmpp_xmlStartElement;
|
||||
saxHandler.characters = xmpp_xmlCharacters;
|
||||
saxHandler.endElementNs = xmpp_xmlEndElement;
|
||||
|
||||
// Create the push parser context
|
||||
parserCtxt = xmlCreatePushParserCtxt(&saxHandler, NULL, NULL, 0, NULL);
|
||||
|
||||
// Note: This method copies the saxHandler, so we don't have to keep it around.
|
||||
|
||||
// Create the document to hold the parsed elements
|
||||
parserCtxt->myDoc = xmlNewDoc(parserCtxt->version);
|
||||
|
||||
// Store reference to ourself
|
||||
parserCtxt->_private = (__bridge void *)(self);
|
||||
|
||||
// Note: The parserCtxt also has a userData variable, but it is used by the DOM building functions.
|
||||
// If we put a value there, it actually causes a crash!
|
||||
// We need to be sure to use the _private variable which libxml won't touch.
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
if (parserCtxt)
|
||||
{
|
||||
// The xmlFreeParserCtxt method will not free the created document in parserCtxt->myDoc.
|
||||
if (parserCtxt->myDoc)
|
||||
{
|
||||
// Free the created xmlDoc
|
||||
xmlFreeDoc(parserCtxt->myDoc);
|
||||
}
|
||||
|
||||
xmlFreeParserCtxt(parserCtxt);
|
||||
}
|
||||
|
||||
#if !OS_OBJECT_USE_OBJC
|
||||
if (delegateQueue)
|
||||
dispatch_release(delegateQueue);
|
||||
if (parserQueue)
|
||||
dispatch_release(parserQueue);
|
||||
#endif
|
||||
}
|
||||
|
||||
- (void)setDelegate:(id)newDelegate delegateQueue:(dispatch_queue_t)newDelegateQueue
|
||||
{
|
||||
#if !OS_OBJECT_USE_OBJC
|
||||
if (newDelegateQueue)
|
||||
dispatch_retain(newDelegateQueue);
|
||||
#endif
|
||||
|
||||
dispatch_block_t block = ^{
|
||||
|
||||
delegate = newDelegate;
|
||||
|
||||
#if !OS_OBJECT_USE_OBJC
|
||||
if (delegateQueue)
|
||||
dispatch_release(delegateQueue);
|
||||
#endif
|
||||
|
||||
delegateQueue = newDelegateQueue;
|
||||
};
|
||||
|
||||
if (dispatch_get_specific(xmppParserQueueTag))
|
||||
block();
|
||||
else
|
||||
dispatch_async(parserQueue, block);
|
||||
}
|
||||
|
||||
- (void)parseData:(NSData *)data
|
||||
{
|
||||
dispatch_block_t block = ^{ @autoreleasepool {
|
||||
|
||||
int result = xmlParseChunk(parserCtxt, (const char *)[data bytes], (int)[data length], 0);
|
||||
|
||||
if (result == 0)
|
||||
{
|
||||
if (delegateQueue && [delegate respondsToSelector:@selector(xmppParserDidParseData:)])
|
||||
{
|
||||
__strong id theDelegate = delegate;
|
||||
|
||||
dispatch_async(delegateQueue, ^{ @autoreleasepool {
|
||||
|
||||
[theDelegate xmppParserDidParseData:self];
|
||||
}});
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (delegateQueue && [delegate respondsToSelector:@selector(xmppParser:didFail:)])
|
||||
{
|
||||
NSError *error;
|
||||
|
||||
xmlError *xmlErr = xmlCtxtGetLastError(parserCtxt);
|
||||
|
||||
if (xmlErr->message)
|
||||
{
|
||||
NSString *errMsg = [NSString stringWithFormat:@"%s", xmlErr->message];
|
||||
NSDictionary *info = @{NSLocalizedDescriptionKey : errMsg};
|
||||
|
||||
error = [NSError errorWithDomain:@"libxmlErrorDomain" code:xmlErr->code userInfo:info];
|
||||
}
|
||||
else
|
||||
{
|
||||
error = [NSError errorWithDomain:@"libxmlErrorDomain" code:xmlErr->code userInfo:nil];
|
||||
}
|
||||
|
||||
__strong id theDelegate = delegate;
|
||||
|
||||
dispatch_async(delegateQueue, ^{ @autoreleasepool {
|
||||
|
||||
[theDelegate xmppParser:self didFail:error];
|
||||
}});
|
||||
}
|
||||
}
|
||||
}};
|
||||
|
||||
if (dispatch_get_specific(xmppParserQueueTag))
|
||||
block();
|
||||
else
|
||||
dispatch_async(parserQueue, block);
|
||||
}
|
||||
|
||||
@end
|
||||
38
Core/XMPPPresence.h
Normal file
38
Core/XMPPPresence.h
Normal file
@ -0,0 +1,38 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "XMPPElement.h"
|
||||
|
||||
/**
|
||||
* The XMPPPresence class represents a <presence> element.
|
||||
* It extends XMPPElement, which in turn extends NSXMLElement.
|
||||
* All <presence> elements that go in and out of the
|
||||
* xmpp stream will automatically be converted to XMPPPresence objects.
|
||||
*
|
||||
* This class exists to provide developers an easy way to add functionality to presence processing.
|
||||
* Simply add your own category to XMPPPresence to extend it with your own custom methods.
|
||||
**/
|
||||
|
||||
@interface XMPPPresence : XMPPElement
|
||||
|
||||
// Converts an NSXMLElement to an XMPPPresence element in place (no memory allocations or copying)
|
||||
+ (XMPPPresence *)presenceFromElement:(NSXMLElement *)element;
|
||||
|
||||
+ (XMPPPresence *)presence;
|
||||
+ (XMPPPresence *)presenceWithType:(NSString *)type;
|
||||
+ (XMPPPresence *)presenceWithType:(NSString *)type to:(XMPPJID *)to;
|
||||
|
||||
- (id)init;
|
||||
- (id)initWithType:(NSString *)type;
|
||||
- (id)initWithType:(NSString *)type to:(XMPPJID *)to;
|
||||
|
||||
- (NSString *)type;
|
||||
|
||||
- (NSString *)show;
|
||||
- (NSString *)status;
|
||||
|
||||
- (int)priority;
|
||||
|
||||
- (int)intShow;
|
||||
|
||||
- (BOOL)isErrorPresence;
|
||||
|
||||
@end
|
||||
145
Core/XMPPPresence.m
Normal file
145
Core/XMPPPresence.m
Normal file
@ -0,0 +1,145 @@
|
||||
#import "XMPPPresence.h"
|
||||
#import "XMPPJID.h"
|
||||
#import "NSXMLElement+XMPP.h"
|
||||
|
||||
#import <objc/runtime.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 XMPPPresence
|
||||
|
||||
#if DEBUG
|
||||
|
||||
+ (void)initialize
|
||||
{
|
||||
// We use the object_setClass method below to dynamically change the class from a standard NSXMLElement.
|
||||
// The size of the two classes is expected to be the same.
|
||||
//
|
||||
// If a developer adds instance methods to this class, bad things happen at runtime that are very hard to debug.
|
||||
// This check is here to aid future developers who may make this mistake.
|
||||
//
|
||||
// For Fearless And Experienced Objective-C Developers:
|
||||
// It may be possible to support adding instance variables to this class if you seriously need it.
|
||||
// To do so, try realloc'ing self after altering the class, and then initialize your variables.
|
||||
|
||||
size_t superSize = class_getInstanceSize([NSXMLElement class]);
|
||||
size_t ourSize = class_getInstanceSize([XMPPPresence class]);
|
||||
|
||||
if (superSize != ourSize)
|
||||
{
|
||||
NSLog(@"Adding instance variables to XMPPPresence is not currently supported!");
|
||||
exit(15);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
+ (XMPPPresence *)presenceFromElement:(NSXMLElement *)element
|
||||
{
|
||||
object_setClass(element, [XMPPPresence class]);
|
||||
|
||||
return (XMPPPresence *)element;
|
||||
}
|
||||
|
||||
+ (XMPPPresence *)presence
|
||||
{
|
||||
return [[XMPPPresence alloc] init];
|
||||
}
|
||||
|
||||
+ (XMPPPresence *)presenceWithType:(NSString *)type
|
||||
{
|
||||
return [[XMPPPresence alloc] initWithType:type to:nil];
|
||||
}
|
||||
|
||||
+ (XMPPPresence *)presenceWithType:(NSString *)type to:(XMPPJID *)to
|
||||
{
|
||||
return [[XMPPPresence alloc] initWithType:type to:to];
|
||||
}
|
||||
|
||||
- (id)init
|
||||
{
|
||||
self = [super initWithName:@"presence"];
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)initWithType:(NSString *)type
|
||||
{
|
||||
return [self initWithType:type to:nil];
|
||||
}
|
||||
|
||||
- (id)initWithType:(NSString *)type to:(XMPPJID *)to
|
||||
{
|
||||
if ((self = [super initWithName:@"presence"]))
|
||||
{
|
||||
if (type)
|
||||
[self addAttributeWithName:@"type" stringValue:type];
|
||||
|
||||
if (to)
|
||||
[self addAttributeWithName:@"to" stringValue:[to full]];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)initWithXMLString:(NSString *)string error:(NSError *__autoreleasing *)error
|
||||
{
|
||||
if((self = [super initWithXMLString:string error:error])){
|
||||
self = [XMPPPresence presenceFromElement:self];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)copyWithZone:(NSZone *)zone
|
||||
{
|
||||
NSXMLElement *element = [super copyWithZone:zone];
|
||||
return [XMPPPresence presenceFromElement:element];
|
||||
}
|
||||
|
||||
- (NSString *)type
|
||||
{
|
||||
NSString *type = [self attributeStringValueForName:@"type"];
|
||||
if(type)
|
||||
return [type lowercaseString];
|
||||
else
|
||||
return @"available";
|
||||
}
|
||||
|
||||
- (NSString *)show
|
||||
{
|
||||
return [[self elementForName:@"show"] stringValue];
|
||||
}
|
||||
|
||||
- (NSString *)status
|
||||
{
|
||||
return [[self elementForName:@"status"] stringValue];
|
||||
}
|
||||
|
||||
- (int)priority
|
||||
{
|
||||
return [[[self elementForName:@"priority"] stringValue] intValue];
|
||||
}
|
||||
|
||||
- (int)intShow
|
||||
{
|
||||
NSString *show = [self show];
|
||||
|
||||
if([show isEqualToString:@"dnd"])
|
||||
return 0;
|
||||
if([show isEqualToString:@"xa"])
|
||||
return 1;
|
||||
if([show isEqualToString:@"away"])
|
||||
return 2;
|
||||
if([show isEqualToString:@"chat"])
|
||||
return 4;
|
||||
|
||||
return 3;
|
||||
}
|
||||
|
||||
- (BOOL)isErrorPresence
|
||||
{
|
||||
return [[self type] isEqualToString:@"error"];
|
||||
}
|
||||
|
||||
@end
|
||||
1111
Core/XMPPStream.h
Normal file
1111
Core/XMPPStream.h
Normal file
File diff suppressed because it is too large
Load Diff
5105
Core/XMPPStream.m
Normal file
5105
Core/XMPPStream.m
Normal file
File diff suppressed because it is too large
Load Diff
@ -467,7 +467,7 @@
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 7.1;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
@ -500,7 +500,7 @@
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 7.1;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
|
||||
SDKROOT = iphoneos;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
VALIDATE_PRODUCT = YES;
|
||||
|
||||
@ -6,14 +6,11 @@ PODS:
|
||||
- CocoaAsyncSocket/RunLoop
|
||||
- CocoaAsyncSocket/GCD (7.4.3)
|
||||
- CocoaAsyncSocket/RunLoop (7.4.3)
|
||||
- CocoaLumberjack (2.2.0):
|
||||
- CocoaLumberjack/Default (= 2.2.0)
|
||||
- CocoaLumberjack/Extensions (= 2.2.0)
|
||||
- CocoaLumberjack/Core (2.2.0)
|
||||
- CocoaLumberjack/Default (2.2.0):
|
||||
- CocoaLumberjack (1.9.2):
|
||||
- CocoaLumberjack/Extensions (= 1.9.2)
|
||||
- CocoaLumberjack/Core (1.9.2)
|
||||
- CocoaLumberjack/Extensions (1.9.2):
|
||||
- CocoaLumberjack/Core
|
||||
- CocoaLumberjack/Extensions (2.2.0):
|
||||
- CocoaLumberjack/Default
|
||||
- FBSnapshotTestCase (2.0.7):
|
||||
- FBSnapshotTestCase/SwiftSupport (= 2.0.7)
|
||||
- FBSnapshotTestCase/Core (2.0.7)
|
||||
@ -25,9 +22,145 @@ PODS:
|
||||
- KissXML/Standard (5.0.3):
|
||||
- KissXML/Core
|
||||
- PNXMPPFramework (0.1.0):
|
||||
- PNXMPPFramework/Authentication (= 0.1.0)
|
||||
- PNXMPPFramework/BandwidthMonitor (= 0.1.0)
|
||||
- PNXMPPFramework/Categories (= 0.1.0)
|
||||
- PNXMPPFramework/Core (= 0.1.0)
|
||||
- PNXMPPFramework/CoreDataStorage (= 0.1.0)
|
||||
- PNXMPPFramework/GoogleSharedStatus (= 0.1.0)
|
||||
- PNXMPPFramework/ProcessOne (= 0.1.0)
|
||||
- PNXMPPFramework/Reconnect (= 0.1.0)
|
||||
- PNXMPPFramework/Roster (= 0.1.0)
|
||||
- PNXMPPFramework/SystemInputActivityMonitor (= 0.1.0)
|
||||
- PNXMPPFramework/Utilities (= 0.1.0)
|
||||
- PNXMPPFramework/XEP-0009 (= 0.1.0)
|
||||
- PNXMPPFramework/XEP-0012 (= 0.1.0)
|
||||
- PNXMPPFramework/XEP-0016 (= 0.1.0)
|
||||
- PNXMPPFramework/XEP-0045 (= 0.1.0)
|
||||
- PNXMPPFramework/XEP-0054 (= 0.1.0)
|
||||
- PNXMPPFramework/XEP-0059 (= 0.1.0)
|
||||
- PNXMPPFramework/XEP-0060 (= 0.1.0)
|
||||
- PNXMPPFramework/XEP-0065 (= 0.1.0)
|
||||
- PNXMPPFramework/XEP-0066 (= 0.1.0)
|
||||
- PNXMPPFramework/XEP-0082 (= 0.1.0)
|
||||
- PNXMPPFramework/XEP-0085 (= 0.1.0)
|
||||
- PNXMPPFramework/XEP-0092 (= 0.1.0)
|
||||
- PNXMPPFramework/XEP-0100 (= 0.1.0)
|
||||
- PNXMPPFramework/XEP-0106 (= 0.1.0)
|
||||
- PNXMPPFramework/XEP-0115 (= 0.1.0)
|
||||
- PNXMPPFramework/XEP-0136 (= 0.1.0)
|
||||
- PNXMPPFramework/XEP-0153 (= 0.1.0)
|
||||
- PNXMPPFramework/XEP-0172 (= 0.1.0)
|
||||
- PNXMPPFramework/XEP-0184 (= 0.1.0)
|
||||
- PNXMPPFramework/XEP-0191 (= 0.1.0)
|
||||
- PNXMPPFramework/XEP-0198 (= 0.1.0)
|
||||
- PNXMPPFramework/XEP-0199 (= 0.1.0)
|
||||
- PNXMPPFramework/XEP-0202 (= 0.1.0)
|
||||
- PNXMPPFramework/XEP-0203 (= 0.1.0)
|
||||
- PNXMPPFramework/XEP-0223 (= 0.1.0)
|
||||
- PNXMPPFramework/XEP-0224 (= 0.1.0)
|
||||
- PNXMPPFramework/XEP-0280 (= 0.1.0)
|
||||
- PNXMPPFramework/XEP-0297 (= 0.1.0)
|
||||
- PNXMPPFramework/XEP-0308 (= 0.1.0)
|
||||
- PNXMPPFramework/XEP-0333 (= 0.1.0)
|
||||
- PNXMPPFramework/XEP-0335 (= 0.1.0)
|
||||
- PNXMPPFramework/Authentication (0.1.0):
|
||||
- PNXMPPFramework/Core
|
||||
- PNXMPPFramework/BandwidthMonitor (0.1.0):
|
||||
- PNXMPPFramework/Core
|
||||
- PNXMPPFramework/Categories (0.1.0):
|
||||
- PNXMPPFramework/Core
|
||||
- PNXMPPFramework/Core (0.1.0):
|
||||
- CocoaAsyncSocket
|
||||
- CocoaLumberjack
|
||||
- CocoaLumberjack (~> 1.9)
|
||||
- KissXML
|
||||
- PNXMPPFramework/CoreDataStorage (0.1.0):
|
||||
- PNXMPPFramework/Core
|
||||
- PNXMPPFramework/GoogleSharedStatus (0.1.0):
|
||||
- PNXMPPFramework/Core
|
||||
- PNXMPPFramework/ProcessOne (0.1.0):
|
||||
- PNXMPPFramework/Core
|
||||
- PNXMPPFramework/Reconnect (0.1.0):
|
||||
- PNXMPPFramework/Core
|
||||
- PNXMPPFramework/Roster (0.1.0):
|
||||
- PNXMPPFramework/Core
|
||||
- PNXMPPFramework/CoreDataStorage
|
||||
- PNXMPPFramework/SystemInputActivityMonitor (0.1.0):
|
||||
- PNXMPPFramework/Core
|
||||
- PNXMPPFramework/Utilities (0.1.0):
|
||||
- PNXMPPFramework/Core
|
||||
- PNXMPPFramework/XEP-0009 (0.1.0):
|
||||
- PNXMPPFramework/Core
|
||||
- PNXMPPFramework/XEP-0012 (0.1.0):
|
||||
- PNXMPPFramework/Core
|
||||
- PNXMPPFramework/XEP-0016 (0.1.0):
|
||||
- PNXMPPFramework/Core
|
||||
- PNXMPPFramework/XEP-0045 (0.1.0):
|
||||
- PNXMPPFramework/Core
|
||||
- PNXMPPFramework/CoreDataStorage
|
||||
- PNXMPPFramework/XEP-0203
|
||||
- PNXMPPFramework/XEP-0054 (0.1.0):
|
||||
- PNXMPPFramework/Core
|
||||
- PNXMPPFramework/Roster
|
||||
- PNXMPPFramework/XEP-0059 (0.1.0):
|
||||
- PNXMPPFramework/Core
|
||||
- PNXMPPFramework/XEP-0060 (0.1.0):
|
||||
- PNXMPPFramework/Core
|
||||
- PNXMPPFramework/XEP-0065 (0.1.0):
|
||||
- PNXMPPFramework/Core
|
||||
- PNXMPPFramework/XEP-0066 (0.1.0):
|
||||
- PNXMPPFramework/Core
|
||||
- PNXMPPFramework/XEP-0082 (0.1.0):
|
||||
- PNXMPPFramework/Core
|
||||
- PNXMPPFramework/XEP-0085 (0.1.0):
|
||||
- PNXMPPFramework/Core
|
||||
- PNXMPPFramework/XEP-0092 (0.1.0):
|
||||
- PNXMPPFramework/Core
|
||||
- PNXMPPFramework/XEP-0100 (0.1.0):
|
||||
- PNXMPPFramework/Core
|
||||
- PNXMPPFramework/XEP-0106 (0.1.0):
|
||||
- PNXMPPFramework/Core
|
||||
- PNXMPPFramework/XEP-0115 (0.1.0):
|
||||
- PNXMPPFramework/Core
|
||||
- PNXMPPFramework/CoreDataStorage
|
||||
- PNXMPPFramework/XEP-0136 (0.1.0):
|
||||
- PNXMPPFramework/CoreDataStorage
|
||||
- PNXMPPFramework/XEP-0085
|
||||
- PNXMPPFramework/XEP-0203
|
||||
- PNXMPPFramework/XEP-0153 (0.1.0):
|
||||
- PNXMPPFramework/Core
|
||||
- PNXMPPFramework/XEP-0054
|
||||
- PNXMPPFramework/XEP-0172 (0.1.0):
|
||||
- PNXMPPFramework/Core
|
||||
- PNXMPPFramework/XEP-0184 (0.1.0):
|
||||
- PNXMPPFramework/Core
|
||||
- PNXMPPFramework/XEP-0191 (0.1.0):
|
||||
- PNXMPPFramework/Core
|
||||
- PNXMPPFramework/XEP-0198 (0.1.0):
|
||||
- PNXMPPFramework/Core
|
||||
- PNXMPPFramework/XEP-0199 (0.1.0):
|
||||
- PNXMPPFramework/Core
|
||||
- PNXMPPFramework/XEP-0202 (0.1.0):
|
||||
- PNXMPPFramework/Core
|
||||
- PNXMPPFramework/XEP-0082
|
||||
- PNXMPPFramework/XEP-0203 (0.1.0):
|
||||
- PNXMPPFramework/Core
|
||||
- PNXMPPFramework/XEP-0082
|
||||
- PNXMPPFramework/XEP-0223 (0.1.0):
|
||||
- PNXMPPFramework/Core
|
||||
- PNXMPPFramework/XEP-0224 (0.1.0):
|
||||
- PNXMPPFramework/Core
|
||||
- PNXMPPFramework/XEP-0280 (0.1.0):
|
||||
- PNXMPPFramework/Core
|
||||
- PNXMPPFramework/XEP-0297 (0.1.0):
|
||||
- PNXMPPFramework/Core
|
||||
- PNXMPPFramework/XEP-0203
|
||||
- PNXMPPFramework/XEP-0308 (0.1.0):
|
||||
- PNXMPPFramework/Core
|
||||
- PNXMPPFramework/XEP-0333 (0.1.0):
|
||||
- PNXMPPFramework/Core
|
||||
- PNXMPPFramework/XEP-0335 (0.1.0):
|
||||
- PNXMPPFramework/Core
|
||||
|
||||
DEPENDENCIES:
|
||||
- FBSnapshotTestCase
|
||||
@ -39,10 +172,10 @@ EXTERNAL SOURCES:
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
CocoaAsyncSocket: a18c75dca4b08723628a0bacca6e94803d90be91
|
||||
CocoaLumberjack: 17fe8581f84914d5d7e6360f7c70022b173c3ae0
|
||||
CocoaLumberjack: 628fca2e88ef06f7cf6817309aa405f325d9a6fa
|
||||
FBSnapshotTestCase: 7e85180d0d141a0cf472352edda7e80d7eaeb547
|
||||
KissXML: d19dd6dc65e0dc721ba92b3077b8ebdd240f1c1e
|
||||
PNXMPPFramework: 85a177de196fd742392f6ed0053c9cd2dd160f06
|
||||
PNXMPPFramework: 69989a4b1f7470763acd3e9a3d982ddfa5c161df
|
||||
|
||||
PODFILE CHECKSUM: c24dacdc80a49fe0e0fea049a6d762eb76667498
|
||||
|
||||
|
||||
@ -1,81 +0,0 @@
|
||||
// Software License Agreement (BSD License)
|
||||
//
|
||||
// Copyright (c) 2010-2015, Deusty, LLC
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use of this software in source and binary forms,
|
||||
// with or without modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Neither the name of Deusty nor the names of its contributors may be used
|
||||
// to endorse or promote products derived from this software without specific
|
||||
// prior written permission of Deusty, LLC.
|
||||
|
||||
/**
|
||||
* Welcome to CocoaLumberjack!
|
||||
*
|
||||
* The project page has a wealth of documentation if you have any questions.
|
||||
* https://github.com/CocoaLumberjack/CocoaLumberjack
|
||||
*
|
||||
* If you're new to the project you may wish to read "Getting Started" at:
|
||||
* Documentation/GettingStarted.md
|
||||
*
|
||||
* Otherwise, here is a quick refresher.
|
||||
* There are three steps to using the macros:
|
||||
*
|
||||
* Step 1:
|
||||
* Import the header in your implementation or prefix file:
|
||||
*
|
||||
* #import <CocoaLumberjack/CocoaLumberjack.h>
|
||||
*
|
||||
* Step 2:
|
||||
* Define your logging level in your implementation file:
|
||||
*
|
||||
* // Log levels: off, error, warn, info, verbose
|
||||
* static const DDLogLevel ddLogLevel = DDLogLevelVerbose;
|
||||
*
|
||||
* Step 2 [3rd party frameworks]:
|
||||
*
|
||||
* Define your LOG_LEVEL_DEF to a different variable/function than ddLogLevel:
|
||||
*
|
||||
* // #undef LOG_LEVEL_DEF // Undefine first only if needed
|
||||
* #define LOG_LEVEL_DEF myLibLogLevel
|
||||
*
|
||||
* Define your logging level in your implementation file:
|
||||
*
|
||||
* // Log levels: off, error, warn, info, verbose
|
||||
* static const DDLogLevel myLibLogLevel = DDLogLevelVerbose;
|
||||
*
|
||||
* Step 3:
|
||||
* Replace your NSLog statements with DDLog statements according to the severity of the message.
|
||||
*
|
||||
* NSLog(@"Fatal error, no dohickey found!"); -> DDLogError(@"Fatal error, no dohickey found!");
|
||||
*
|
||||
* DDLog works exactly the same as NSLog.
|
||||
* This means you can pass it multiple variables just like NSLog.
|
||||
**/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
// Disable legacy macros
|
||||
#ifndef DD_LEGACY_MACROS
|
||||
#define DD_LEGACY_MACROS 0
|
||||
#endif
|
||||
|
||||
// Core
|
||||
#import "DDLog.h"
|
||||
|
||||
// Main macros
|
||||
#import "DDLogMacros.h"
|
||||
#import "DDAssertMacros.h"
|
||||
|
||||
// Capture ASL
|
||||
#import "DDASLLogCapture.h"
|
||||
|
||||
// Loggers
|
||||
#import "DDTTYLogger.h"
|
||||
#import "DDASLLogger.h"
|
||||
#import "DDFileLogger.h"
|
||||
|
||||
@ -1,91 +0,0 @@
|
||||
// Software License Agreement (BSD License)
|
||||
//
|
||||
// Copyright (c) 2014-2015, Deusty, LLC
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use of this software in source and binary forms,
|
||||
// with or without modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Neither the name of Deusty nor the names of its contributors may be used
|
||||
// to endorse or promote products derived from this software without specific
|
||||
// prior written permission of Deusty, LLC.
|
||||
|
||||
import Foundation
|
||||
|
||||
extension DDLogFlag {
|
||||
public static func fromLogLevel(logLevel: DDLogLevel) -> DDLogFlag {
|
||||
return DDLogFlag(rawValue: logLevel.rawValue)
|
||||
}
|
||||
|
||||
public init(_ logLevel: DDLogLevel) {
|
||||
self = DDLogFlag(rawValue: logLevel.rawValue)
|
||||
}
|
||||
|
||||
///returns the log level, or the lowest equivalant.
|
||||
public func toLogLevel() -> DDLogLevel {
|
||||
if let ourValid = DDLogLevel(rawValue: self.rawValue) {
|
||||
return ourValid
|
||||
} else {
|
||||
let logFlag:DDLogFlag = self
|
||||
|
||||
if logFlag.contains(.Verbose) {
|
||||
return .Verbose
|
||||
} else if logFlag.contains(.Debug) {
|
||||
return .Debug
|
||||
} else if logFlag.contains(.Info) {
|
||||
return .Info
|
||||
} else if logFlag.contains(.Warning) {
|
||||
return .Warning
|
||||
} else if logFlag.contains(.Error) {
|
||||
return .Error
|
||||
} else {
|
||||
return .Off
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public var defaultDebugLevel = DDLogLevel.Verbose
|
||||
|
||||
public func resetDefaultDebugLevel() {
|
||||
defaultDebugLevel = DDLogLevel.Verbose
|
||||
}
|
||||
|
||||
public func SwiftLogMacro(isAsynchronous: Bool, level: DDLogLevel, flag flg: DDLogFlag, context: Int = 0, file: StaticString = __FILE__, function: StaticString = __FUNCTION__, line: UInt = __LINE__, tag: AnyObject? = nil, @autoclosure string: () -> String) {
|
||||
if level.rawValue & flg.rawValue != 0 {
|
||||
// Tell the DDLogMessage constructor to copy the C strings that get passed to it.
|
||||
// Using string interpolation to prevent integer overflow warning when using StaticString.stringValue
|
||||
let logMessage = DDLogMessage(message: string(), level: level, flag: flg, context: context, file: "\(file)", function: "\(function)", line: line, tag: tag, options: [.CopyFile, .CopyFunction], timestamp: nil)
|
||||
DDLog.log(isAsynchronous, message: logMessage)
|
||||
}
|
||||
}
|
||||
|
||||
public func DDLogDebug(@autoclosure logText: () -> String, level: DDLogLevel = defaultDebugLevel, context: Int = 0, file: StaticString = __FILE__, function: StaticString = __FUNCTION__, line: UInt = __LINE__, tag: AnyObject? = nil, asynchronous async: Bool = true) {
|
||||
SwiftLogMacro(async, level: level, flag: .Debug, context: context, file: file, function: function, line: line, tag: tag, string: logText)
|
||||
}
|
||||
|
||||
public func DDLogInfo(@autoclosure logText: () -> String, level: DDLogLevel = defaultDebugLevel, context: Int = 0, file: StaticString = __FILE__, function: StaticString = __FUNCTION__, line: UInt = __LINE__, tag: AnyObject? = nil, asynchronous async: Bool = true) {
|
||||
SwiftLogMacro(async, level: level, flag: .Info, context: context, file: file, function: function, line: line, tag: tag, string: logText)
|
||||
}
|
||||
|
||||
public func DDLogWarn(@autoclosure logText: () -> String, level: DDLogLevel = defaultDebugLevel, context: Int = 0, file: StaticString = __FILE__, function: StaticString = __FUNCTION__, line: UInt = __LINE__, tag: AnyObject? = nil, asynchronous async: Bool = true) {
|
||||
SwiftLogMacro(async, level: level, flag: .Warning, context: context, file: file, function: function, line: line, tag: tag, string: logText)
|
||||
}
|
||||
|
||||
public func DDLogVerbose(@autoclosure logText: () -> String, level: DDLogLevel = defaultDebugLevel, context: Int = 0, file: StaticString = __FILE__, function: StaticString = __FUNCTION__, line: UInt = __LINE__, tag: AnyObject? = nil, asynchronous async: Bool = true) {
|
||||
SwiftLogMacro(async, level: level, flag: .Verbose, context: context, file: file, function: function, line: line, tag: tag, string: logText)
|
||||
}
|
||||
|
||||
public func DDLogError(@autoclosure logText: () -> String, level: DDLogLevel = defaultDebugLevel, context: Int = 0, file: StaticString = __FILE__, function: StaticString = __FUNCTION__, line: UInt = __LINE__, tag: AnyObject? = nil, asynchronous async: Bool = false) {
|
||||
SwiftLogMacro(async, level: level, flag: .Error, context: context, file: file, function: function, line: line, tag: tag, string: logText)
|
||||
}
|
||||
|
||||
/// Analogous to the C preprocessor macro `THIS_FILE`.
|
||||
public func CurrentFileName(fileName: StaticString = __FILE__) -> String {
|
||||
// Using string interpolation to prevent integer overflow warning when using StaticString.stringValue
|
||||
// This double-casting to NSString is necessary as changes to how Swift handles NSPathUtilities requres the string to be an NSString
|
||||
return (("\(fileName)" as NSString).lastPathComponent as NSString).stringByDeletingPathExtension
|
||||
}
|
||||
@ -1,48 +0,0 @@
|
||||
// Software License Agreement (BSD License)
|
||||
//
|
||||
// Copyright (c) 2010-2015, Deusty, LLC
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use of this software in source and binary forms,
|
||||
// with or without modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Neither the name of Deusty nor the names of its contributors may be used
|
||||
// to endorse or promote products derived from this software without specific
|
||||
// prior written permission of Deusty, LLC.
|
||||
|
||||
#import "DDASLLogger.h"
|
||||
|
||||
@protocol DDLogger;
|
||||
|
||||
/**
|
||||
* This class provides the ability to capture the ASL (Apple System Logs)
|
||||
*/
|
||||
@interface DDASLLogCapture : NSObject
|
||||
|
||||
/**
|
||||
* Start capturing logs
|
||||
*/
|
||||
+ (void)start;
|
||||
|
||||
/**
|
||||
* Stop capturing logs
|
||||
*/
|
||||
+ (void)stop;
|
||||
|
||||
/**
|
||||
* Returns the current capture level.
|
||||
* @note Default log level: DDLogLevelVerbose (i.e. capture all ASL messages).
|
||||
*/
|
||||
+ (DDLogLevel)captureLevel;
|
||||
|
||||
/**
|
||||
* Set the capture level
|
||||
*
|
||||
* @param level new level
|
||||
*/
|
||||
+ (void)setCaptureLevel:(DDLogLevel)level;
|
||||
|
||||
@end
|
||||
230
Example/Pods/CocoaLumberjack/Classes/DDASLLogCapture.m
generated
230
Example/Pods/CocoaLumberjack/Classes/DDASLLogCapture.m
generated
@ -1,230 +0,0 @@
|
||||
// Software License Agreement (BSD License)
|
||||
//
|
||||
// Copyright (c) 2010-2015, Deusty, LLC
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use of this software in source and binary forms,
|
||||
// with or without modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Neither the name of Deusty nor the names of its contributors may be used
|
||||
// to endorse or promote products derived from this software without specific
|
||||
// prior written permission of Deusty, LLC.
|
||||
|
||||
#import "DDASLLogCapture.h"
|
||||
|
||||
// Disable legacy macros
|
||||
#ifndef DD_LEGACY_MACROS
|
||||
#define DD_LEGACY_MACROS 0
|
||||
#endif
|
||||
|
||||
#import "DDLog.h"
|
||||
|
||||
#include <asl.h>
|
||||
#include <notify.h>
|
||||
#include <notify_keys.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
static BOOL _cancel = YES;
|
||||
static DDLogLevel _captureLevel = DDLogLevelVerbose;
|
||||
|
||||
#ifdef __IPHONE_8_0
|
||||
#define DDASL_IOS_PIVOT_VERSION __IPHONE_8_0
|
||||
#endif
|
||||
#ifdef __MAC_10_10
|
||||
#define DDASL_OSX_PIVOT_VERSION __MAC_10_10
|
||||
#endif
|
||||
|
||||
@implementation DDASLLogCapture
|
||||
|
||||
static aslmsg (*dd_asl_next)(aslresponse obj);
|
||||
static void (*dd_asl_release)(aslresponse obj);
|
||||
|
||||
+ (void)initialize
|
||||
{
|
||||
#if (defined(DDASL_IOS_PIVOT_VERSION) && __IPHONE_OS_VERSION_MAX_ALLOWED >= DDASL_IOS_PIVOT_VERSION) || (defined(DDASL_OSX_PIVOT_VERSION) && __MAC_OS_X_VERSION_MAX_ALLOWED >= DDASL_OSX_PIVOT_VERSION)
|
||||
#if __IPHONE_OS_VERSION_MIN_REQUIRED < DDASL_IOS_PIVOT_VERSION || __MAC_OS_X_VERSION_MIN_REQUIRED < DDASL_OSX_PIVOT_VERSION
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
// Building on falsely advertised SDK, targeting deprecated API
|
||||
dd_asl_next = &aslresponse_next;
|
||||
dd_asl_release = &aslresponse_free;
|
||||
#pragma GCC diagnostic pop
|
||||
#else
|
||||
// Building on lastest, correct SDK, targeting latest API
|
||||
dd_asl_next = &asl_next;
|
||||
dd_asl_release = &asl_release;
|
||||
#endif
|
||||
#else
|
||||
// Building on old SDKs, targeting deprecated API
|
||||
dd_asl_next = &aslresponse_next;
|
||||
dd_asl_release = &aslresponse_free;
|
||||
#endif
|
||||
}
|
||||
|
||||
+ (void)start {
|
||||
// Ignore subsequent calls
|
||||
if (!_cancel) {
|
||||
return;
|
||||
}
|
||||
|
||||
_cancel = NO;
|
||||
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) {
|
||||
[self captureAslLogs];
|
||||
});
|
||||
}
|
||||
|
||||
+ (void)stop {
|
||||
_cancel = YES;
|
||||
}
|
||||
|
||||
+ (DDLogLevel)captureLevel {
|
||||
return _captureLevel;
|
||||
}
|
||||
|
||||
+ (void)setCaptureLevel:(DDLogLevel)level {
|
||||
_captureLevel = level;
|
||||
}
|
||||
|
||||
#pragma mark - Private methods
|
||||
|
||||
+ (void)configureAslQuery:(aslmsg)query {
|
||||
const char param[] = "7"; // ASL_LEVEL_DEBUG, which is everything. We'll rely on regular DDlog log level to filter
|
||||
|
||||
asl_set_query(query, ASL_KEY_LEVEL, param, ASL_QUERY_OP_LESS_EQUAL | ASL_QUERY_OP_NUMERIC);
|
||||
|
||||
// Don't retrieve logs from our own DDASLLogger
|
||||
asl_set_query(query, kDDASLKeyDDLog, kDDASLDDLogValue, ASL_QUERY_OP_NOT_EQUAL);
|
||||
|
||||
#if !TARGET_OS_IPHONE || TARGET_SIMULATOR
|
||||
int processId = [[NSProcessInfo processInfo] processIdentifier];
|
||||
char pid[16];
|
||||
sprintf(pid, "%d", processId);
|
||||
asl_set_query(query, ASL_KEY_PID, pid, ASL_QUERY_OP_EQUAL | ASL_QUERY_OP_NUMERIC);
|
||||
#endif
|
||||
}
|
||||
|
||||
+ (void)aslMessageReceived:(aslmsg)msg {
|
||||
const char* messageCString = asl_get( msg, ASL_KEY_MSG );
|
||||
if ( messageCString == NULL )
|
||||
return;
|
||||
|
||||
int flag;
|
||||
BOOL async;
|
||||
|
||||
const char* levelCString = asl_get(msg, ASL_KEY_LEVEL);
|
||||
switch (levelCString? atoi(levelCString) : 0) {
|
||||
// By default all NSLog's with a ASL_LEVEL_WARNING level
|
||||
case ASL_LEVEL_EMERG :
|
||||
case ASL_LEVEL_ALERT :
|
||||
case ASL_LEVEL_CRIT : flag = DDLogFlagError; async = NO; break;
|
||||
case ASL_LEVEL_ERR : flag = DDLogFlagWarning; async = YES; break;
|
||||
case ASL_LEVEL_WARNING : flag = DDLogFlagInfo; async = YES; break;
|
||||
case ASL_LEVEL_NOTICE : flag = DDLogFlagDebug; async = YES; break;
|
||||
case ASL_LEVEL_INFO :
|
||||
case ASL_LEVEL_DEBUG :
|
||||
default : flag = DDLogFlagVerbose; async = YES; break;
|
||||
}
|
||||
|
||||
if (!(_captureLevel & flag)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// NSString * sender = [NSString stringWithCString:asl_get(msg, ASL_KEY_SENDER) encoding:NSUTF8StringEncoding];
|
||||
NSString *message = @(messageCString);
|
||||
|
||||
const char* secondsCString = asl_get( msg, ASL_KEY_TIME );
|
||||
const char* nanoCString = asl_get( msg, ASL_KEY_TIME_NSEC );
|
||||
NSTimeInterval seconds = secondsCString ? strtod(secondsCString, NULL) : [NSDate timeIntervalSinceReferenceDate] - NSTimeIntervalSince1970;
|
||||
double nanoSeconds = nanoCString? strtod(nanoCString, NULL) : 0;
|
||||
NSTimeInterval totalSeconds = seconds + (nanoSeconds / 1e9);
|
||||
|
||||
NSDate *timeStamp = [NSDate dateWithTimeIntervalSince1970:totalSeconds];
|
||||
|
||||
DDLogMessage *logMessage = [[DDLogMessage alloc]initWithMessage:message
|
||||
level:_captureLevel
|
||||
flag:flag
|
||||
context:0
|
||||
file:@"DDASLLogCapture"
|
||||
function:0
|
||||
line:0
|
||||
tag:nil
|
||||
options:0
|
||||
timestamp:timeStamp];
|
||||
|
||||
[DDLog log:async message:logMessage];
|
||||
}
|
||||
|
||||
+ (void)captureAslLogs {
|
||||
@autoreleasepool
|
||||
{
|
||||
/*
|
||||
We use ASL_KEY_MSG_ID to see each message once, but there's no
|
||||
obvious way to get the "next" ID. To bootstrap the process, we'll
|
||||
search by timestamp until we've seen a message.
|
||||
*/
|
||||
|
||||
struct timeval timeval = {
|
||||
.tv_sec = 0
|
||||
};
|
||||
gettimeofday(&timeval, NULL);
|
||||
unsigned long long startTime = timeval.tv_sec;
|
||||
__block unsigned long long lastSeenID = 0;
|
||||
|
||||
/*
|
||||
syslogd posts kNotifyASLDBUpdate (com.apple.system.logger.message)
|
||||
through the notify API when it saves messages to the ASL database.
|
||||
There is some coalescing - currently it is sent at most twice per
|
||||
second - but there is no documented guarantee about this. In any
|
||||
case, there may be multiple messages per notification.
|
||||
|
||||
Notify notifications don't carry any payload, so we need to search
|
||||
for the messages.
|
||||
*/
|
||||
int notifyToken = 0; // Can be used to unregister with notify_cancel().
|
||||
notify_register_dispatch(kNotifyASLDBUpdate, ¬ifyToken, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^(int token)
|
||||
{
|
||||
// At least one message has been posted; build a search query.
|
||||
@autoreleasepool
|
||||
{
|
||||
aslmsg query = asl_new(ASL_TYPE_QUERY);
|
||||
char stringValue[64];
|
||||
|
||||
if (lastSeenID > 0) {
|
||||
snprintf(stringValue, sizeof stringValue, "%llu", lastSeenID);
|
||||
asl_set_query(query, ASL_KEY_MSG_ID, stringValue, ASL_QUERY_OP_GREATER | ASL_QUERY_OP_NUMERIC);
|
||||
} else {
|
||||
snprintf(stringValue, sizeof stringValue, "%llu", startTime);
|
||||
asl_set_query(query, ASL_KEY_TIME, stringValue, ASL_QUERY_OP_GREATER_EQUAL | ASL_QUERY_OP_NUMERIC);
|
||||
}
|
||||
|
||||
[self configureAslQuery:query];
|
||||
|
||||
// Iterate over new messages.
|
||||
aslmsg msg;
|
||||
aslresponse response = asl_search(NULL, query);
|
||||
|
||||
while ((msg = dd_asl_next(response)))
|
||||
{
|
||||
[self aslMessageReceived:msg];
|
||||
|
||||
// Keep track of which messages we've seen.
|
||||
lastSeenID = atoll(asl_get(msg, ASL_KEY_MSG_ID));
|
||||
}
|
||||
dd_asl_release(response);
|
||||
asl_free(query);
|
||||
|
||||
if (_cancel) {
|
||||
notify_cancel(token);
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
58
Example/Pods/CocoaLumberjack/Classes/DDASLLogger.h
generated
58
Example/Pods/CocoaLumberjack/Classes/DDASLLogger.h
generated
@ -1,58 +0,0 @@
|
||||
// Software License Agreement (BSD License)
|
||||
//
|
||||
// Copyright (c) 2010-2015, Deusty, LLC
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use of this software in source and binary forms,
|
||||
// with or without modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Neither the name of Deusty nor the names of its contributors may be used
|
||||
// to endorse or promote products derived from this software without specific
|
||||
// prior written permission of Deusty, LLC.
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
// Disable legacy macros
|
||||
#ifndef DD_LEGACY_MACROS
|
||||
#define DD_LEGACY_MACROS 0
|
||||
#endif
|
||||
|
||||
#import "DDLog.h"
|
||||
|
||||
// Custom key set on messages sent to ASL
|
||||
extern const char* const kDDASLKeyDDLog;
|
||||
|
||||
// Value set for kDDASLKeyDDLog
|
||||
extern const char* const kDDASLDDLogValue;
|
||||
|
||||
/**
|
||||
* This class provides a logger for the Apple System Log facility.
|
||||
*
|
||||
* As described in the "Getting Started" page,
|
||||
* the traditional NSLog() function directs its output to two places:
|
||||
*
|
||||
* - Apple System Log
|
||||
* - StdErr (if stderr is a TTY) so log statements show up in Xcode console
|
||||
*
|
||||
* To duplicate NSLog() functionality you can simply add this logger and a tty logger.
|
||||
* However, if you instead choose to use file logging (for faster performance),
|
||||
* you may choose to use a file logger and a tty logger.
|
||||
**/
|
||||
@interface DDASLLogger : DDAbstractLogger <DDLogger>
|
||||
|
||||
/**
|
||||
* Singleton method
|
||||
*
|
||||
* @return the shared instance
|
||||
*/
|
||||
+ (instancetype)sharedInstance;
|
||||
|
||||
// Inherited from DDAbstractLogger
|
||||
|
||||
// - (id <DDLogFormatter>)logFormatter;
|
||||
// - (void)setLogFormatter:(id <DDLogFormatter>)formatter;
|
||||
|
||||
@end
|
||||
121
Example/Pods/CocoaLumberjack/Classes/DDASLLogger.m
generated
121
Example/Pods/CocoaLumberjack/Classes/DDASLLogger.m
generated
@ -1,121 +0,0 @@
|
||||
// Software License Agreement (BSD License)
|
||||
//
|
||||
// Copyright (c) 2010-2015, Deusty, LLC
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use of this software in source and binary forms,
|
||||
// with or without modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Neither the name of Deusty nor the names of its contributors may be used
|
||||
// to endorse or promote products derived from this software without specific
|
||||
// prior written permission of Deusty, LLC.
|
||||
|
||||
#import "DDASLLogger.h"
|
||||
#import <asl.h>
|
||||
|
||||
#if !__has_feature(objc_arc)
|
||||
#error This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC).
|
||||
#endif
|
||||
|
||||
const char* const kDDASLKeyDDLog = "DDLog";
|
||||
|
||||
const char* const kDDASLDDLogValue = "1";
|
||||
|
||||
static DDASLLogger *sharedInstance;
|
||||
|
||||
@interface DDASLLogger () {
|
||||
aslclient _client;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation DDASLLogger
|
||||
|
||||
+ (instancetype)sharedInstance {
|
||||
static dispatch_once_t DDASLLoggerOnceToken;
|
||||
|
||||
dispatch_once(&DDASLLoggerOnceToken, ^{
|
||||
sharedInstance = [[[self class] alloc] init];
|
||||
});
|
||||
|
||||
return sharedInstance;
|
||||
}
|
||||
|
||||
- (instancetype)init {
|
||||
if (sharedInstance != nil) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
if ((self = [super init])) {
|
||||
// A default asl client is provided for the main thread,
|
||||
// but background threads need to create their own client.
|
||||
|
||||
_client = asl_open(NULL, "com.apple.console", 0);
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)logMessage:(DDLogMessage *)logMessage {
|
||||
// Skip captured log messages
|
||||
if ([logMessage->_fileName isEqualToString:@"DDASLLogCapture"]) {
|
||||
return;
|
||||
}
|
||||
|
||||
NSString * message = _logFormatter ? [_logFormatter formatLogMessage:logMessage] : logMessage->_message;
|
||||
|
||||
if (logMessage) {
|
||||
const char *msg = [message UTF8String];
|
||||
|
||||
size_t aslLogLevel;
|
||||
switch (logMessage->_flag) {
|
||||
// Note: By default ASL will filter anything above level 5 (Notice).
|
||||
// So our mappings shouldn't go above that level.
|
||||
case DDLogFlagError : aslLogLevel = ASL_LEVEL_CRIT; break;
|
||||
case DDLogFlagWarning : aslLogLevel = ASL_LEVEL_ERR; break;
|
||||
case DDLogFlagInfo : aslLogLevel = ASL_LEVEL_WARNING; break; // Regular NSLog's level
|
||||
case DDLogFlagDebug :
|
||||
case DDLogFlagVerbose :
|
||||
default : aslLogLevel = ASL_LEVEL_NOTICE; break;
|
||||
}
|
||||
|
||||
static char const *const level_strings[] = { "0", "1", "2", "3", "4", "5", "6", "7" };
|
||||
|
||||
// NSLog uses the current euid to set the ASL_KEY_READ_UID.
|
||||
uid_t const readUID = geteuid();
|
||||
|
||||
char readUIDString[16];
|
||||
#ifndef NS_BLOCK_ASSERTIONS
|
||||
int l = snprintf(readUIDString, sizeof(readUIDString), "%d", readUID);
|
||||
#else
|
||||
snprintf(readUIDString, sizeof(readUIDString), "%d", readUID);
|
||||
#endif
|
||||
|
||||
NSAssert(l < sizeof(readUIDString),
|
||||
@"Formatted euid is too long.");
|
||||
NSAssert(aslLogLevel < (sizeof(level_strings) / sizeof(level_strings[0])),
|
||||
@"Unhandled ASL log level.");
|
||||
|
||||
aslmsg m = asl_new(ASL_TYPE_MSG);
|
||||
if (m != NULL) {
|
||||
if (asl_set(m, ASL_KEY_LEVEL, level_strings[aslLogLevel]) == 0 &&
|
||||
asl_set(m, ASL_KEY_MSG, msg) == 0 &&
|
||||
asl_set(m, ASL_KEY_READ_UID, readUIDString) == 0 &&
|
||||
asl_set(m, kDDASLKeyDDLog, kDDASLDDLogValue) == 0) {
|
||||
asl_send(_client, m);
|
||||
}
|
||||
asl_free(m);
|
||||
}
|
||||
//TODO handle asl_* failures non-silently?
|
||||
}
|
||||
}
|
||||
|
||||
- (NSString *)loggerName {
|
||||
return @"cocoa.lumberjack.aslLogger";
|
||||
}
|
||||
|
||||
@end
|
||||
@ -1,660 +0,0 @@
|
||||
// Software License Agreement (BSD License)
|
||||
//
|
||||
// Copyright (c) 2010-2015, Deusty, LLC
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use of this software in source and binary forms,
|
||||
// with or without modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Neither the name of Deusty nor the names of its contributors may be used
|
||||
// to endorse or promote products derived from this software without specific
|
||||
// prior written permission of Deusty, LLC.
|
||||
|
||||
#import "DDAbstractDatabaseLogger.h"
|
||||
#import <math.h>
|
||||
|
||||
|
||||
#if !__has_feature(objc_arc)
|
||||
#error This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC).
|
||||
#endif
|
||||
|
||||
@interface DDAbstractDatabaseLogger ()
|
||||
|
||||
- (void)destroySaveTimer;
|
||||
- (void)destroyDeleteTimer;
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@implementation DDAbstractDatabaseLogger
|
||||
|
||||
- (instancetype)init {
|
||||
if ((self = [super init])) {
|
||||
_saveThreshold = 500;
|
||||
_saveInterval = 60; // 60 seconds
|
||||
_maxAge = (60 * 60 * 24 * 7); // 7 days
|
||||
_deleteInterval = (60 * 5); // 5 minutes
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
[self destroySaveTimer];
|
||||
[self destroyDeleteTimer];
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark Override Me
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
- (BOOL)db_log:(DDLogMessage *)logMessage {
|
||||
// Override me and add your implementation.
|
||||
//
|
||||
// Return YES if an item was added to the buffer.
|
||||
// Return NO if the logMessage was ignored.
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (void)db_save {
|
||||
// Override me and add your implementation.
|
||||
}
|
||||
|
||||
- (void)db_delete {
|
||||
// Override me and add your implementation.
|
||||
}
|
||||
|
||||
- (void)db_saveAndDelete {
|
||||
// Override me and add your implementation.
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark Private API
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
- (void)performSaveAndSuspendSaveTimer {
|
||||
if (_unsavedCount > 0) {
|
||||
if (_deleteOnEverySave) {
|
||||
[self db_saveAndDelete];
|
||||
} else {
|
||||
[self db_save];
|
||||
}
|
||||
}
|
||||
|
||||
_unsavedCount = 0;
|
||||
_unsavedTime = 0;
|
||||
|
||||
if (_saveTimer && !_saveTimerSuspended) {
|
||||
dispatch_suspend(_saveTimer);
|
||||
_saveTimerSuspended = YES;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)performDelete {
|
||||
if (_maxAge > 0.0) {
|
||||
[self db_delete];
|
||||
|
||||
_lastDeleteTime = dispatch_time(DISPATCH_TIME_NOW, 0);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark Timers
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
- (void)destroySaveTimer {
|
||||
if (_saveTimer) {
|
||||
dispatch_source_cancel(_saveTimer);
|
||||
|
||||
if (_saveTimerSuspended) {
|
||||
// Must resume a timer before releasing it (or it will crash)
|
||||
dispatch_resume(_saveTimer);
|
||||
_saveTimerSuspended = NO;
|
||||
}
|
||||
|
||||
#if !OS_OBJECT_USE_OBJC
|
||||
dispatch_release(_saveTimer);
|
||||
#endif
|
||||
_saveTimer = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)updateAndResumeSaveTimer {
|
||||
if ((_saveTimer != NULL) && (_saveInterval > 0.0) && (_unsavedTime > 0.0)) {
|
||||
uint64_t interval = (uint64_t)(_saveInterval * NSEC_PER_SEC);
|
||||
dispatch_time_t startTime = dispatch_time(_unsavedTime, interval);
|
||||
|
||||
dispatch_source_set_timer(_saveTimer, startTime, interval, 1.0);
|
||||
|
||||
if (_saveTimerSuspended) {
|
||||
dispatch_resume(_saveTimer);
|
||||
_saveTimerSuspended = NO;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)createSuspendedSaveTimer {
|
||||
if ((_saveTimer == NULL) && (_saveInterval > 0.0)) {
|
||||
_saveTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, self.loggerQueue);
|
||||
|
||||
dispatch_source_set_event_handler(_saveTimer, ^{ @autoreleasepool {
|
||||
[self performSaveAndSuspendSaveTimer];
|
||||
} });
|
||||
|
||||
_saveTimerSuspended = YES;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)destroyDeleteTimer {
|
||||
if (_deleteTimer) {
|
||||
dispatch_source_cancel(_deleteTimer);
|
||||
#if !OS_OBJECT_USE_OBJC
|
||||
dispatch_release(_deleteTimer);
|
||||
#endif
|
||||
_deleteTimer = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)updateDeleteTimer {
|
||||
if ((_deleteTimer != NULL) && (_deleteInterval > 0.0) && (_maxAge > 0.0)) {
|
||||
uint64_t interval = (uint64_t)(_deleteInterval * NSEC_PER_SEC);
|
||||
dispatch_time_t startTime;
|
||||
|
||||
if (_lastDeleteTime > 0) {
|
||||
startTime = dispatch_time(_lastDeleteTime, interval);
|
||||
} else {
|
||||
startTime = dispatch_time(DISPATCH_TIME_NOW, interval);
|
||||
}
|
||||
|
||||
dispatch_source_set_timer(_deleteTimer, startTime, interval, 1.0);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)createAndStartDeleteTimer {
|
||||
if ((_deleteTimer == NULL) && (_deleteInterval > 0.0) && (_maxAge > 0.0)) {
|
||||
_deleteTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, self.loggerQueue);
|
||||
|
||||
if (_deleteTimer != NULL) {
|
||||
dispatch_source_set_event_handler(_deleteTimer, ^{ @autoreleasepool {
|
||||
[self performDelete];
|
||||
} });
|
||||
|
||||
[self updateDeleteTimer];
|
||||
|
||||
if (_deleteTimer != NULL) {
|
||||
dispatch_resume(_deleteTimer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark Configuration
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
- (NSUInteger)saveThreshold {
|
||||
// The design of this method is taken from the DDAbstractLogger implementation.
|
||||
// For extensive documentation please refer to the DDAbstractLogger implementation.
|
||||
|
||||
// Note: The internal implementation MUST access the colorsEnabled variable directly,
|
||||
// This method is designed explicitly for external access.
|
||||
//
|
||||
// Using "self." syntax to go through this method will cause immediate deadlock.
|
||||
// This is the intended result. Fix it by accessing the ivar directly.
|
||||
// Great strides have been take to ensure this is safe to do. Plus it's MUCH faster.
|
||||
|
||||
NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
|
||||
NSAssert(![self isOnInternalLoggerQueue], @"MUST access ivar directly, NOT via self.* syntax.");
|
||||
|
||||
dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
|
||||
|
||||
__block NSUInteger result;
|
||||
|
||||
dispatch_sync(globalLoggingQueue, ^{
|
||||
dispatch_sync(self.loggerQueue, ^{
|
||||
result = _saveThreshold;
|
||||
});
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
- (void)setSaveThreshold:(NSUInteger)threshold {
|
||||
dispatch_block_t block = ^{
|
||||
@autoreleasepool {
|
||||
if (_saveThreshold != threshold) {
|
||||
_saveThreshold = threshold;
|
||||
|
||||
// Since the saveThreshold has changed,
|
||||
// we check to see if the current unsavedCount has surpassed the new threshold.
|
||||
//
|
||||
// If it has, we immediately save the log.
|
||||
|
||||
if ((_unsavedCount >= _saveThreshold) && (_saveThreshold > 0)) {
|
||||
[self performSaveAndSuspendSaveTimer];
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// The design of the setter logic below is taken from the DDAbstractLogger implementation.
|
||||
// For documentation please refer to the DDAbstractLogger implementation.
|
||||
|
||||
if ([self isOnInternalLoggerQueue]) {
|
||||
block();
|
||||
} else {
|
||||
dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
|
||||
NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
|
||||
|
||||
dispatch_async(globalLoggingQueue, ^{
|
||||
dispatch_async(self.loggerQueue, block);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
- (NSTimeInterval)saveInterval {
|
||||
// The design of this method is taken from the DDAbstractLogger implementation.
|
||||
// For extensive documentation please refer to the DDAbstractLogger implementation.
|
||||
|
||||
// Note: The internal implementation MUST access the colorsEnabled variable directly,
|
||||
// This method is designed explicitly for external access.
|
||||
//
|
||||
// Using "self." syntax to go through this method will cause immediate deadlock.
|
||||
// This is the intended result. Fix it by accessing the ivar directly.
|
||||
// Great strides have been take to ensure this is safe to do. Plus it's MUCH faster.
|
||||
|
||||
NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
|
||||
NSAssert(![self isOnInternalLoggerQueue], @"MUST access ivar directly, NOT via self.* syntax.");
|
||||
|
||||
dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
|
||||
|
||||
__block NSTimeInterval result;
|
||||
|
||||
dispatch_sync(globalLoggingQueue, ^{
|
||||
dispatch_sync(self.loggerQueue, ^{
|
||||
result = _saveInterval;
|
||||
});
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
- (void)setSaveInterval:(NSTimeInterval)interval {
|
||||
dispatch_block_t block = ^{
|
||||
@autoreleasepool {
|
||||
// C99 recommended floating point comparison macro
|
||||
// Read: isLessThanOrGreaterThan(floatA, floatB)
|
||||
|
||||
if (/* saveInterval != interval */ islessgreater(_saveInterval, interval)) {
|
||||
_saveInterval = interval;
|
||||
|
||||
// There are several cases we need to handle here.
|
||||
//
|
||||
// 1. If the saveInterval was previously enabled and it just got disabled,
|
||||
// then we need to stop the saveTimer. (And we might as well release it.)
|
||||
//
|
||||
// 2. If the saveInterval was previously disabled and it just got enabled,
|
||||
// then we need to setup the saveTimer. (Plus we might need to do an immediate save.)
|
||||
//
|
||||
// 3. If the saveInterval increased, then we need to reset the timer so that it fires at the later date.
|
||||
//
|
||||
// 4. If the saveInterval decreased, then we need to reset the timer so that it fires at an earlier date.
|
||||
// (Plus we might need to do an immediate save.)
|
||||
|
||||
if (_saveInterval > 0.0) {
|
||||
if (_saveTimer == NULL) {
|
||||
// Handles #2
|
||||
//
|
||||
// Since the saveTimer uses the unsavedTime to calculate it's first fireDate,
|
||||
// if a save is needed the timer will fire immediately.
|
||||
|
||||
[self createSuspendedSaveTimer];
|
||||
[self updateAndResumeSaveTimer];
|
||||
} else {
|
||||
// Handles #3
|
||||
// Handles #4
|
||||
//
|
||||
// Since the saveTimer uses the unsavedTime to calculate it's first fireDate,
|
||||
// if a save is needed the timer will fire immediately.
|
||||
|
||||
[self updateAndResumeSaveTimer];
|
||||
}
|
||||
} else if (_saveTimer) {
|
||||
// Handles #1
|
||||
|
||||
[self destroySaveTimer];
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// The design of the setter logic below is taken from the DDAbstractLogger implementation.
|
||||
// For documentation please refer to the DDAbstractLogger implementation.
|
||||
|
||||
if ([self isOnInternalLoggerQueue]) {
|
||||
block();
|
||||
} else {
|
||||
dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
|
||||
NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
|
||||
|
||||
dispatch_async(globalLoggingQueue, ^{
|
||||
dispatch_async(self.loggerQueue, block);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
- (NSTimeInterval)maxAge {
|
||||
// The design of this method is taken from the DDAbstractLogger implementation.
|
||||
// For extensive documentation please refer to the DDAbstractLogger implementation.
|
||||
|
||||
// Note: The internal implementation MUST access the colorsEnabled variable directly,
|
||||
// This method is designed explicitly for external access.
|
||||
//
|
||||
// Using "self." syntax to go through this method will cause immediate deadlock.
|
||||
// This is the intended result. Fix it by accessing the ivar directly.
|
||||
// Great strides have been take to ensure this is safe to do. Plus it's MUCH faster.
|
||||
|
||||
NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
|
||||
NSAssert(![self isOnInternalLoggerQueue], @"MUST access ivar directly, NOT via self.* syntax.");
|
||||
|
||||
dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
|
||||
|
||||
__block NSTimeInterval result;
|
||||
|
||||
dispatch_sync(globalLoggingQueue, ^{
|
||||
dispatch_sync(self.loggerQueue, ^{
|
||||
result = _maxAge;
|
||||
});
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
- (void)setMaxAge:(NSTimeInterval)interval {
|
||||
dispatch_block_t block = ^{
|
||||
@autoreleasepool {
|
||||
// C99 recommended floating point comparison macro
|
||||
// Read: isLessThanOrGreaterThan(floatA, floatB)
|
||||
|
||||
if (/* maxAge != interval */ islessgreater(_maxAge, interval)) {
|
||||
NSTimeInterval oldMaxAge = _maxAge;
|
||||
NSTimeInterval newMaxAge = interval;
|
||||
|
||||
_maxAge = interval;
|
||||
|
||||
// There are several cases we need to handle here.
|
||||
//
|
||||
// 1. If the maxAge was previously enabled and it just got disabled,
|
||||
// then we need to stop the deleteTimer. (And we might as well release it.)
|
||||
//
|
||||
// 2. If the maxAge was previously disabled and it just got enabled,
|
||||
// then we need to setup the deleteTimer. (Plus we might need to do an immediate delete.)
|
||||
//
|
||||
// 3. If the maxAge was increased,
|
||||
// then we don't need to do anything.
|
||||
//
|
||||
// 4. If the maxAge was decreased,
|
||||
// then we should do an immediate delete.
|
||||
|
||||
BOOL shouldDeleteNow = NO;
|
||||
|
||||
if (oldMaxAge > 0.0) {
|
||||
if (newMaxAge <= 0.0) {
|
||||
// Handles #1
|
||||
|
||||
[self destroyDeleteTimer];
|
||||
} else if (oldMaxAge > newMaxAge) {
|
||||
// Handles #4
|
||||
shouldDeleteNow = YES;
|
||||
}
|
||||
} else if (newMaxAge > 0.0) {
|
||||
// Handles #2
|
||||
shouldDeleteNow = YES;
|
||||
}
|
||||
|
||||
if (shouldDeleteNow) {
|
||||
[self performDelete];
|
||||
|
||||
if (_deleteTimer) {
|
||||
[self updateDeleteTimer];
|
||||
} else {
|
||||
[self createAndStartDeleteTimer];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// The design of the setter logic below is taken from the DDAbstractLogger implementation.
|
||||
// For documentation please refer to the DDAbstractLogger implementation.
|
||||
|
||||
if ([self isOnInternalLoggerQueue]) {
|
||||
block();
|
||||
} else {
|
||||
dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
|
||||
NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
|
||||
|
||||
dispatch_async(globalLoggingQueue, ^{
|
||||
dispatch_async(self.loggerQueue, block);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
- (NSTimeInterval)deleteInterval {
|
||||
// The design of this method is taken from the DDAbstractLogger implementation.
|
||||
// For extensive documentation please refer to the DDAbstractLogger implementation.
|
||||
|
||||
// Note: The internal implementation MUST access the colorsEnabled variable directly,
|
||||
// This method is designed explicitly for external access.
|
||||
//
|
||||
// Using "self." syntax to go through this method will cause immediate deadlock.
|
||||
// This is the intended result. Fix it by accessing the ivar directly.
|
||||
// Great strides have been take to ensure this is safe to do. Plus it's MUCH faster.
|
||||
|
||||
NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
|
||||
NSAssert(![self isOnInternalLoggerQueue], @"MUST access ivar directly, NOT via self.* syntax.");
|
||||
|
||||
dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
|
||||
|
||||
__block NSTimeInterval result;
|
||||
|
||||
dispatch_sync(globalLoggingQueue, ^{
|
||||
dispatch_sync(self.loggerQueue, ^{
|
||||
result = _deleteInterval;
|
||||
});
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
- (void)setDeleteInterval:(NSTimeInterval)interval {
|
||||
dispatch_block_t block = ^{
|
||||
@autoreleasepool {
|
||||
// C99 recommended floating point comparison macro
|
||||
// Read: isLessThanOrGreaterThan(floatA, floatB)
|
||||
|
||||
if (/* deleteInterval != interval */ islessgreater(_deleteInterval, interval)) {
|
||||
_deleteInterval = interval;
|
||||
|
||||
// There are several cases we need to handle here.
|
||||
//
|
||||
// 1. If the deleteInterval was previously enabled and it just got disabled,
|
||||
// then we need to stop the deleteTimer. (And we might as well release it.)
|
||||
//
|
||||
// 2. If the deleteInterval was previously disabled and it just got enabled,
|
||||
// then we need to setup the deleteTimer. (Plus we might need to do an immediate delete.)
|
||||
//
|
||||
// 3. If the deleteInterval increased, then we need to reset the timer so that it fires at the later date.
|
||||
//
|
||||
// 4. If the deleteInterval decreased, then we need to reset the timer so that it fires at an earlier date.
|
||||
// (Plus we might need to do an immediate delete.)
|
||||
|
||||
if (_deleteInterval > 0.0) {
|
||||
if (_deleteTimer == NULL) {
|
||||
// Handles #2
|
||||
//
|
||||
// Since the deleteTimer uses the lastDeleteTime to calculate it's first fireDate,
|
||||
// if a delete is needed the timer will fire immediately.
|
||||
|
||||
[self createAndStartDeleteTimer];
|
||||
} else {
|
||||
// Handles #3
|
||||
// Handles #4
|
||||
//
|
||||
// Since the deleteTimer uses the lastDeleteTime to calculate it's first fireDate,
|
||||
// if a save is needed the timer will fire immediately.
|
||||
|
||||
[self updateDeleteTimer];
|
||||
}
|
||||
} else if (_deleteTimer) {
|
||||
// Handles #1
|
||||
|
||||
[self destroyDeleteTimer];
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// The design of the setter logic below is taken from the DDAbstractLogger implementation.
|
||||
// For documentation please refer to the DDAbstractLogger implementation.
|
||||
|
||||
if ([self isOnInternalLoggerQueue]) {
|
||||
block();
|
||||
} else {
|
||||
dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
|
||||
NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
|
||||
|
||||
dispatch_async(globalLoggingQueue, ^{
|
||||
dispatch_async(self.loggerQueue, block);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)deleteOnEverySave {
|
||||
// The design of this method is taken from the DDAbstractLogger implementation.
|
||||
// For extensive documentation please refer to the DDAbstractLogger implementation.
|
||||
|
||||
// Note: The internal implementation MUST access the colorsEnabled variable directly,
|
||||
// This method is designed explicitly for external access.
|
||||
//
|
||||
// Using "self." syntax to go through this method will cause immediate deadlock.
|
||||
// This is the intended result. Fix it by accessing the ivar directly.
|
||||
// Great strides have been take to ensure this is safe to do. Plus it's MUCH faster.
|
||||
|
||||
NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
|
||||
NSAssert(![self isOnInternalLoggerQueue], @"MUST access ivar directly, NOT via self.* syntax.");
|
||||
|
||||
dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
|
||||
|
||||
__block BOOL result;
|
||||
|
||||
dispatch_sync(globalLoggingQueue, ^{
|
||||
dispatch_sync(self.loggerQueue, ^{
|
||||
result = _deleteOnEverySave;
|
||||
});
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
- (void)setDeleteOnEverySave:(BOOL)flag {
|
||||
dispatch_block_t block = ^{
|
||||
_deleteOnEverySave = flag;
|
||||
};
|
||||
|
||||
// The design of the setter logic below is taken from the DDAbstractLogger implementation.
|
||||
// For documentation please refer to the DDAbstractLogger implementation.
|
||||
|
||||
if ([self isOnInternalLoggerQueue]) {
|
||||
block();
|
||||
} else {
|
||||
dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
|
||||
NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
|
||||
|
||||
dispatch_async(globalLoggingQueue, ^{
|
||||
dispatch_async(self.loggerQueue, block);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark Public API
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
- (void)savePendingLogEntries {
|
||||
dispatch_block_t block = ^{
|
||||
@autoreleasepool {
|
||||
[self performSaveAndSuspendSaveTimer];
|
||||
}
|
||||
};
|
||||
|
||||
if ([self isOnInternalLoggerQueue]) {
|
||||
block();
|
||||
} else {
|
||||
dispatch_async(self.loggerQueue, block);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)deleteOldLogEntries {
|
||||
dispatch_block_t block = ^{
|
||||
@autoreleasepool {
|
||||
[self performDelete];
|
||||
}
|
||||
};
|
||||
|
||||
if ([self isOnInternalLoggerQueue]) {
|
||||
block();
|
||||
} else {
|
||||
dispatch_async(self.loggerQueue, block);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark DDLogger
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
- (void)didAddLogger {
|
||||
// If you override me be sure to invoke [super didAddLogger];
|
||||
|
||||
[self createSuspendedSaveTimer];
|
||||
|
||||
[self createAndStartDeleteTimer];
|
||||
}
|
||||
|
||||
- (void)willRemoveLogger {
|
||||
// If you override me be sure to invoke [super willRemoveLogger];
|
||||
|
||||
[self performSaveAndSuspendSaveTimer];
|
||||
|
||||
[self destroySaveTimer];
|
||||
[self destroyDeleteTimer];
|
||||
}
|
||||
|
||||
- (void)logMessage:(DDLogMessage *)logMessage {
|
||||
if ([self db_log:logMessage]) {
|
||||
BOOL firstUnsavedEntry = (++_unsavedCount == 1);
|
||||
|
||||
if ((_unsavedCount >= _saveThreshold) && (_saveThreshold > 0)) {
|
||||
[self performSaveAndSuspendSaveTimer];
|
||||
} else if (firstUnsavedEntry) {
|
||||
_unsavedTime = dispatch_time(DISPATCH_TIME_NOW, 0);
|
||||
[self updateAndResumeSaveTimer];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)flush {
|
||||
// This method is invoked by DDLog's flushLog method.
|
||||
//
|
||||
// It is called automatically when the application quits,
|
||||
// or if the developer invokes DDLog's flushLog method prior to crashing or something.
|
||||
|
||||
[self performSaveAndSuspendSaveTimer];
|
||||
}
|
||||
|
||||
@end
|
||||
@ -1,26 +0,0 @@
|
||||
// Software License Agreement (BSD License)
|
||||
//
|
||||
// Copyright (c) 2010-2015, Deusty, LLC
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use of this software in source and binary forms,
|
||||
// with or without modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Neither the name of Deusty nor the names of its contributors may be used
|
||||
// to endorse or promote products derived from this software without specific
|
||||
// prior written permission of Deusty, LLC.
|
||||
|
||||
/**
|
||||
* NSAsset replacement that will output a log message even when assertions are disabled.
|
||||
**/
|
||||
#define DDAssert(condition, frmt, ...) \
|
||||
if (!(condition)) { \
|
||||
NSString *description = [NSString stringWithFormat:frmt, ## __VA_ARGS__]; \
|
||||
DDLogError(@"%@", description); \
|
||||
NSAssert(NO, description); \
|
||||
}
|
||||
#define DDAssertCondition(condition) DDAssert(condition, @"Condition not satisfied: %s", #condition)
|
||||
|
||||
@ -1,75 +0,0 @@
|
||||
// Software License Agreement (BSD License)
|
||||
//
|
||||
// Copyright (c) 2010-2015, Deusty, LLC
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use of this software in source and binary forms,
|
||||
// with or without modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Neither the name of Deusty nor the names of its contributors may be used
|
||||
// to endorse or promote products derived from this software without specific
|
||||
// prior written permission of Deusty, LLC.
|
||||
|
||||
/**
|
||||
* Legacy macros used for 1.9.x backwards compatibility.
|
||||
*
|
||||
* Imported by default when importing a DDLog.h directly and DD_LEGACY_MACROS is not defined and set to 0.
|
||||
**/
|
||||
#if DD_LEGACY_MACROS
|
||||
|
||||
#warning CocoaLumberjack 1.9.x legacy macros enabled. \
|
||||
Disable legacy macros by importing CocoaLumberjack.h or DDLogMacros.h instead of DDLog.h or add `#define DD_LEGACY_MACROS 0` before importing DDLog.h.
|
||||
|
||||
#ifndef LOG_LEVEL_DEF
|
||||
#define LOG_LEVEL_DEF ddLogLevel
|
||||
#endif
|
||||
|
||||
#define LOG_FLAG_ERROR DDLogFlagError
|
||||
#define LOG_FLAG_WARN DDLogFlagWarning
|
||||
#define LOG_FLAG_INFO DDLogFlagInfo
|
||||
#define LOG_FLAG_DEBUG DDLogFlagDebug
|
||||
#define LOG_FLAG_VERBOSE DDLogFlagVerbose
|
||||
|
||||
#define LOG_LEVEL_OFF DDLogLevelOff
|
||||
#define LOG_LEVEL_ERROR DDLogLevelError
|
||||
#define LOG_LEVEL_WARN DDLogLevelWarning
|
||||
#define LOG_LEVEL_INFO DDLogLevelInfo
|
||||
#define LOG_LEVEL_DEBUG DDLogLevelDebug
|
||||
#define LOG_LEVEL_VERBOSE DDLogLevelVerbose
|
||||
#define LOG_LEVEL_ALL DDLogLevelAll
|
||||
|
||||
#define LOG_ASYNC_ENABLED YES
|
||||
|
||||
#define LOG_ASYNC_ERROR ( NO && LOG_ASYNC_ENABLED)
|
||||
#define LOG_ASYNC_WARN (YES && LOG_ASYNC_ENABLED)
|
||||
#define LOG_ASYNC_INFO (YES && LOG_ASYNC_ENABLED)
|
||||
#define LOG_ASYNC_DEBUG (YES && LOG_ASYNC_ENABLED)
|
||||
#define LOG_ASYNC_VERBOSE (YES && LOG_ASYNC_ENABLED)
|
||||
|
||||
#define LOG_MACRO(isAsynchronous, lvl, flg, ctx, atag, fnct, frmt, ...) \
|
||||
[DDLog log : isAsynchronous \
|
||||
level : lvl \
|
||||
flag : flg \
|
||||
context : ctx \
|
||||
file : __FILE__ \
|
||||
function : fnct \
|
||||
line : __LINE__ \
|
||||
tag : atag \
|
||||
format : (frmt), ## __VA_ARGS__]
|
||||
|
||||
#define LOG_MAYBE(async, lvl, flg, ctx, fnct, frmt, ...) \
|
||||
do { if(lvl & flg) LOG_MACRO(async, lvl, flg, ctx, nil, fnct, frmt, ##__VA_ARGS__); } while(0)
|
||||
|
||||
#define LOG_OBJC_MAYBE(async, lvl, flg, ctx, frmt, ...) \
|
||||
LOG_MAYBE(async, lvl, flg, ctx, __PRETTY_FUNCTION__, frmt, ## __VA_ARGS__)
|
||||
|
||||
#define DDLogError(frmt, ...) LOG_OBJC_MAYBE(LOG_ASYNC_ERROR, LOG_LEVEL_DEF, LOG_FLAG_ERROR, 0, frmt, ##__VA_ARGS__)
|
||||
#define DDLogWarn(frmt, ...) LOG_OBJC_MAYBE(LOG_ASYNC_WARN, LOG_LEVEL_DEF, LOG_FLAG_WARN, 0, frmt, ##__VA_ARGS__)
|
||||
#define DDLogInfo(frmt, ...) LOG_OBJC_MAYBE(LOG_ASYNC_INFO, LOG_LEVEL_DEF, LOG_FLAG_INFO, 0, frmt, ##__VA_ARGS__)
|
||||
#define DDLogDebug(frmt, ...) LOG_OBJC_MAYBE(LOG_ASYNC_DEBUG, LOG_LEVEL_DEF, LOG_FLAG_DEBUG, 0, frmt, ##__VA_ARGS__)
|
||||
#define DDLogVerbose(frmt, ...) LOG_OBJC_MAYBE(LOG_ASYNC_VERBOSE, LOG_LEVEL_DEF, LOG_FLAG_VERBOSE, 0, frmt, ##__VA_ARGS__)
|
||||
|
||||
#endif
|
||||
83
Example/Pods/CocoaLumberjack/Classes/DDLog+LOGV.h
generated
83
Example/Pods/CocoaLumberjack/Classes/DDLog+LOGV.h
generated
@ -1,83 +0,0 @@
|
||||
// Software License Agreement (BSD License)
|
||||
//
|
||||
// Copyright (c) 2010-2015, Deusty, LLC
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use of this software in source and binary forms,
|
||||
// with or without modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Neither the name of Deusty nor the names of its contributors may be used
|
||||
// to endorse or promote products derived from this software without specific
|
||||
// prior written permission of Deusty, LLC.
|
||||
|
||||
// Disable legacy macros
|
||||
#ifndef DD_LEGACY_MACROS
|
||||
#define DD_LEGACY_MACROS 0
|
||||
#endif
|
||||
|
||||
#import "DDLog.h"
|
||||
|
||||
/**
|
||||
* The constant/variable/method responsible for controlling the current log level.
|
||||
**/
|
||||
#ifndef LOG_LEVEL_DEF
|
||||
#define LOG_LEVEL_DEF ddLogLevel
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Whether async should be used by log messages, excluding error messages that are always sent sync.
|
||||
**/
|
||||
#ifndef LOG_ASYNC_ENABLED
|
||||
#define LOG_ASYNC_ENABLED YES
|
||||
#endif
|
||||
|
||||
/**
|
||||
* This is the single macro that all other macros below compile into.
|
||||
* This big multiline macro makes all the other macros easier to read.
|
||||
**/
|
||||
#define LOGV_MACRO(isAsynchronous, lvl, flg, ctx, atag, fnct, frmt, avalist) \
|
||||
[DDLog log : isAsynchronous \
|
||||
level : lvl \
|
||||
flag : flg \
|
||||
context : ctx \
|
||||
file : __FILE__ \
|
||||
function : fnct \
|
||||
line : __LINE__ \
|
||||
tag : atag \
|
||||
format : frmt \
|
||||
args : avalist]
|
||||
|
||||
/**
|
||||
* Define version of the macro that only execute if the log level is above the threshold.
|
||||
* The compiled versions essentially look like this:
|
||||
*
|
||||
* if (logFlagForThisLogMsg & ddLogLevel) { execute log message }
|
||||
*
|
||||
* When LOG_LEVEL_DEF is defined as ddLogLevel.
|
||||
*
|
||||
* As shown further below, Lumberjack actually uses a bitmask as opposed to primitive log levels.
|
||||
* This allows for a great amount of flexibility and some pretty advanced fine grained logging techniques.
|
||||
*
|
||||
* Note that when compiler optimizations are enabled (as they are for your release builds),
|
||||
* the log messages above your logging threshold will automatically be compiled out.
|
||||
*
|
||||
* (If the compiler sees LOG_LEVEL_DEF/ddLogLevel declared as a constant, the compiler simply checks to see
|
||||
* if the 'if' statement would execute, and if not it strips it from the binary.)
|
||||
*
|
||||
* We also define shorthand versions for asynchronous and synchronous logging.
|
||||
**/
|
||||
#define LOGV_MAYBE(async, lvl, flg, ctx, tag, fnct, frmt, avalist) \
|
||||
do { if(lvl & flg) LOGV_MACRO(async, lvl, flg, ctx, tag, fnct, frmt, avalist); } while(0)
|
||||
|
||||
/**
|
||||
* Ready to use log macros with no context or tag.
|
||||
**/
|
||||
#define DDLogVError(frmt, avalist) LOGV_MAYBE(NO, LOG_LEVEL_DEF, DDLogFlagError, 0, nil, __PRETTY_FUNCTION__, frmt, avalist)
|
||||
#define DDLogVWarn(frmt, avalist) LOGV_MAYBE(LOG_ASYNC_ENABLED, LOG_LEVEL_DEF, DDLogFlagWarning, 0, nil, __PRETTY_FUNCTION__, frmt, avalist)
|
||||
#define DDLogVInfo(frmt, avalist) LOGV_MAYBE(LOG_ASYNC_ENABLED, LOG_LEVEL_DEF, DDLogFlagInfo, 0, nil, __PRETTY_FUNCTION__, frmt, avalist)
|
||||
#define DDLogVDebug(frmt, avalist) LOGV_MAYBE(LOG_ASYNC_ENABLED, LOG_LEVEL_DEF, DDLogFlagDebug, 0, nil, __PRETTY_FUNCTION__, frmt, avalist)
|
||||
#define DDLogVVerbose(frmt, avalist) LOGV_MAYBE(LOG_ASYNC_ENABLED, LOG_LEVEL_DEF, DDLogFlagVerbose, 0, nil, __PRETTY_FUNCTION__, frmt, avalist)
|
||||
|
||||
743
Example/Pods/CocoaLumberjack/Classes/DDLog.h
generated
743
Example/Pods/CocoaLumberjack/Classes/DDLog.h
generated
@ -1,743 +0,0 @@
|
||||
// Software License Agreement (BSD License)
|
||||
//
|
||||
// Copyright (c) 2010-2015, Deusty, LLC
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use of this software in source and binary forms,
|
||||
// with or without modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Neither the name of Deusty nor the names of its contributors may be used
|
||||
// to endorse or promote products derived from this software without specific
|
||||
// prior written permission of Deusty, LLC.
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
// Enable 1.9.x legacy macros if imported directly
|
||||
#ifndef DD_LEGACY_MACROS
|
||||
#define DD_LEGACY_MACROS 1
|
||||
#endif
|
||||
// DD_LEGACY_MACROS is checked in the file itself
|
||||
#import "DDLegacyMacros.h"
|
||||
|
||||
#if OS_OBJECT_USE_OBJC
|
||||
#define DISPATCH_QUEUE_REFERENCE_TYPE strong
|
||||
#else
|
||||
#define DISPATCH_QUEUE_REFERENCE_TYPE assign
|
||||
#endif
|
||||
|
||||
@class DDLogMessage;
|
||||
@protocol DDLogger;
|
||||
@protocol DDLogFormatter;
|
||||
|
||||
/**
|
||||
* Define the standard options.
|
||||
*
|
||||
* We default to only 4 levels because it makes it easier for beginners
|
||||
* to make the transition to a logging framework.
|
||||
*
|
||||
* More advanced users may choose to completely customize the levels (and level names) to suite their needs.
|
||||
* For more information on this see the "Custom Log Levels" page:
|
||||
* Documentation/CustomLogLevels.md
|
||||
*
|
||||
* Advanced users may also notice that we're using a bitmask.
|
||||
* This is to allow for custom fine grained logging:
|
||||
* Documentation/FineGrainedLogging.md
|
||||
*
|
||||
* -- Flags --
|
||||
*
|
||||
* Typically you will use the LOG_LEVELS (see below), but the flags may be used directly in certain situations.
|
||||
* For example, say you have a lot of warning log messages, and you wanted to disable them.
|
||||
* However, you still needed to see your error and info log messages.
|
||||
* You could accomplish that with the following:
|
||||
*
|
||||
* static const DDLogLevel ddLogLevel = DDLogFlagError | DDLogFlagInfo;
|
||||
*
|
||||
* When LOG_LEVEL_DEF is defined as ddLogLevel.
|
||||
*
|
||||
* Flags may also be consulted when writing custom log formatters,
|
||||
* as the DDLogMessage class captures the individual flag that caused the log message to fire.
|
||||
*
|
||||
* -- Levels --
|
||||
*
|
||||
* Log levels are simply the proper bitmask of the flags.
|
||||
*
|
||||
* -- Booleans --
|
||||
*
|
||||
* The booleans may be used when your logging code involves more than one line.
|
||||
* For example:
|
||||
*
|
||||
* if (LOG_VERBOSE) {
|
||||
* for (id sprocket in sprockets)
|
||||
* DDLogVerbose(@"sprocket: %@", [sprocket description])
|
||||
* }
|
||||
*
|
||||
* -- Async --
|
||||
*
|
||||
* Defines the default asynchronous options.
|
||||
* The default philosophy for asynchronous logging is very simple:
|
||||
*
|
||||
* Log messages with errors should be executed synchronously.
|
||||
* After all, an error just occurred. The application could be unstable.
|
||||
*
|
||||
* All other log messages, such as debug output, are executed asynchronously.
|
||||
* After all, if it wasn't an error, then it was just informational output,
|
||||
* or something the application was easily able to recover from.
|
||||
*
|
||||
* -- Changes --
|
||||
*
|
||||
* You are strongly discouraged from modifying this file.
|
||||
* If you do, you make it more difficult on yourself to merge future bug fixes and improvements from the project.
|
||||
* Instead, create your own MyLogging.h or ApplicationNameLogging.h or CompanyLogging.h
|
||||
*
|
||||
* For an example of customizing your logging experience, see the "Custom Log Levels" page:
|
||||
* Documentation/CustomLogLevels.md
|
||||
**/
|
||||
|
||||
/**
|
||||
* Flags accompany each log. They are used together with levels to filter out logs.
|
||||
*/
|
||||
typedef NS_OPTIONS(NSUInteger, DDLogFlag){
|
||||
/**
|
||||
* 0...00000 DDLogFlagError
|
||||
*/
|
||||
DDLogFlagError = (1 << 0),
|
||||
|
||||
/**
|
||||
* 0...00001 DDLogFlagWarning
|
||||
*/
|
||||
DDLogFlagWarning = (1 << 1),
|
||||
|
||||
/**
|
||||
* 0...00010 DDLogFlagInfo
|
||||
*/
|
||||
DDLogFlagInfo = (1 << 2),
|
||||
|
||||
/**
|
||||
* 0...00100 DDLogFlagDebug
|
||||
*/
|
||||
DDLogFlagDebug = (1 << 3),
|
||||
|
||||
/**
|
||||
* 0...01000 DDLogFlagVerbose
|
||||
*/
|
||||
DDLogFlagVerbose = (1 << 4)
|
||||
};
|
||||
|
||||
/**
|
||||
* Log levels are used to filter out logs. Used together with flags.
|
||||
*/
|
||||
typedef NS_ENUM(NSUInteger, DDLogLevel){
|
||||
/**
|
||||
* No logs
|
||||
*/
|
||||
DDLogLevelOff = 0,
|
||||
|
||||
/**
|
||||
* Error logs only
|
||||
*/
|
||||
DDLogLevelError = (DDLogFlagError),
|
||||
|
||||
/**
|
||||
* Error and warning logs
|
||||
*/
|
||||
DDLogLevelWarning = (DDLogLevelError | DDLogFlagWarning),
|
||||
|
||||
/**
|
||||
* Error, warning and info logs
|
||||
*/
|
||||
DDLogLevelInfo = (DDLogLevelWarning | DDLogFlagInfo),
|
||||
|
||||
/**
|
||||
* Error, warning, info and debug logs
|
||||
*/
|
||||
DDLogLevelDebug = (DDLogLevelInfo | DDLogFlagDebug),
|
||||
|
||||
/**
|
||||
* Error, warning, info, debug and verbose logs
|
||||
*/
|
||||
DDLogLevelVerbose = (DDLogLevelDebug | DDLogFlagVerbose),
|
||||
|
||||
/**
|
||||
* All logs (1...11111)
|
||||
*/
|
||||
DDLogLevelAll = NSUIntegerMax
|
||||
};
|
||||
|
||||
/**
|
||||
* Extracts just the file name, no path or extension
|
||||
*
|
||||
* @param filePath input file path
|
||||
* @param copy YES if we want the result to be copied
|
||||
*
|
||||
* @return the file name
|
||||
*/
|
||||
NSString * DDExtractFileNameWithoutExtension(const char *filePath, BOOL copy);
|
||||
|
||||
/**
|
||||
* The THIS_FILE macro gives you an NSString of the file name.
|
||||
* For simplicity and clarity, the file name does not include the full path or file extension.
|
||||
*
|
||||
* For example: DDLogWarn(@"%@: Unable to find thingy", THIS_FILE) -> @"MyViewController: Unable to find thingy"
|
||||
**/
|
||||
#define THIS_FILE (DDExtractFileNameWithoutExtension(__FILE__, NO))
|
||||
|
||||
/**
|
||||
* The THIS_METHOD macro gives you the name of the current objective-c method.
|
||||
*
|
||||
* For example: DDLogWarn(@"%@ - Requires non-nil strings", THIS_METHOD) -> @"setMake:model: requires non-nil strings"
|
||||
*
|
||||
* Note: This does NOT work in straight C functions (non objective-c).
|
||||
* Instead you should use the predefined __FUNCTION__ macro.
|
||||
**/
|
||||
#define THIS_METHOD NSStringFromSelector(_cmd)
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark -
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* The main class, exposes all logging mechanisms, loggers, ...
|
||||
* For most of the users, this class is hidden behind the logging functions like `DDLogInfo`
|
||||
*/
|
||||
@interface DDLog : NSObject
|
||||
|
||||
/**
|
||||
* Provides access to the underlying logging queue.
|
||||
* This may be helpful to Logger classes for things like thread synchronization.
|
||||
**/
|
||||
+ (dispatch_queue_t)loggingQueue;
|
||||
|
||||
/**
|
||||
* Logging Primitive.
|
||||
*
|
||||
* This method is used by the macros or logging functions.
|
||||
* It is suggested you stick with the macros as they're easier to use.
|
||||
*
|
||||
* @param asynchronous YES if the logging is done async, NO if you want to force sync
|
||||
* @param level the log level
|
||||
* @param flag the log flag
|
||||
* @param context the context (if any is defined)
|
||||
* @param file the current file
|
||||
* @param function the current function
|
||||
* @param line the current code line
|
||||
* @param tag potential tag
|
||||
* @param format the log format
|
||||
*/
|
||||
+ (void)log:(BOOL)asynchronous
|
||||
level:(DDLogLevel)level
|
||||
flag:(DDLogFlag)flag
|
||||
context:(NSInteger)context
|
||||
file:(const char *)file
|
||||
function:(const char *)function
|
||||
line:(NSUInteger)line
|
||||
tag:(id)tag
|
||||
format:(NSString *)format, ... NS_FORMAT_FUNCTION(9,10);
|
||||
|
||||
/**
|
||||
* Logging Primitive.
|
||||
*
|
||||
* This method can be used if you have a prepared va_list.
|
||||
* Similar to `log:level:flag:context:file:function:line:tag:format:...`
|
||||
*
|
||||
* @param asynchronous YES if the logging is done async, NO if you want to force sync
|
||||
* @param level the log level
|
||||
* @param flag the log flag
|
||||
* @param context the context (if any is defined)
|
||||
* @param file the current file
|
||||
* @param function the current function
|
||||
* @param line the current code line
|
||||
* @param tag potential tag
|
||||
* @param format the log format
|
||||
* @param argList the arguments list as a va_list
|
||||
*/
|
||||
+ (void)log:(BOOL)asynchronous
|
||||
level:(DDLogLevel)level
|
||||
flag:(DDLogFlag)flag
|
||||
context:(NSInteger)context
|
||||
file:(const char *)file
|
||||
function:(const char *)function
|
||||
line:(NSUInteger)line
|
||||
tag:(id)tag
|
||||
format:(NSString *)format
|
||||
args:(va_list)argList;
|
||||
|
||||
/**
|
||||
* Logging Primitive.
|
||||
*
|
||||
* @param asynchronous YES if the logging is done async, NO if you want to force sync
|
||||
* @param message the message
|
||||
* @param level the log level
|
||||
* @param flag the log flag
|
||||
* @param context the context (if any is defined)
|
||||
* @param file the current file
|
||||
* @param function the current function
|
||||
* @param line the current code line
|
||||
* @param tag potential tag
|
||||
*/
|
||||
+ (void)log:(BOOL)asynchronous
|
||||
message:(NSString *)message
|
||||
level:(DDLogLevel)level
|
||||
flag:(DDLogFlag)flag
|
||||
context:(NSInteger)context
|
||||
file:(const char *)file
|
||||
function:(const char *)function
|
||||
line:(NSUInteger)line
|
||||
tag:(id)tag;
|
||||
|
||||
/**
|
||||
* Logging Primitive.
|
||||
*
|
||||
* This method can be used if you manualy prepared DDLogMessage.
|
||||
*
|
||||
* @param asynchronous YES if the logging is done async, NO if you want to force sync
|
||||
* @param logMessage the log message stored in a `DDLogMessage` model object
|
||||
*/
|
||||
+ (void)log:(BOOL)asynchronous
|
||||
message:(DDLogMessage *)logMessage;
|
||||
|
||||
/**
|
||||
* Since logging can be asynchronous, there may be times when you want to flush the logs.
|
||||
* The framework invokes this automatically when the application quits.
|
||||
**/
|
||||
+ (void)flushLog;
|
||||
|
||||
/**
|
||||
* Loggers
|
||||
*
|
||||
* In order for your log statements to go somewhere, you should create and add a logger.
|
||||
*
|
||||
* You can add multiple loggers in order to direct your log statements to multiple places.
|
||||
* And each logger can be configured separately.
|
||||
* So you could have, for example, verbose logging to the console, but a concise log file with only warnings & errors.
|
||||
**/
|
||||
|
||||
/**
|
||||
* Adds the logger to the system.
|
||||
*
|
||||
* This is equivalent to invoking `[DDLog addLogger:logger withLogLevel:DDLogLevelAll]`.
|
||||
**/
|
||||
+ (void)addLogger:(id <DDLogger>)logger;
|
||||
|
||||
/**
|
||||
* Adds the logger to the system.
|
||||
*
|
||||
* The level that you provide here is a preemptive filter (for performance).
|
||||
* That is, the level specified here will be used to filter out logMessages so that
|
||||
* the logger is never even invoked for the messages.
|
||||
*
|
||||
* More information:
|
||||
* When you issue a log statement, the logging framework iterates over each logger,
|
||||
* and checks to see if it should forward the logMessage to the logger.
|
||||
* This check is done using the level parameter passed to this method.
|
||||
*
|
||||
* For example:
|
||||
*
|
||||
* `[DDLog addLogger:consoleLogger withLogLevel:DDLogLevelVerbose];`
|
||||
* `[DDLog addLogger:fileLogger withLogLevel:DDLogLevelWarning];`
|
||||
*
|
||||
* `DDLogError(@"oh no");` => gets forwarded to consoleLogger & fileLogger
|
||||
* `DDLogInfo(@"hi");` => gets forwarded to consoleLogger only
|
||||
*
|
||||
* It is important to remember that Lumberjack uses a BITMASK.
|
||||
* Many developers & third party frameworks may define extra log levels & flags.
|
||||
* For example:
|
||||
*
|
||||
* `#define SOME_FRAMEWORK_LOG_FLAG_TRACE (1 << 6) // 0...1000000`
|
||||
*
|
||||
* So if you specify `DDLogLevelVerbose` to this method, you won't see the framework's trace messages.
|
||||
*
|
||||
* `(SOME_FRAMEWORK_LOG_FLAG_TRACE & DDLogLevelVerbose) => (01000000 & 00011111) => NO`
|
||||
*
|
||||
* Consider passing `DDLogLevelAll` to this method, which has all bits set.
|
||||
* You can also use the exclusive-or bitwise operator to get a bitmask that has all flags set,
|
||||
* except the ones you explicitly don't want. For example, if you wanted everything except verbose & debug:
|
||||
*
|
||||
* `((DDLogLevelAll ^ DDLogLevelVerbose) | DDLogLevelInfo)`
|
||||
**/
|
||||
+ (void)addLogger:(id <DDLogger>)logger withLevel:(DDLogLevel)level;
|
||||
|
||||
/**
|
||||
* Remove the logger from the system
|
||||
*/
|
||||
+ (void)removeLogger:(id <DDLogger>)logger;
|
||||
|
||||
/**
|
||||
* Remove all the current loggers
|
||||
*/
|
||||
+ (void)removeAllLoggers;
|
||||
|
||||
/**
|
||||
* Return all the current loggers
|
||||
*/
|
||||
+ (NSArray *)allLoggers;
|
||||
|
||||
/**
|
||||
* Registered Dynamic Logging
|
||||
*
|
||||
* These methods allow you to obtain a list of classes that are using registered dynamic logging,
|
||||
* and also provides methods to get and set their log level during run time.
|
||||
**/
|
||||
|
||||
/**
|
||||
* Returns an array with the classes that are using registered dynamic logging
|
||||
*/
|
||||
+ (NSArray *)registeredClasses;
|
||||
|
||||
/**
|
||||
* Returns an array with the classes names that are using registered dynamic logging
|
||||
*/
|
||||
+ (NSArray *)registeredClassNames;
|
||||
|
||||
/**
|
||||
* Returns the current log level for a certain class
|
||||
*
|
||||
* @param aClass `Class` param
|
||||
*/
|
||||
+ (DDLogLevel)levelForClass:(Class)aClass;
|
||||
|
||||
/**
|
||||
* Returns the current log level for a certain class
|
||||
*
|
||||
* @param aClassName string param
|
||||
*/
|
||||
+ (DDLogLevel)levelForClassWithName:(NSString *)aClassName;
|
||||
|
||||
/**
|
||||
* Set the log level for a certain class
|
||||
*
|
||||
* @param level the new level
|
||||
* @param aClass `Class` param
|
||||
*/
|
||||
+ (void)setLevel:(DDLogLevel)level forClass:(Class)aClass;
|
||||
|
||||
/**
|
||||
* Set the log level for a certain class
|
||||
*
|
||||
* @param level the new level
|
||||
* @param aClassName string param
|
||||
*/
|
||||
+ (void)setLevel:(DDLogLevel)level forClassWithName:(NSString *)aClassName;
|
||||
|
||||
@end
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark -
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* This protocol describes a basic logger behavior.
|
||||
* Basically, it can log messages, store a logFormatter plus a bunch of optional behaviors.
|
||||
* (i.e. flush, get its loggerQueue, get its name, ...
|
||||
*/
|
||||
@protocol DDLogger <NSObject>
|
||||
|
||||
/**
|
||||
* The log message method
|
||||
*
|
||||
* @param logMessage the message (model)
|
||||
*/
|
||||
- (void)logMessage:(DDLogMessage *)logMessage;
|
||||
|
||||
/**
|
||||
* Formatters may optionally be added to any logger.
|
||||
*
|
||||
* If no formatter is set, the logger simply logs the message as it is given in logMessage,
|
||||
* or it may use its own built in formatting style.
|
||||
**/
|
||||
@property (nonatomic, strong) id <DDLogFormatter> logFormatter;
|
||||
|
||||
@optional
|
||||
|
||||
/**
|
||||
* Since logging is asynchronous, adding and removing loggers is also asynchronous.
|
||||
* In other words, the loggers are added and removed at appropriate times with regards to log messages.
|
||||
*
|
||||
* - Loggers will not receive log messages that were executed prior to when they were added.
|
||||
* - Loggers will not receive log messages that were executed after they were removed.
|
||||
*
|
||||
* These methods are executed in the logging thread/queue.
|
||||
* This is the same thread/queue that will execute every logMessage: invocation.
|
||||
* Loggers may use these methods for thread synchronization or other setup/teardown tasks.
|
||||
**/
|
||||
- (void)didAddLogger;
|
||||
|
||||
/**
|
||||
* See the above description for `didAddLoger`
|
||||
*/
|
||||
- (void)willRemoveLogger;
|
||||
|
||||
/**
|
||||
* Some loggers may buffer IO for optimization purposes.
|
||||
* For example, a database logger may only save occasionaly as the disk IO is slow.
|
||||
* In such loggers, this method should be implemented to flush any pending IO.
|
||||
*
|
||||
* This allows invocations of DDLog's flushLog method to be propogated to loggers that need it.
|
||||
*
|
||||
* Note that DDLog's flushLog method is invoked automatically when the application quits,
|
||||
* and it may be also invoked manually by the developer prior to application crashes, or other such reasons.
|
||||
**/
|
||||
- (void)flush;
|
||||
|
||||
/**
|
||||
* Each logger is executed concurrently with respect to the other loggers.
|
||||
* Thus, a dedicated dispatch queue is used for each logger.
|
||||
* Logger implementations may optionally choose to provide their own dispatch queue.
|
||||
**/
|
||||
@property (nonatomic, DISPATCH_QUEUE_REFERENCE_TYPE, readonly) dispatch_queue_t loggerQueue;
|
||||
|
||||
/**
|
||||
* If the logger implementation does not choose to provide its own queue,
|
||||
* one will automatically be created for it.
|
||||
* The created queue will receive its name from this method.
|
||||
* This may be helpful for debugging or profiling reasons.
|
||||
**/
|
||||
@property (nonatomic, readonly) NSString *loggerName;
|
||||
|
||||
@end
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark -
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* This protocol describes the behavior of a log formatter
|
||||
*/
|
||||
@protocol DDLogFormatter <NSObject>
|
||||
@required
|
||||
|
||||
/**
|
||||
* Formatters may optionally be added to any logger.
|
||||
* This allows for increased flexibility in the logging environment.
|
||||
* For example, log messages for log files may be formatted differently than log messages for the console.
|
||||
*
|
||||
* For more information about formatters, see the "Custom Formatters" page:
|
||||
* Documentation/CustomFormatters.md
|
||||
*
|
||||
* The formatter may also optionally filter the log message by returning nil,
|
||||
* in which case the logger will not log the message.
|
||||
**/
|
||||
- (NSString *)formatLogMessage:(DDLogMessage *)logMessage;
|
||||
|
||||
@optional
|
||||
|
||||
/**
|
||||
* A single formatter instance can be added to multiple loggers.
|
||||
* These methods provides hooks to notify the formatter of when it's added/removed.
|
||||
*
|
||||
* This is primarily for thread-safety.
|
||||
* If a formatter is explicitly not thread-safe, it may wish to throw an exception if added to multiple loggers.
|
||||
* Or if a formatter has potentially thread-unsafe code (e.g. NSDateFormatter),
|
||||
* it could possibly use these hooks to switch to thread-safe versions of the code.
|
||||
**/
|
||||
- (void)didAddToLogger:(id <DDLogger>)logger;
|
||||
|
||||
/**
|
||||
* See the above description for `didAddToLogger:`
|
||||
*/
|
||||
- (void)willRemoveFromLogger:(id <DDLogger>)logger;
|
||||
|
||||
@end
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark -
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* This protocol describes a dynamic logging component
|
||||
*/
|
||||
@protocol DDRegisteredDynamicLogging
|
||||
|
||||
/**
|
||||
* Implement these methods to allow a file's log level to be managed from a central location.
|
||||
*
|
||||
* This is useful if you'd like to be able to change log levels for various parts
|
||||
* of your code from within the running application.
|
||||
*
|
||||
* Imagine pulling up the settings for your application,
|
||||
* and being able to configure the logging level on a per file basis.
|
||||
*
|
||||
* The implementation can be very straight-forward:
|
||||
*
|
||||
* ```
|
||||
* + (int)ddLogLevel
|
||||
* {
|
||||
* return ddLogLevel;
|
||||
* }
|
||||
*
|
||||
* + (void)ddSetLogLevel:(DDLogLevel)level
|
||||
* {
|
||||
* ddLogLevel = level;
|
||||
* }
|
||||
* ```
|
||||
**/
|
||||
+ (DDLogLevel)ddLogLevel;
|
||||
|
||||
/**
|
||||
* See the above description for `ddLogLevel`
|
||||
*/
|
||||
+ (void)ddSetLogLevel:(DDLogLevel)level;
|
||||
|
||||
@end
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark -
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef NS_DESIGNATED_INITIALIZER
|
||||
#define NS_DESIGNATED_INITIALIZER
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Log message options, allow copying certain log elements
|
||||
*/
|
||||
typedef NS_OPTIONS(NSInteger, DDLogMessageOptions){
|
||||
/**
|
||||
* Use this to use a copy of the file path
|
||||
*/
|
||||
DDLogMessageCopyFile = 1 << 0,
|
||||
/**
|
||||
* Use this to use a copy of the function name
|
||||
*/
|
||||
DDLogMessageCopyFunction = 1 << 1
|
||||
};
|
||||
|
||||
/**
|
||||
* The `DDLogMessage` class encapsulates information about the log message.
|
||||
* If you write custom loggers or formatters, you will be dealing with objects of this class.
|
||||
**/
|
||||
@interface DDLogMessage : NSObject <NSCopying>
|
||||
{
|
||||
// Direct accessors to be used only for performance
|
||||
@public
|
||||
NSString *_message;
|
||||
DDLogLevel _level;
|
||||
DDLogFlag _flag;
|
||||
NSInteger _context;
|
||||
NSString *_file;
|
||||
NSString *_fileName;
|
||||
NSString *_function;
|
||||
NSUInteger _line;
|
||||
id _tag;
|
||||
DDLogMessageOptions _options;
|
||||
NSDate *_timestamp;
|
||||
NSString *_threadID;
|
||||
NSString *_threadName;
|
||||
NSString *_queueLabel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Default `init` is not available
|
||||
*/
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
|
||||
/**
|
||||
* Standard init method for a log message object.
|
||||
* Used by the logging primitives. (And the macros use the logging primitives.)
|
||||
*
|
||||
* If you find need to manually create logMessage objects, there is one thing you should be aware of:
|
||||
*
|
||||
* If no flags are passed, the method expects the file and function parameters to be string literals.
|
||||
* That is, it expects the given strings to exist for the duration of the object's lifetime,
|
||||
* and it expects the given strings to be immutable.
|
||||
* In other words, it does not copy these strings, it simply points to them.
|
||||
* This is due to the fact that __FILE__ and __FUNCTION__ are usually used to specify these parameters,
|
||||
* so it makes sense to optimize and skip the unnecessary allocations.
|
||||
* However, if you need them to be copied you may use the options parameter to specify this.
|
||||
*
|
||||
* @param message the message
|
||||
* @param level the log level
|
||||
* @param flag the log flag
|
||||
* @param context the context (if any is defined)
|
||||
* @param file the current file
|
||||
* @param function the current function
|
||||
* @param line the current code line
|
||||
* @param tag potential tag
|
||||
* @param options a bitmask which supports DDLogMessageCopyFile and DDLogMessageCopyFunction.
|
||||
* @param timestamp the log timestamp
|
||||
*
|
||||
* @return a new instance of a log message model object
|
||||
*/
|
||||
- (instancetype)initWithMessage:(NSString *)message
|
||||
level:(DDLogLevel)level
|
||||
flag:(DDLogFlag)flag
|
||||
context:(NSInteger)context
|
||||
file:(NSString *)file
|
||||
function:(NSString *)function
|
||||
line:(NSUInteger)line
|
||||
tag:(id)tag
|
||||
options:(DDLogMessageOptions)options
|
||||
timestamp:(NSDate *)timestamp NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
/**
|
||||
* Read-only properties
|
||||
**/
|
||||
|
||||
/**
|
||||
* The log message
|
||||
*/
|
||||
@property (readonly, nonatomic) NSString *message;
|
||||
@property (readonly, nonatomic) DDLogLevel level;
|
||||
@property (readonly, nonatomic) DDLogFlag flag;
|
||||
@property (readonly, nonatomic) NSInteger context;
|
||||
@property (readonly, nonatomic) NSString *file;
|
||||
@property (readonly, nonatomic) NSString *fileName;
|
||||
@property (readonly, nonatomic) NSString *function;
|
||||
@property (readonly, nonatomic) NSUInteger line;
|
||||
@property (readonly, nonatomic) id tag;
|
||||
@property (readonly, nonatomic) DDLogMessageOptions options;
|
||||
@property (readonly, nonatomic) NSDate *timestamp;
|
||||
@property (readonly, nonatomic) NSString *threadID; // ID as it appears in NSLog calculated from the machThreadID
|
||||
@property (readonly, nonatomic) NSString *threadName;
|
||||
@property (readonly, nonatomic) NSString *queueLabel;
|
||||
|
||||
@end
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark -
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* The `DDLogger` protocol specifies that an optional formatter can be added to a logger.
|
||||
* Most (but not all) loggers will want to support formatters.
|
||||
*
|
||||
* However, writting getters and setters in a thread safe manner,
|
||||
* while still maintaining maximum speed for the logging process, is a difficult task.
|
||||
*
|
||||
* To do it right, the implementation of the getter/setter has strict requiremenets:
|
||||
* - Must NOT require the `logMessage:` method to acquire a lock.
|
||||
* - Must NOT require the `logMessage:` method to access an atomic property (also a lock of sorts).
|
||||
*
|
||||
* To simplify things, an abstract logger is provided that implements the getter and setter.
|
||||
*
|
||||
* Logger implementations may simply extend this class,
|
||||
* and they can ACCESS THE FORMATTER VARIABLE DIRECTLY from within their `logMessage:` method!
|
||||
**/
|
||||
@interface DDAbstractLogger : NSObject <DDLogger>
|
||||
{
|
||||
// Direct accessors to be used only for performance
|
||||
@public
|
||||
id <DDLogFormatter> _logFormatter;
|
||||
dispatch_queue_t _loggerQueue;
|
||||
}
|
||||
|
||||
@property (nonatomic, strong) id <DDLogFormatter> logFormatter;
|
||||
@property (nonatomic, DISPATCH_QUEUE_REFERENCE_TYPE) dispatch_queue_t loggerQueue;
|
||||
|
||||
// For thread-safety assertions
|
||||
|
||||
/**
|
||||
* Return YES if the current logger uses a global queue for logging
|
||||
*/
|
||||
@property (nonatomic, readonly, getter=isOnGlobalLoggingQueue) BOOL onGlobalLoggingQueue;
|
||||
|
||||
/**
|
||||
* Return YES if the current logger uses the internal designated queue for logging
|
||||
*/
|
||||
@property (nonatomic, readonly, getter=isOnInternalLoggerQueue) BOOL onInternalLoggerQueue;
|
||||
|
||||
@end
|
||||
|
||||
82
Example/Pods/CocoaLumberjack/Classes/DDLogMacros.h
generated
82
Example/Pods/CocoaLumberjack/Classes/DDLogMacros.h
generated
@ -1,82 +0,0 @@
|
||||
// Software License Agreement (BSD License)
|
||||
//
|
||||
// Copyright (c) 2010-2015, Deusty, LLC
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use of this software in source and binary forms,
|
||||
// with or without modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Neither the name of Deusty nor the names of its contributors may be used
|
||||
// to endorse or promote products derived from this software without specific
|
||||
// prior written permission of Deusty, LLC.
|
||||
|
||||
// Disable legacy macros
|
||||
#ifndef DD_LEGACY_MACROS
|
||||
#define DD_LEGACY_MACROS 0
|
||||
#endif
|
||||
|
||||
#import "DDLog.h"
|
||||
|
||||
/**
|
||||
* The constant/variable/method responsible for controlling the current log level.
|
||||
**/
|
||||
#ifndef LOG_LEVEL_DEF
|
||||
#define LOG_LEVEL_DEF ddLogLevel
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Whether async should be used by log messages, excluding error messages that are always sent sync.
|
||||
**/
|
||||
#ifndef LOG_ASYNC_ENABLED
|
||||
#define LOG_ASYNC_ENABLED YES
|
||||
#endif
|
||||
|
||||
/**
|
||||
* This is the single macro that all other macros below compile into.
|
||||
* This big multiline macro makes all the other macros easier to read.
|
||||
**/
|
||||
#define LOG_MACRO(isAsynchronous, lvl, flg, ctx, atag, fnct, frmt, ...) \
|
||||
[DDLog log : isAsynchronous \
|
||||
level : lvl \
|
||||
flag : flg \
|
||||
context : ctx \
|
||||
file : __FILE__ \
|
||||
function : fnct \
|
||||
line : __LINE__ \
|
||||
tag : atag \
|
||||
format : (frmt), ## __VA_ARGS__]
|
||||
|
||||
/**
|
||||
* Define version of the macro that only execute if the log level is above the threshold.
|
||||
* The compiled versions essentially look like this:
|
||||
*
|
||||
* if (logFlagForThisLogMsg & ddLogLevel) { execute log message }
|
||||
*
|
||||
* When LOG_LEVEL_DEF is defined as ddLogLevel.
|
||||
*
|
||||
* As shown further below, Lumberjack actually uses a bitmask as opposed to primitive log levels.
|
||||
* This allows for a great amount of flexibility and some pretty advanced fine grained logging techniques.
|
||||
*
|
||||
* Note that when compiler optimizations are enabled (as they are for your release builds),
|
||||
* the log messages above your logging threshold will automatically be compiled out.
|
||||
*
|
||||
* (If the compiler sees LOG_LEVEL_DEF/ddLogLevel declared as a constant, the compiler simply checks to see
|
||||
* if the 'if' statement would execute, and if not it strips it from the binary.)
|
||||
*
|
||||
* We also define shorthand versions for asynchronous and synchronous logging.
|
||||
**/
|
||||
#define LOG_MAYBE(async, lvl, flg, ctx, tag, fnct, frmt, ...) \
|
||||
do { if(lvl & flg) LOG_MACRO(async, lvl, flg, ctx, tag, fnct, frmt, ##__VA_ARGS__); } while(0)
|
||||
|
||||
/**
|
||||
* Ready to use log macros with no context or tag.
|
||||
**/
|
||||
#define DDLogError(frmt, ...) LOG_MAYBE(NO, LOG_LEVEL_DEF, DDLogFlagError, 0, nil, __PRETTY_FUNCTION__, frmt, ##__VA_ARGS__)
|
||||
#define DDLogWarn(frmt, ...) LOG_MAYBE(LOG_ASYNC_ENABLED, LOG_LEVEL_DEF, DDLogFlagWarning, 0, nil, __PRETTY_FUNCTION__, frmt, ##__VA_ARGS__)
|
||||
#define DDLogInfo(frmt, ...) LOG_MAYBE(LOG_ASYNC_ENABLED, LOG_LEVEL_DEF, DDLogFlagInfo, 0, nil, __PRETTY_FUNCTION__, frmt, ##__VA_ARGS__)
|
||||
#define DDLogDebug(frmt, ...) LOG_MAYBE(LOG_ASYNC_ENABLED, LOG_LEVEL_DEF, DDLogFlagDebug, 0, nil, __PRETTY_FUNCTION__, frmt, ##__VA_ARGS__)
|
||||
#define DDLogVerbose(frmt, ...) LOG_MAYBE(LOG_ASYNC_ENABLED, LOG_LEVEL_DEF, DDLogFlagVerbose, 0, nil, __PRETTY_FUNCTION__, frmt, ##__VA_ARGS__)
|
||||
|
||||
1481
Example/Pods/CocoaLumberjack/Classes/DDTTYLogger.m
generated
1481
Example/Pods/CocoaLumberjack/Classes/DDTTYLogger.m
generated
File diff suppressed because it is too large
Load Diff
@ -1,117 +0,0 @@
|
||||
// Software License Agreement (BSD License)
|
||||
//
|
||||
// Copyright (c) 2010-2015, Deusty, LLC
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use of this software in source and binary forms,
|
||||
// with or without modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Neither the name of Deusty nor the names of its contributors may be used
|
||||
// to endorse or promote products derived from this software without specific
|
||||
// prior written permission of Deusty, LLC.
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
// Disable legacy macros
|
||||
#ifndef DD_LEGACY_MACROS
|
||||
#define DD_LEGACY_MACROS 0
|
||||
#endif
|
||||
|
||||
#import "DDLog.h"
|
||||
|
||||
/**
|
||||
* This class provides a log formatter that filters log statements from a logging context not on the whitelist.
|
||||
*
|
||||
* A log formatter can be added to any logger to format and/or filter its output.
|
||||
* You can learn more about log formatters here:
|
||||
* Documentation/CustomFormatters.md
|
||||
*
|
||||
* You can learn more about logging context's here:
|
||||
* Documentation/CustomContext.md
|
||||
*
|
||||
* But here's a quick overview / refresher:
|
||||
*
|
||||
* Every log statement has a logging context.
|
||||
* These come from the underlying logging macros defined in DDLog.h.
|
||||
* The default logging context is zero.
|
||||
* You can define multiple logging context's for use in your application.
|
||||
* For example, logically separate parts of your app each have a different logging context.
|
||||
* Also 3rd party frameworks that make use of Lumberjack generally use their own dedicated logging context.
|
||||
**/
|
||||
@interface DDContextWhitelistFilterLogFormatter : NSObject <DDLogFormatter>
|
||||
|
||||
/**
|
||||
* Designated default initializer
|
||||
*/
|
||||
- (instancetype)init NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
/**
|
||||
* Add a context to the whitelist
|
||||
*
|
||||
* @param loggingContext the context
|
||||
*/
|
||||
- (void)addToWhitelist:(NSUInteger)loggingContext;
|
||||
|
||||
/**
|
||||
* Remove context from whitelist
|
||||
*
|
||||
* @param loggingContext the context
|
||||
*/
|
||||
- (void)removeFromWhitelist:(NSUInteger)loggingContext;
|
||||
|
||||
/**
|
||||
* Return the whitelist
|
||||
*/
|
||||
@property (readonly, copy) NSArray *whitelist;
|
||||
|
||||
/**
|
||||
* Check if a context is on the whitelist
|
||||
*
|
||||
* @param loggingContext the context
|
||||
*/
|
||||
- (BOOL)isOnWhitelist:(NSUInteger)loggingContext;
|
||||
|
||||
@end
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark -
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* This class provides a log formatter that filters log statements from a logging context on the blacklist.
|
||||
**/
|
||||
@interface DDContextBlacklistFilterLogFormatter : NSObject <DDLogFormatter>
|
||||
|
||||
- (instancetype)init NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
/**
|
||||
* Add a context to the blacklist
|
||||
*
|
||||
* @param loggingContext the context
|
||||
*/
|
||||
- (void)addToBlacklist:(NSUInteger)loggingContext;
|
||||
|
||||
/**
|
||||
* Remove context from blacklist
|
||||
*
|
||||
* @param loggingContext the context
|
||||
*/
|
||||
- (void)removeFromBlacklist:(NSUInteger)loggingContext;
|
||||
|
||||
/**
|
||||
* Return the blacklist
|
||||
*/
|
||||
@property (readonly, copy) NSArray *blacklist;
|
||||
|
||||
|
||||
/**
|
||||
* Check if a context is on the blacklist
|
||||
*
|
||||
* @param loggingContext the context
|
||||
*/
|
||||
- (BOOL)isOnBlacklist:(NSUInteger)loggingContext;
|
||||
|
||||
@end
|
||||
@ -1,191 +0,0 @@
|
||||
// Software License Agreement (BSD License)
|
||||
//
|
||||
// Copyright (c) 2010-2015, Deusty, LLC
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use of this software in source and binary forms,
|
||||
// with or without modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Neither the name of Deusty nor the names of its contributors may be used
|
||||
// to endorse or promote products derived from this software without specific
|
||||
// prior written permission of Deusty, LLC.
|
||||
|
||||
#import "DDContextFilterLogFormatter.h"
|
||||
#import <libkern/OSAtomic.h>
|
||||
|
||||
#if !__has_feature(objc_arc)
|
||||
#error This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC).
|
||||
#endif
|
||||
|
||||
@interface DDLoggingContextSet : NSObject
|
||||
|
||||
- (void)addToSet:(NSUInteger)loggingContext;
|
||||
- (void)removeFromSet:(NSUInteger)loggingContext;
|
||||
|
||||
@property (readonly, copy) NSArray *currentSet;
|
||||
|
||||
- (BOOL)isInSet:(NSUInteger)loggingContext;
|
||||
|
||||
@end
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark -
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@interface DDContextWhitelistFilterLogFormatter () {
|
||||
DDLoggingContextSet *_contextSet;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation DDContextWhitelistFilterLogFormatter
|
||||
|
||||
- (instancetype)init {
|
||||
if ((self = [super init])) {
|
||||
_contextSet = [[DDLoggingContextSet alloc] init];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)addToWhitelist:(NSUInteger)loggingContext {
|
||||
[_contextSet addToSet:loggingContext];
|
||||
}
|
||||
|
||||
- (void)removeFromWhitelist:(NSUInteger)loggingContext {
|
||||
[_contextSet removeFromSet:loggingContext];
|
||||
}
|
||||
|
||||
- (NSArray *)whitelist {
|
||||
return [_contextSet currentSet];
|
||||
}
|
||||
|
||||
- (BOOL)isOnWhitelist:(NSUInteger)loggingContext {
|
||||
return [_contextSet isInSet:loggingContext];
|
||||
}
|
||||
|
||||
- (NSString *)formatLogMessage:(DDLogMessage *)logMessage {
|
||||
if ([self isOnWhitelist:logMessage->_context]) {
|
||||
return logMessage->_message;
|
||||
} else {
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark -
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@interface DDContextBlacklistFilterLogFormatter () {
|
||||
DDLoggingContextSet *_contextSet;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation DDContextBlacklistFilterLogFormatter
|
||||
|
||||
- (instancetype)init {
|
||||
if ((self = [super init])) {
|
||||
_contextSet = [[DDLoggingContextSet alloc] init];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)addToBlacklist:(NSUInteger)loggingContext {
|
||||
[_contextSet addToSet:loggingContext];
|
||||
}
|
||||
|
||||
- (void)removeFromBlacklist:(NSUInteger)loggingContext {
|
||||
[_contextSet removeFromSet:loggingContext];
|
||||
}
|
||||
|
||||
- (NSArray *)blacklist {
|
||||
return [_contextSet currentSet];
|
||||
}
|
||||
|
||||
- (BOOL)isOnBlacklist:(NSUInteger)loggingContext {
|
||||
return [_contextSet isInSet:loggingContext];
|
||||
}
|
||||
|
||||
- (NSString *)formatLogMessage:(DDLogMessage *)logMessage {
|
||||
if ([self isOnBlacklist:logMessage->_context]) {
|
||||
return nil;
|
||||
} else {
|
||||
return logMessage->_message;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark -
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
@interface DDLoggingContextSet () {
|
||||
OSSpinLock _lock;
|
||||
NSMutableSet *_set;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation DDLoggingContextSet
|
||||
|
||||
- (instancetype)init {
|
||||
if ((self = [super init])) {
|
||||
_set = [[NSMutableSet alloc] init];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)addToSet:(NSUInteger)loggingContext {
|
||||
OSSpinLockLock(&_lock);
|
||||
{
|
||||
[_set addObject:@(loggingContext)];
|
||||
}
|
||||
OSSpinLockUnlock(&_lock);
|
||||
}
|
||||
|
||||
- (void)removeFromSet:(NSUInteger)loggingContext {
|
||||
OSSpinLockLock(&_lock);
|
||||
{
|
||||
[_set removeObject:@(loggingContext)];
|
||||
}
|
||||
OSSpinLockUnlock(&_lock);
|
||||
}
|
||||
|
||||
- (NSArray *)currentSet {
|
||||
NSArray *result = nil;
|
||||
|
||||
OSSpinLockLock(&_lock);
|
||||
{
|
||||
result = [_set allObjects];
|
||||
}
|
||||
OSSpinLockUnlock(&_lock);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
- (BOOL)isInSet:(NSUInteger)loggingContext {
|
||||
BOOL result = NO;
|
||||
|
||||
OSSpinLockLock(&_lock);
|
||||
{
|
||||
result = [_set containsObject:@(loggingContext)];
|
||||
}
|
||||
OSSpinLockUnlock(&_lock);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@end
|
||||
@ -1,178 +0,0 @@
|
||||
// Software License Agreement (BSD License)
|
||||
//
|
||||
// Copyright (c) 2010-2015, Deusty, LLC
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use of this software in source and binary forms,
|
||||
// with or without modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Neither the name of Deusty nor the names of its contributors may be used
|
||||
// to endorse or promote products derived from this software without specific
|
||||
// prior written permission of Deusty, LLC.
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <libkern/OSAtomic.h>
|
||||
|
||||
// Disable legacy macros
|
||||
#ifndef DD_LEGACY_MACROS
|
||||
#define DD_LEGACY_MACROS 0
|
||||
#endif
|
||||
|
||||
#import "DDLog.h"
|
||||
|
||||
/**
|
||||
* Log formatter mode
|
||||
*/
|
||||
typedef NS_ENUM(NSUInteger, DDDispatchQueueLogFormatterMode){
|
||||
/**
|
||||
* This is the default option, means the formatter can be reused between multiple loggers and therefore is thread-safe.
|
||||
* There is, of course, a performance cost for the thread-safety
|
||||
*/
|
||||
DDDispatchQueueLogFormatterModeShareble = 0,
|
||||
/**
|
||||
* If the formatter will only be used by a single logger, then the thread-safety can be removed
|
||||
* @note: there is an assert checking if the formatter is added to multiple loggers and the mode is non-shareble
|
||||
*/
|
||||
DDDispatchQueueLogFormatterModeNonShareble,
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* This class provides a log formatter that prints the dispatch_queue label instead of the mach_thread_id.
|
||||
*
|
||||
* A log formatter can be added to any logger to format and/or filter its output.
|
||||
* You can learn more about log formatters here:
|
||||
* Documentation/CustomFormatters.md
|
||||
*
|
||||
* A typical `NSLog` (or `DDTTYLogger`) prints detailed info as `[<process_id>:<thread_id>]`.
|
||||
* For example:
|
||||
*
|
||||
* `2011-10-17 20:21:45.435 AppName[19928:5207] Your log message here`
|
||||
*
|
||||
* Where:
|
||||
* `- 19928 = process id`
|
||||
* `- 5207 = thread id (mach_thread_id printed in hex)`
|
||||
*
|
||||
* When using grand central dispatch (GCD), this information is less useful.
|
||||
* This is because a single serial dispatch queue may be run on any thread from an internally managed thread pool.
|
||||
* For example:
|
||||
*
|
||||
* `2011-10-17 20:32:31.111 AppName[19954:4d07] Message from my_serial_dispatch_queue`
|
||||
* `2011-10-17 20:32:31.112 AppName[19954:5207] Message from my_serial_dispatch_queue`
|
||||
* `2011-10-17 20:32:31.113 AppName[19954:2c55] Message from my_serial_dispatch_queue`
|
||||
*
|
||||
* This formatter allows you to replace the standard `[box:info]` with the dispatch_queue name.
|
||||
* For example:
|
||||
*
|
||||
* `2011-10-17 20:32:31.111 AppName[img-scaling] Message from my_serial_dispatch_queue`
|
||||
* `2011-10-17 20:32:31.112 AppName[img-scaling] Message from my_serial_dispatch_queue`
|
||||
* `2011-10-17 20:32:31.113 AppName[img-scaling] Message from my_serial_dispatch_queue`
|
||||
*
|
||||
* If the dispatch_queue doesn't have a set name, then it falls back to the thread name.
|
||||
* If the current thread doesn't have a set name, then it falls back to the mach_thread_id in hex (like normal).
|
||||
*
|
||||
* Note: If manually creating your own background threads (via `NSThread/alloc/init` or `NSThread/detachNeThread`),
|
||||
* you can use `[[NSThread currentThread] setName:(NSString *)]`.
|
||||
**/
|
||||
@interface DDDispatchQueueLogFormatter : NSObject <DDLogFormatter>
|
||||
|
||||
/**
|
||||
* Standard init method.
|
||||
* Configure using properties as desired.
|
||||
**/
|
||||
- (instancetype)init NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
/**
|
||||
* Initializer with ability to set the queue mode
|
||||
*
|
||||
* @param mode choose between DDDispatchQueueLogFormatterModeShareble and DDDispatchQueueLogFormatterModeNonShareble, depending if the formatter is shared between several loggers or not
|
||||
*/
|
||||
- (instancetype)initWithMode:(DDDispatchQueueLogFormatterMode)mode;
|
||||
|
||||
/**
|
||||
* The minQueueLength restricts the minimum size of the [detail box].
|
||||
* If the minQueueLength is set to 0, there is no restriction.
|
||||
*
|
||||
* For example, say a dispatch_queue has a label of "diskIO":
|
||||
*
|
||||
* If the minQueueLength is 0: [diskIO]
|
||||
* If the minQueueLength is 4: [diskIO]
|
||||
* If the minQueueLength is 5: [diskIO]
|
||||
* If the minQueueLength is 6: [diskIO]
|
||||
* If the minQueueLength is 7: [diskIO ]
|
||||
* If the minQueueLength is 8: [diskIO ]
|
||||
*
|
||||
* The default minQueueLength is 0 (no minimum, so [detail box] won't be padded).
|
||||
*
|
||||
* If you want every [detail box] to have the exact same width,
|
||||
* set both minQueueLength and maxQueueLength to the same value.
|
||||
**/
|
||||
@property (assign, atomic) NSUInteger minQueueLength;
|
||||
|
||||
/**
|
||||
* The maxQueueLength restricts the number of characters that will be inside the [detail box].
|
||||
* If the maxQueueLength is 0, there is no restriction.
|
||||
*
|
||||
* For example, say a dispatch_queue has a label of "diskIO":
|
||||
*
|
||||
* If the maxQueueLength is 0: [diskIO]
|
||||
* If the maxQueueLength is 4: [disk]
|
||||
* If the maxQueueLength is 5: [diskI]
|
||||
* If the maxQueueLength is 6: [diskIO]
|
||||
* If the maxQueueLength is 7: [diskIO]
|
||||
* If the maxQueueLength is 8: [diskIO]
|
||||
*
|
||||
* The default maxQueueLength is 0 (no maximum, so [detail box] won't be truncated).
|
||||
*
|
||||
* If you want every [detail box] to have the exact same width,
|
||||
* set both minQueueLength and maxQueueLength to the same value.
|
||||
**/
|
||||
@property (assign, atomic) NSUInteger maxQueueLength;
|
||||
|
||||
/**
|
||||
* Sometimes queue labels have long names like "com.apple.main-queue",
|
||||
* but you'd prefer something shorter like simply "main".
|
||||
*
|
||||
* This method allows you to set such preferred replacements.
|
||||
* The above example is set by default.
|
||||
*
|
||||
* To remove/undo a previous replacement, invoke this method with nil for the 'shortLabel' parameter.
|
||||
**/
|
||||
- (NSString *)replacementStringForQueueLabel:(NSString *)longLabel;
|
||||
|
||||
/**
|
||||
* See the `replacementStringForQueueLabel:` description
|
||||
*/
|
||||
- (void)setReplacementString:(NSString *)shortLabel forQueueLabel:(NSString *)longLabel;
|
||||
|
||||
@end
|
||||
|
||||
/**
|
||||
* Category on `DDDispatchQueueLogFormatter` to make method declarations easier to extend/modify
|
||||
**/
|
||||
@interface DDDispatchQueueLogFormatter (OverridableMethods)
|
||||
|
||||
/**
|
||||
* Date formatter default configuration
|
||||
*/
|
||||
- (void)configureDateFormatter:(NSDateFormatter *)dateFormatter;
|
||||
|
||||
/**
|
||||
* Formatter method to transfrom from date to string
|
||||
*/
|
||||
- (NSString *)stringFromDate:(NSDate *)date;
|
||||
|
||||
/**
|
||||
* Method to compute the queue thread label
|
||||
*/
|
||||
- (NSString *)queueThreadLabelForLogMessage:(DDLogMessage *)logMessage;
|
||||
|
||||
/**
|
||||
* The actual method that formats a message (transforms a `DDLogMessage` model into a printable string)
|
||||
*/
|
||||
- (NSString *)formatLogMessage:(DDLogMessage *)logMessage;
|
||||
|
||||
@end
|
||||
@ -1,277 +0,0 @@
|
||||
// Software License Agreement (BSD License)
|
||||
//
|
||||
// Copyright (c) 2010-2015, Deusty, LLC
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use of this software in source and binary forms,
|
||||
// with or without modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Neither the name of Deusty nor the names of its contributors may be used
|
||||
// to endorse or promote products derived from this software without specific
|
||||
// prior written permission of Deusty, LLC.
|
||||
|
||||
#import "DDDispatchQueueLogFormatter.h"
|
||||
#import <libkern/OSAtomic.h>
|
||||
#import <objc/runtime.h>
|
||||
|
||||
|
||||
#if !__has_feature(objc_arc)
|
||||
#error This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC).
|
||||
#endif
|
||||
|
||||
@interface DDDispatchQueueLogFormatter () {
|
||||
DDDispatchQueueLogFormatterMode _mode;
|
||||
NSString *_dateFormatterKey;
|
||||
|
||||
int32_t _atomicLoggerCount;
|
||||
NSDateFormatter *_threadUnsafeDateFormatter; // Use [self stringFromDate]
|
||||
|
||||
OSSpinLock _lock;
|
||||
|
||||
NSUInteger _minQueueLength; // _prefix == Only access via atomic property
|
||||
NSUInteger _maxQueueLength; // _prefix == Only access via atomic property
|
||||
NSMutableDictionary *_replacements; // _prefix == Only access from within spinlock
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation DDDispatchQueueLogFormatter
|
||||
|
||||
- (instancetype)init {
|
||||
if ((self = [super init])) {
|
||||
_mode = DDDispatchQueueLogFormatterModeShareble;
|
||||
|
||||
// We need to carefully pick the name for storing in thread dictionary to not
|
||||
// use a formatter configured by subclass and avoid surprises.
|
||||
Class cls = [self class];
|
||||
Class superClass = class_getSuperclass(cls);
|
||||
SEL configMethodName = @selector(configureDateFormatter:);
|
||||
Method configMethod = class_getInstanceMethod(cls, configMethodName);
|
||||
while (class_getInstanceMethod(superClass, configMethodName) == configMethod) {
|
||||
cls = superClass;
|
||||
superClass = class_getSuperclass(cls);
|
||||
}
|
||||
// now `cls` is the class that provides implementation for `configureDateFormatter:`
|
||||
_dateFormatterKey = [NSString stringWithFormat:@"%s_NSDateFormatter", class_getName(cls)];
|
||||
|
||||
_atomicLoggerCount = 0;
|
||||
_threadUnsafeDateFormatter = nil;
|
||||
|
||||
_minQueueLength = 0;
|
||||
_maxQueueLength = 0;
|
||||
_replacements = [[NSMutableDictionary alloc] init];
|
||||
|
||||
// Set default replacements:
|
||||
|
||||
_replacements[@"com.apple.main-thread"] = @"main";
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithMode:(DDDispatchQueueLogFormatterMode)mode {
|
||||
if ((self = [self init])) {
|
||||
_mode = mode;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark Configuration
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@synthesize minQueueLength = _minQueueLength;
|
||||
@synthesize maxQueueLength = _maxQueueLength;
|
||||
|
||||
- (NSString *)replacementStringForQueueLabel:(NSString *)longLabel {
|
||||
NSString *result = nil;
|
||||
|
||||
OSSpinLockLock(&_lock);
|
||||
{
|
||||
result = _replacements[longLabel];
|
||||
}
|
||||
OSSpinLockUnlock(&_lock);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
- (void)setReplacementString:(NSString *)shortLabel forQueueLabel:(NSString *)longLabel {
|
||||
OSSpinLockLock(&_lock);
|
||||
{
|
||||
if (shortLabel) {
|
||||
_replacements[longLabel] = shortLabel;
|
||||
} else {
|
||||
[_replacements removeObjectForKey:longLabel];
|
||||
}
|
||||
}
|
||||
OSSpinLockUnlock(&_lock);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark DDLogFormatter
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
- (NSDateFormatter *)createDateFormatter {
|
||||
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
|
||||
[self configureDateFormatter:formatter];
|
||||
return formatter;
|
||||
}
|
||||
|
||||
- (void)configureDateFormatter:(NSDateFormatter *)dateFormatter {
|
||||
[dateFormatter setFormatterBehavior:NSDateFormatterBehavior10_4];
|
||||
[dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss:SSS"];
|
||||
[dateFormatter setLocale:[NSLocale localeWithLocaleIdentifier:@"en_US_POSIX"]];
|
||||
|
||||
NSString *calendarIdentifier = nil;
|
||||
#if defined(__IPHONE_8_0) || defined(__MAC_10_10)
|
||||
calendarIdentifier = NSCalendarIdentifierGregorian;
|
||||
#else
|
||||
calendarIdentifier = NSGregorianCalendar;
|
||||
#endif
|
||||
|
||||
[dateFormatter setCalendar:[[NSCalendar alloc] initWithCalendarIdentifier:calendarIdentifier]];
|
||||
}
|
||||
|
||||
- (NSString *)stringFromDate:(NSDate *)date {
|
||||
|
||||
NSDateFormatter *dateFormatter = nil;
|
||||
if (_mode == DDDispatchQueueLogFormatterModeNonShareble) {
|
||||
// Single-threaded mode.
|
||||
|
||||
dateFormatter = _threadUnsafeDateFormatter;
|
||||
if (dateFormatter == nil) {
|
||||
dateFormatter = [self createDateFormatter];
|
||||
_threadUnsafeDateFormatter = dateFormatter;
|
||||
}
|
||||
} else {
|
||||
// Multi-threaded mode.
|
||||
// NSDateFormatter is NOT thread-safe.
|
||||
|
||||
NSString *key = _dateFormatterKey;
|
||||
|
||||
NSMutableDictionary *threadDictionary = [[NSThread currentThread] threadDictionary];
|
||||
dateFormatter = threadDictionary[key];
|
||||
|
||||
if (dateFormatter == nil) {
|
||||
dateFormatter = [self createDateFormatter];
|
||||
threadDictionary[key] = dateFormatter;
|
||||
}
|
||||
}
|
||||
|
||||
return [dateFormatter stringFromDate:date];
|
||||
}
|
||||
|
||||
- (NSString *)queueThreadLabelForLogMessage:(DDLogMessage *)logMessage {
|
||||
// As per the DDLogFormatter contract, this method is always invoked on the same thread/dispatch_queue
|
||||
|
||||
NSUInteger minQueueLength = self.minQueueLength;
|
||||
NSUInteger maxQueueLength = self.maxQueueLength;
|
||||
|
||||
// Get the name of the queue, thread, or machID (whichever we are to use).
|
||||
|
||||
NSString *queueThreadLabel = nil;
|
||||
|
||||
BOOL useQueueLabel = YES;
|
||||
BOOL useThreadName = NO;
|
||||
|
||||
if (logMessage->_queueLabel) {
|
||||
// If you manually create a thread, it's dispatch_queue will have one of the thread names below.
|
||||
// Since all such threads have the same name, we'd prefer to use the threadName or the machThreadID.
|
||||
|
||||
NSArray *names = @[
|
||||
@"com.apple.root.low-priority",
|
||||
@"com.apple.root.default-priority",
|
||||
@"com.apple.root.high-priority",
|
||||
@"com.apple.root.low-overcommit-priority",
|
||||
@"com.apple.root.default-overcommit-priority",
|
||||
@"com.apple.root.high-overcommit-priority"
|
||||
];
|
||||
|
||||
for (NSString * name in names) {
|
||||
if ([logMessage->_queueLabel isEqualToString:name]) {
|
||||
useQueueLabel = NO;
|
||||
useThreadName = [logMessage->_threadName length] > 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
useQueueLabel = NO;
|
||||
useThreadName = [logMessage->_threadName length] > 0;
|
||||
}
|
||||
|
||||
if (useQueueLabel || useThreadName) {
|
||||
NSString *fullLabel;
|
||||
NSString *abrvLabel;
|
||||
|
||||
if (useQueueLabel) {
|
||||
fullLabel = logMessage->_queueLabel;
|
||||
} else {
|
||||
fullLabel = logMessage->_threadName;
|
||||
}
|
||||
|
||||
OSSpinLockLock(&_lock);
|
||||
{
|
||||
abrvLabel = _replacements[fullLabel];
|
||||
}
|
||||
OSSpinLockUnlock(&_lock);
|
||||
|
||||
if (abrvLabel) {
|
||||
queueThreadLabel = abrvLabel;
|
||||
} else {
|
||||
queueThreadLabel = fullLabel;
|
||||
}
|
||||
} else {
|
||||
queueThreadLabel = logMessage->_threadID;
|
||||
}
|
||||
|
||||
// Now use the thread label in the output
|
||||
|
||||
NSUInteger labelLength = [queueThreadLabel length];
|
||||
|
||||
// labelLength > maxQueueLength : truncate
|
||||
// labelLength < minQueueLength : padding
|
||||
// : exact
|
||||
|
||||
if ((maxQueueLength > 0) && (labelLength > maxQueueLength)) {
|
||||
// Truncate
|
||||
|
||||
return [queueThreadLabel substringToIndex:maxQueueLength];
|
||||
} else if (labelLength < minQueueLength) {
|
||||
// Padding
|
||||
|
||||
NSUInteger numSpaces = minQueueLength - labelLength;
|
||||
|
||||
char spaces[numSpaces + 1];
|
||||
memset(spaces, ' ', numSpaces);
|
||||
spaces[numSpaces] = '\0';
|
||||
|
||||
return [NSString stringWithFormat:@"%@%s", queueThreadLabel, spaces];
|
||||
} else {
|
||||
// Exact
|
||||
|
||||
return queueThreadLabel;
|
||||
}
|
||||
}
|
||||
|
||||
- (NSString *)formatLogMessage:(DDLogMessage *)logMessage {
|
||||
NSString *timestamp = [self stringFromDate:(logMessage->_timestamp)];
|
||||
NSString *queueThreadLabel = [self queueThreadLabelForLogMessage:logMessage];
|
||||
|
||||
return [NSString stringWithFormat:@"%@ [%@] %@", timestamp, queueThreadLabel, logMessage->_message];
|
||||
}
|
||||
|
||||
- (void)didAddToLogger:(id <DDLogger> __attribute__((unused)))logger {
|
||||
int32_t count = 0;
|
||||
count = OSAtomicIncrement32(&_atomicLoggerCount);
|
||||
NSAssert(count <= 1 || _mode == DDDispatchQueueLogFormatterModeShareble, @"Can't reuse formatter with multiple loggers in non-shareable mode.");
|
||||
}
|
||||
|
||||
- (void)willRemoveFromLogger:(id <DDLogger> __attribute__((unused)))logger {
|
||||
OSAtomicDecrement32(&_atomicLoggerCount);
|
||||
}
|
||||
|
||||
@end
|
||||
@ -1,56 +0,0 @@
|
||||
// Software License Agreement (BSD License)
|
||||
//
|
||||
// Copyright (c) 2010-2015, Deusty, LLC
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use of this software in source and binary forms,
|
||||
// with or without modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Neither the name of Deusty nor the names of its contributors may be used
|
||||
// to endorse or promote products derived from this software without specific
|
||||
// prior written permission of Deusty, LLC.
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
// Disable legacy macros
|
||||
#ifndef DD_LEGACY_MACROS
|
||||
#define DD_LEGACY_MACROS 0
|
||||
#endif
|
||||
|
||||
#import "DDLog.h"
|
||||
|
||||
/**
|
||||
* This formatter can be used to chain different formatters together.
|
||||
* The log message will processed in the order of the formatters added.
|
||||
**/
|
||||
@interface DDMultiFormatter : NSObject <DDLogFormatter>
|
||||
|
||||
/**
|
||||
* Array of chained formatters
|
||||
*/
|
||||
@property (readonly) NSArray *formatters;
|
||||
|
||||
/**
|
||||
* Add a new formatter
|
||||
*/
|
||||
- (void)addFormatter:(id<DDLogFormatter>)formatter;
|
||||
|
||||
/**
|
||||
* Remove a formatter
|
||||
*/
|
||||
- (void)removeFormatter:(id<DDLogFormatter>)formatter;
|
||||
|
||||
/**
|
||||
* Remove all existing formatters
|
||||
*/
|
||||
- (void)removeAllFormatters;
|
||||
|
||||
/**
|
||||
* Check if a certain formatter is used
|
||||
*/
|
||||
- (BOOL)isFormattingWithFormatter:(id<DDLogFormatter>)formatter;
|
||||
|
||||
@end
|
||||
@ -1,36 +0,0 @@
|
||||
framework module CocoaLumberjack {
|
||||
umbrella header "CocoaLumberjack.h"
|
||||
|
||||
export *
|
||||
module * { export * }
|
||||
|
||||
textual header "DDLogMacros.h"
|
||||
|
||||
exclude header "DDLog+LOGV.h"
|
||||
exclude header "DDLegacyMacros.h"
|
||||
|
||||
explicit module DDContextFilterLogFormatter {
|
||||
header "DDContextFilterLogFormatter.h"
|
||||
export *
|
||||
}
|
||||
|
||||
explicit module DDDispatchQueueLogFormatter {
|
||||
header "DDDispatchQueueLogFormatter.h"
|
||||
export *
|
||||
}
|
||||
|
||||
explicit module DDMultiFormatter {
|
||||
header "DDMultiFormatter.h"
|
||||
export *
|
||||
}
|
||||
|
||||
explicit module DDASLLogCapture {
|
||||
header "DDASLLogCapture.h"
|
||||
export *
|
||||
}
|
||||
|
||||
explicit module DDAbstractDatabaseLogger {
|
||||
header "DDAbstractDatabaseLogger.h"
|
||||
export *
|
||||
}
|
||||
}
|
||||
2
Example/Pods/CocoaLumberjack/LICENSE.txt
generated
2
Example/Pods/CocoaLumberjack/LICENSE.txt
generated
@ -1,6 +1,6 @@
|
||||
Software License Agreement (BSD License)
|
||||
|
||||
Copyright (c) 2010-2015, Deusty, LLC
|
||||
Copyright (c) 2010, Deusty, LLC
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use of this software in source and binary forms,
|
||||
|
||||
23
Example/Pods/CocoaLumberjack/Lumberjack/DDASLLogCapture.h
generated
Normal file
23
Example/Pods/CocoaLumberjack/Lumberjack/DDASLLogCapture.h
generated
Normal file
@ -0,0 +1,23 @@
|
||||
//
|
||||
// DDASLLogCapture.h
|
||||
// Lumberjack
|
||||
//
|
||||
// Created by Dario Ahdoot on 3/17/14.
|
||||
//
|
||||
//
|
||||
|
||||
#import "DDASLLogger.h"
|
||||
|
||||
@protocol DDLogger;
|
||||
|
||||
@interface DDASLLogCapture : NSObject
|
||||
|
||||
+ (void)start;
|
||||
+ (void)stop;
|
||||
|
||||
// Default log level: LOG_LEVEL_VERBOSE (i.e. capture all ASL messages).
|
||||
+ (int)captureLogLevel;
|
||||
+ (void)setCaptureLogLevel:(int)LOG_LEVEL_XXX;
|
||||
|
||||
@end
|
||||
|
||||
188
Example/Pods/CocoaLumberjack/Lumberjack/DDASLLogCapture.m
generated
Normal file
188
Example/Pods/CocoaLumberjack/Lumberjack/DDASLLogCapture.m
generated
Normal file
@ -0,0 +1,188 @@
|
||||
//
|
||||
// DDASLLogCapture.m
|
||||
// Lumberjack
|
||||
//
|
||||
// Created by Dario Ahdoot on 3/17/14.
|
||||
//
|
||||
//
|
||||
|
||||
#import "DDASLLogCapture.h"
|
||||
#import "DDLog.h"
|
||||
|
||||
#include <asl.h>
|
||||
#include <notify.h>
|
||||
#include <notify_keys.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
static BOOL _cancel = YES;
|
||||
static int _captureLogLevel = LOG_LEVEL_VERBOSE;
|
||||
|
||||
@implementation DDASLLogCapture
|
||||
|
||||
+ (void)start
|
||||
{
|
||||
// Ignore subsequent calls
|
||||
if (!_cancel)
|
||||
return;
|
||||
|
||||
_cancel = NO;
|
||||
|
||||
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void)
|
||||
{
|
||||
[DDASLLogCapture captureAslLogs];
|
||||
});
|
||||
}
|
||||
|
||||
+ (void)stop
|
||||
{
|
||||
_cancel = YES;
|
||||
}
|
||||
|
||||
+ (int)captureLogLevel
|
||||
{
|
||||
return _captureLogLevel;
|
||||
}
|
||||
|
||||
+ (void)setCaptureLogLevel:(int)LOG_LEVEL_XXX
|
||||
{
|
||||
_captureLogLevel = LOG_LEVEL_XXX;
|
||||
}
|
||||
|
||||
# pragma mark - Private methods
|
||||
|
||||
+ (void)configureAslQuery:(aslmsg)query
|
||||
{
|
||||
const char param[] = "7"; // ASL_LEVEL_DEBUG, which is everything. We'll rely on regular DDlog log level to filter
|
||||
asl_set_query(query, ASL_KEY_LEVEL, param, ASL_QUERY_OP_LESS_EQUAL | ASL_QUERY_OP_NUMERIC);
|
||||
|
||||
#if !TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR
|
||||
int processId = [[NSProcessInfo processInfo] processIdentifier];
|
||||
char pid[16];
|
||||
sprintf(pid, "%d", processId);
|
||||
asl_set_query(query, ASL_KEY_PID, pid, ASL_QUERY_OP_EQUAL | ASL_QUERY_OP_NUMERIC);
|
||||
#endif
|
||||
}
|
||||
|
||||
+ (void)aslMessageRecieved:(aslmsg)msg
|
||||
{
|
||||
// NSString * sender = [NSString stringWithCString:asl_get(msg, ASL_KEY_SENDER) encoding:NSUTF8StringEncoding];
|
||||
NSString * message = [NSString stringWithCString:asl_get(msg, ASL_KEY_MSG) encoding:NSUTF8StringEncoding];
|
||||
NSString * level = [NSString stringWithCString:asl_get(msg, ASL_KEY_LEVEL) encoding:NSUTF8StringEncoding];
|
||||
NSString * secondsStr = [NSString stringWithCString:asl_get(msg, ASL_KEY_TIME) encoding:NSUTF8StringEncoding];
|
||||
NSString * nanoStr = [NSString stringWithCString:asl_get(msg, ASL_KEY_TIME_NSEC) encoding:NSUTF8StringEncoding];
|
||||
|
||||
NSTimeInterval seconds = [secondsStr doubleValue];
|
||||
NSTimeInterval nanoSeconds = [nanoStr doubleValue];
|
||||
NSTimeInterval totalSeconds = seconds + (nanoSeconds / 1e9);
|
||||
|
||||
NSDate * timeStamp = [NSDate dateWithTimeIntervalSince1970:totalSeconds];
|
||||
|
||||
int flag;
|
||||
BOOL async;
|
||||
switch([level intValue])
|
||||
{
|
||||
// By default all NSLog's with a ASL_LEVEL_WARNING level
|
||||
case ASL_LEVEL_EMERG :
|
||||
case ASL_LEVEL_ALERT :
|
||||
case ASL_LEVEL_CRIT : flag = LOG_FLAG_ERROR; async = LOG_ASYNC_ERROR; break;
|
||||
case ASL_LEVEL_ERR : flag = LOG_FLAG_WARN; async = LOG_ASYNC_WARN; break;
|
||||
case ASL_LEVEL_WARNING : flag = LOG_FLAG_INFO; async = LOG_ASYNC_INFO; break;
|
||||
case ASL_LEVEL_NOTICE : flag = LOG_FLAG_DEBUG; async = LOG_ASYNC_DEBUG; break;
|
||||
case ASL_LEVEL_INFO :
|
||||
case ASL_LEVEL_DEBUG :
|
||||
default : flag = LOG_FLAG_VERBOSE; async = LOG_ASYNC_VERBOSE; break;
|
||||
}
|
||||
|
||||
if (!(_captureLogLevel & flag))
|
||||
return;
|
||||
|
||||
DDLogMessage * logMessage = [[DDLogMessage alloc]initWithLogMsg:message
|
||||
level:_captureLogLevel
|
||||
flag:flag
|
||||
context:0
|
||||
file:"DDASLLogCapture"
|
||||
function:0
|
||||
line:0
|
||||
tag:nil
|
||||
options:0
|
||||
timestamp:timeStamp];
|
||||
|
||||
[DDLog log:async message:logMessage];
|
||||
}
|
||||
|
||||
+ (void)captureAslLogs
|
||||
{
|
||||
@autoreleasepool
|
||||
{
|
||||
/*
|
||||
We use ASL_KEY_MSG_ID to see each message once, but there's no
|
||||
obvious way to get the "next" ID. To bootstrap the process, we'll
|
||||
search by timestamp until we've seen a message.
|
||||
*/
|
||||
|
||||
struct timeval timeval = { .tv_sec = 0 };
|
||||
gettimeofday(&timeval, NULL);
|
||||
unsigned long long startTime = timeval.tv_sec;
|
||||
__block unsigned long long lastSeenID = 0;
|
||||
|
||||
/*
|
||||
syslogd posts kNotifyASLDBUpdate (com.apple.system.logger.message)
|
||||
through the notify API when it saves messages to the ASL database.
|
||||
There is some coalescing - currently it is sent at most twice per
|
||||
second - but there is no documented guarantee about this. In any
|
||||
case, there may be multiple messages per notification.
|
||||
|
||||
Notify notifications don't carry any payload, so we need to search
|
||||
for the messages.
|
||||
*/
|
||||
int notifyToken = 0; // Can be used to unregister with notify_cancel().
|
||||
notify_register_dispatch(kNotifyASLDBUpdate, ¬ifyToken, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^(int token)
|
||||
{
|
||||
// At least one message has been posted; build a search query.
|
||||
@autoreleasepool
|
||||
{
|
||||
aslmsg query = asl_new(ASL_TYPE_QUERY);
|
||||
char stringValue[64];
|
||||
if (lastSeenID > 0)
|
||||
{
|
||||
snprintf(stringValue, sizeof stringValue, "%llu", lastSeenID);
|
||||
asl_set_query(query, ASL_KEY_MSG_ID, stringValue, ASL_QUERY_OP_GREATER | ASL_QUERY_OP_NUMERIC);
|
||||
}
|
||||
else
|
||||
{
|
||||
snprintf(stringValue, sizeof stringValue, "%llu", startTime);
|
||||
asl_set_query(query, ASL_KEY_TIME, stringValue, ASL_QUERY_OP_GREATER_EQUAL | ASL_QUERY_OP_NUMERIC);
|
||||
}
|
||||
[DDASLLogCapture configureAslQuery:query];
|
||||
|
||||
// Iterate over new messages.
|
||||
aslmsg msg;
|
||||
aslresponse response = asl_search(NULL, query);
|
||||
#if defined(__IPHONE_8_0) || defined(__MAC_10_10)
|
||||
while ((msg = asl_next(response)))
|
||||
#else
|
||||
while ((msg = aslresponse_next(response)))
|
||||
#endif
|
||||
{
|
||||
[DDASLLogCapture aslMessageRecieved:msg];
|
||||
|
||||
// Keep track of which messages we've seen.
|
||||
lastSeenID = atoll(asl_get(msg, ASL_KEY_MSG_ID));
|
||||
}
|
||||
#if defined(__IPHONE_8_0) || defined(__MAC_10_10)
|
||||
asl_release(response);
|
||||
#else
|
||||
aslresponse_free(response);
|
||||
#endif
|
||||
if(_cancel)
|
||||
{
|
||||
notify_cancel(notifyToken);
|
||||
return;
|
||||
}
|
||||
free(query);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
37
Example/Pods/CocoaLumberjack/Lumberjack/DDASLLogger.h
generated
Executable file
37
Example/Pods/CocoaLumberjack/Lumberjack/DDASLLogger.h
generated
Executable file
@ -0,0 +1,37 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "DDLog.h"
|
||||
|
||||
/**
|
||||
* Welcome to Cocoa Lumberjack!
|
||||
*
|
||||
* The project page has a wealth of documentation if you have any questions.
|
||||
* https://github.com/CocoaLumberjack/CocoaLumberjack
|
||||
*
|
||||
* If you're new to the project you may wish to read the "Getting Started" wiki.
|
||||
* https://github.com/CocoaLumberjack/CocoaLumberjack/wiki/GettingStarted
|
||||
*
|
||||
*
|
||||
* This class provides a logger for the Apple System Log facility.
|
||||
*
|
||||
* As described in the "Getting Started" page,
|
||||
* the traditional NSLog() function directs it's output to two places:
|
||||
*
|
||||
* - Apple System Log
|
||||
* - StdErr (if stderr is a TTY) so log statements show up in Xcode console
|
||||
*
|
||||
* To duplicate NSLog() functionality you can simply add this logger and a tty logger.
|
||||
* However, if you instead choose to use file logging (for faster performance),
|
||||
* you may choose to use a file logger and a tty logger.
|
||||
**/
|
||||
|
||||
@interface DDASLLogger : DDAbstractLogger <DDLogger>
|
||||
|
||||
+ (instancetype)sharedInstance;
|
||||
|
||||
// Inherited from DDAbstractLogger
|
||||
|
||||
// - (id <DDLogFormatter>)logFormatter;
|
||||
// - (void)setLogFormatter:(id <DDLogFormatter>)formatter;
|
||||
|
||||
@end
|
||||
95
Example/Pods/CocoaLumberjack/Lumberjack/DDASLLogger.m
generated
Executable file
95
Example/Pods/CocoaLumberjack/Lumberjack/DDASLLogger.m
generated
Executable file
@ -0,0 +1,95 @@
|
||||
#import "DDASLLogger.h"
|
||||
#import <asl.h>
|
||||
#import <libkern/OSAtomic.h>
|
||||
|
||||
/**
|
||||
* Welcome to Cocoa Lumberjack!
|
||||
*
|
||||
* The project page has a wealth of documentation if you have any questions.
|
||||
* https://github.com/CocoaLumberjack/CocoaLumberjack
|
||||
*
|
||||
* If you're new to the project you may wish to read the "Getting Started" wiki.
|
||||
* https://github.com/CocoaLumberjack/CocoaLumberjack/wiki/GettingStarted
|
||||
**/
|
||||
|
||||
#if ! __has_feature(objc_arc)
|
||||
#error This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC).
|
||||
#endif
|
||||
|
||||
static DDASLLogger *sharedInstance;
|
||||
|
||||
@implementation DDASLLogger
|
||||
{
|
||||
aslclient client;
|
||||
}
|
||||
|
||||
+ (instancetype)sharedInstance
|
||||
{
|
||||
static dispatch_once_t DDASLLoggerOnceToken;
|
||||
dispatch_once(&DDASLLoggerOnceToken, ^{
|
||||
sharedInstance = [[[self class] alloc] init];
|
||||
});
|
||||
|
||||
return sharedInstance;
|
||||
}
|
||||
|
||||
- (id)init
|
||||
{
|
||||
if (sharedInstance != nil)
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
if ((self = [super init]))
|
||||
{
|
||||
// A default asl client is provided for the main thread,
|
||||
// but background threads need to create their own client.
|
||||
|
||||
client = asl_open(NULL, "com.apple.console", 0);
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)logMessage:(DDLogMessage *)logMessage
|
||||
{
|
||||
// Skip captured log messages.
|
||||
if (strcmp(logMessage->file, "DDASLLogCapture") == 0)
|
||||
return;
|
||||
|
||||
NSString *logMsg = logMessage->logMsg;
|
||||
|
||||
if (formatter)
|
||||
{
|
||||
logMsg = [formatter formatLogMessage:logMessage];
|
||||
}
|
||||
|
||||
if (logMsg)
|
||||
{
|
||||
const char *msg = [logMsg UTF8String];
|
||||
|
||||
int aslLogLevel;
|
||||
switch (logMessage->logFlag)
|
||||
{
|
||||
// Note: By default ASL will filter anything above level 5 (Notice).
|
||||
// So our mappings shouldn't go above that level.
|
||||
case LOG_FLAG_ERROR : aslLogLevel = ASL_LEVEL_CRIT; break;
|
||||
case LOG_FLAG_WARN : aslLogLevel = ASL_LEVEL_ERR; break;
|
||||
case LOG_FLAG_INFO : aslLogLevel = ASL_LEVEL_WARNING; break; // Regular NSLog's level
|
||||
case LOG_FLAG_DEBUG :
|
||||
case LOG_FLAG_VERBOSE :
|
||||
default : aslLogLevel = ASL_LEVEL_NOTICE; break;
|
||||
}
|
||||
|
||||
aslmsg m = asl_new(ASL_TYPE_MSG);
|
||||
asl_set(m, ASL_KEY_READ_UID, "501");
|
||||
asl_log(client, m, aslLogLevel, "%s", msg);
|
||||
asl_free(m);
|
||||
}
|
||||
}
|
||||
|
||||
- (NSString *)loggerName
|
||||
{
|
||||
return @"cocoa.lumberjack.aslLogger";
|
||||
}
|
||||
|
||||
@end
|
||||
@ -1,123 +1,102 @@
|
||||
// Software License Agreement (BSD License)
|
||||
//
|
||||
// Copyright (c) 2010-2015, Deusty, LLC
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use of this software in source and binary forms,
|
||||
// with or without modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Neither the name of Deusty nor the names of its contributors may be used
|
||||
// to endorse or promote products derived from this software without specific
|
||||
// prior written permission of Deusty, LLC.
|
||||
|
||||
// Disable legacy macros
|
||||
#ifndef DD_LEGACY_MACROS
|
||||
#define DD_LEGACY_MACROS 0
|
||||
#endif
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "DDLog.h"
|
||||
|
||||
/**
|
||||
* Welcome to Cocoa Lumberjack!
|
||||
*
|
||||
* The project page has a wealth of documentation if you have any questions.
|
||||
* https://github.com/CocoaLumberjack/CocoaLumberjack
|
||||
*
|
||||
* If you're new to the project you may wish to read the "Getting Started" wiki.
|
||||
* https://github.com/CocoaLumberjack/CocoaLumberjack/wiki/GettingStarted
|
||||
*
|
||||
*
|
||||
* This class provides an abstract implementation of a database logger.
|
||||
*
|
||||
*
|
||||
* That is, it provides the base implementation for a database logger to build atop of.
|
||||
* All that is needed for a concrete database logger is to extend this class
|
||||
* and override the methods in the implementation file that are prefixed with "db_".
|
||||
**/
|
||||
**/
|
||||
|
||||
@interface DDAbstractDatabaseLogger : DDAbstractLogger {
|
||||
|
||||
@protected
|
||||
NSUInteger _saveThreshold;
|
||||
NSTimeInterval _saveInterval;
|
||||
NSTimeInterval _maxAge;
|
||||
NSTimeInterval _deleteInterval;
|
||||
BOOL _deleteOnEverySave;
|
||||
NSUInteger saveThreshold;
|
||||
NSTimeInterval saveInterval;
|
||||
NSTimeInterval maxAge;
|
||||
NSTimeInterval deleteInterval;
|
||||
BOOL deleteOnEverySave;
|
||||
|
||||
BOOL _saveTimerSuspended;
|
||||
NSUInteger _unsavedCount;
|
||||
dispatch_time_t _unsavedTime;
|
||||
dispatch_source_t _saveTimer;
|
||||
dispatch_time_t _lastDeleteTime;
|
||||
dispatch_source_t _deleteTimer;
|
||||
BOOL saveTimerSuspended;
|
||||
NSUInteger unsavedCount;
|
||||
dispatch_time_t unsavedTime;
|
||||
dispatch_source_t saveTimer;
|
||||
dispatch_time_t lastDeleteTime;
|
||||
dispatch_source_t deleteTimer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies how often to save the data to disk.
|
||||
* Since saving is an expensive operation (disk io) it is not done after every log statement.
|
||||
* These properties allow you to configure how/when the logger saves to disk.
|
||||
*
|
||||
*
|
||||
* A save is done when either (whichever happens first):
|
||||
*
|
||||
*
|
||||
* - The number of unsaved log entries reaches saveThreshold
|
||||
* - The amount of time since the oldest unsaved log entry was created reaches saveInterval
|
||||
*
|
||||
*
|
||||
* You can optionally disable the saveThreshold by setting it to zero.
|
||||
* If you disable the saveThreshold you are entirely dependent on the saveInterval.
|
||||
*
|
||||
*
|
||||
* You can optionally disable the saveInterval by setting it to zero (or a negative value).
|
||||
* If you disable the saveInterval you are entirely dependent on the saveThreshold.
|
||||
*
|
||||
*
|
||||
* It's not wise to disable both saveThreshold and saveInterval.
|
||||
*
|
||||
*
|
||||
* The default saveThreshold is 500.
|
||||
* The default saveInterval is 60 seconds.
|
||||
**/
|
||||
**/
|
||||
@property (assign, readwrite) NSUInteger saveThreshold;
|
||||
|
||||
/**
|
||||
* See the description for the `saveThreshold` property
|
||||
*/
|
||||
@property (assign, readwrite) NSTimeInterval saveInterval;
|
||||
|
||||
/**
|
||||
* It is likely you don't want the log entries to persist forever.
|
||||
* Doing so would allow the database to grow infinitely large over time.
|
||||
*
|
||||
*
|
||||
* The maxAge property provides a way to specify how old a log statement can get
|
||||
* before it should get deleted from the database.
|
||||
*
|
||||
*
|
||||
* The deleteInterval specifies how often to sweep for old log entries.
|
||||
* Since deleting is an expensive operation (disk io) is is done on a fixed interval.
|
||||
*
|
||||
*
|
||||
* An alternative to the deleteInterval is the deleteOnEverySave option.
|
||||
* This specifies that old log entries should be deleted during every save operation.
|
||||
*
|
||||
*
|
||||
* You can optionally disable the maxAge by setting it to zero (or a negative value).
|
||||
* If you disable the maxAge then old log statements are not deleted.
|
||||
*
|
||||
*
|
||||
* You can optionally disable the deleteInterval by setting it to zero (or a negative value).
|
||||
*
|
||||
*
|
||||
* If you disable both deleteInterval and deleteOnEverySave then old log statements are not deleted.
|
||||
*
|
||||
*
|
||||
* It's not wise to enable both deleteInterval and deleteOnEverySave.
|
||||
*
|
||||
*
|
||||
* The default maxAge is 7 days.
|
||||
* The default deleteInterval is 5 minutes.
|
||||
* The default deleteOnEverySave is NO.
|
||||
**/
|
||||
**/
|
||||
@property (assign, readwrite) NSTimeInterval maxAge;
|
||||
|
||||
/**
|
||||
* See the description for the `maxAge` property
|
||||
*/
|
||||
@property (assign, readwrite) NSTimeInterval deleteInterval;
|
||||
|
||||
/**
|
||||
* See the description for the `maxAge` property
|
||||
*/
|
||||
@property (assign, readwrite) BOOL deleteOnEverySave;
|
||||
|
||||
/**
|
||||
* Forces a save of any pending log entries (flushes log entries to disk).
|
||||
**/
|
||||
**/
|
||||
- (void)savePendingLogEntries;
|
||||
|
||||
/**
|
||||
* Removes any log entries that are older than maxAge.
|
||||
**/
|
||||
**/
|
||||
- (void)deleteOldLogEntries;
|
||||
|
||||
@end
|
||||
727
Example/Pods/CocoaLumberjack/Lumberjack/DDAbstractDatabaseLogger.m
generated
Normal file
727
Example/Pods/CocoaLumberjack/Lumberjack/DDAbstractDatabaseLogger.m
generated
Normal file
@ -0,0 +1,727 @@
|
||||
#import "DDAbstractDatabaseLogger.h"
|
||||
#import <math.h>
|
||||
|
||||
/**
|
||||
* Welcome to Cocoa Lumberjack!
|
||||
*
|
||||
* The project page has a wealth of documentation if you have any questions.
|
||||
* https://github.com/CocoaLumberjack/CocoaLumberjack
|
||||
*
|
||||
* If you're new to the project you may wish to read the "Getting Started" wiki.
|
||||
* https://github.com/CocoaLumberjack/CocoaLumberjack/wiki/GettingStarted
|
||||
**/
|
||||
|
||||
#if ! __has_feature(objc_arc)
|
||||
#error This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC).
|
||||
#endif
|
||||
|
||||
@interface DDAbstractDatabaseLogger ()
|
||||
- (void)destroySaveTimer;
|
||||
- (void)destroyDeleteTimer;
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@implementation DDAbstractDatabaseLogger
|
||||
|
||||
- (id)init
|
||||
{
|
||||
if ((self = [super init]))
|
||||
{
|
||||
saveThreshold = 500;
|
||||
saveInterval = 60; // 60 seconds
|
||||
maxAge = (60 * 60 * 24 * 7); // 7 days
|
||||
deleteInterval = (60 * 5); // 5 minutes
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[self destroySaveTimer];
|
||||
[self destroyDeleteTimer];
|
||||
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark Override Me
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
- (BOOL)db_log:(DDLogMessage *)logMessage
|
||||
{
|
||||
// Override me and add your implementation.
|
||||
//
|
||||
// Return YES if an item was added to the buffer.
|
||||
// Return NO if the logMessage was ignored.
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (void)db_save
|
||||
{
|
||||
// Override me and add your implementation.
|
||||
}
|
||||
|
||||
- (void)db_delete
|
||||
{
|
||||
// Override me and add your implementation.
|
||||
}
|
||||
|
||||
- (void)db_saveAndDelete
|
||||
{
|
||||
// Override me and add your implementation.
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark Private API
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
- (void)performSaveAndSuspendSaveTimer
|
||||
{
|
||||
if (unsavedCount > 0)
|
||||
{
|
||||
if (deleteOnEverySave)
|
||||
[self db_saveAndDelete];
|
||||
else
|
||||
[self db_save];
|
||||
}
|
||||
|
||||
unsavedCount = 0;
|
||||
unsavedTime = 0;
|
||||
|
||||
if (saveTimer && !saveTimerSuspended)
|
||||
{
|
||||
dispatch_suspend(saveTimer);
|
||||
saveTimerSuspended = YES;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)performDelete
|
||||
{
|
||||
if (maxAge > 0.0)
|
||||
{
|
||||
[self db_delete];
|
||||
|
||||
lastDeleteTime = dispatch_time(DISPATCH_TIME_NOW, 0);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark Timers
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
- (void)destroySaveTimer
|
||||
{
|
||||
if (saveTimer)
|
||||
{
|
||||
dispatch_source_cancel(saveTimer);
|
||||
if (saveTimerSuspended)
|
||||
{
|
||||
// Must resume a timer before releasing it (or it will crash)
|
||||
dispatch_resume(saveTimer);
|
||||
saveTimerSuspended = NO;
|
||||
}
|
||||
#if !OS_OBJECT_USE_OBJC
|
||||
dispatch_release(saveTimer);
|
||||
#endif
|
||||
saveTimer = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)updateAndResumeSaveTimer
|
||||
{
|
||||
if ((saveTimer != NULL) && (saveInterval > 0.0) && (unsavedTime > 0.0))
|
||||
{
|
||||
uint64_t interval = (uint64_t)(saveInterval * NSEC_PER_SEC);
|
||||
dispatch_time_t startTime = dispatch_time(unsavedTime, interval);
|
||||
|
||||
dispatch_source_set_timer(saveTimer, startTime, interval, 1.0);
|
||||
|
||||
if (saveTimerSuspended)
|
||||
{
|
||||
dispatch_resume(saveTimer);
|
||||
saveTimerSuspended = NO;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)createSuspendedSaveTimer
|
||||
{
|
||||
if ((saveTimer == NULL) && (saveInterval > 0.0))
|
||||
{
|
||||
saveTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, loggerQueue);
|
||||
|
||||
dispatch_source_set_event_handler(saveTimer, ^{ @autoreleasepool {
|
||||
|
||||
[self performSaveAndSuspendSaveTimer];
|
||||
|
||||
}});
|
||||
|
||||
saveTimerSuspended = YES;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)destroyDeleteTimer
|
||||
{
|
||||
if (deleteTimer)
|
||||
{
|
||||
dispatch_source_cancel(deleteTimer);
|
||||
#if !OS_OBJECT_USE_OBJC
|
||||
dispatch_release(deleteTimer);
|
||||
#endif
|
||||
deleteTimer = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)updateDeleteTimer
|
||||
{
|
||||
if ((deleteTimer != NULL) && (deleteInterval > 0.0) && (maxAge > 0.0))
|
||||
{
|
||||
uint64_t interval = (uint64_t)(deleteInterval * NSEC_PER_SEC);
|
||||
dispatch_time_t startTime;
|
||||
|
||||
if (lastDeleteTime > 0)
|
||||
startTime = dispatch_time(lastDeleteTime, interval);
|
||||
else
|
||||
startTime = dispatch_time(DISPATCH_TIME_NOW, interval);
|
||||
|
||||
dispatch_source_set_timer(deleteTimer, startTime, interval, 1.0);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)createAndStartDeleteTimer
|
||||
{
|
||||
if ((deleteTimer == NULL) && (deleteInterval > 0.0) && (maxAge > 0.0))
|
||||
{
|
||||
deleteTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, loggerQueue);
|
||||
|
||||
if (deleteTimer != NULL) {
|
||||
dispatch_source_set_event_handler(deleteTimer, ^{ @autoreleasepool {
|
||||
|
||||
[self performDelete];
|
||||
|
||||
}});
|
||||
|
||||
[self updateDeleteTimer];
|
||||
|
||||
if (deleteTimer != NULL) dispatch_resume(deleteTimer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark Configuration
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
- (NSUInteger)saveThreshold
|
||||
{
|
||||
// The design of this method is taken from the DDAbstractLogger implementation.
|
||||
// For extensive documentation please refer to the DDAbstractLogger implementation.
|
||||
|
||||
// Note: The internal implementation MUST access the colorsEnabled variable directly,
|
||||
// This method is designed explicitly for external access.
|
||||
//
|
||||
// Using "self." syntax to go through this method will cause immediate deadlock.
|
||||
// This is the intended result. Fix it by accessing the ivar directly.
|
||||
// Great strides have been take to ensure this is safe to do. Plus it's MUCH faster.
|
||||
|
||||
NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
|
||||
NSAssert(![self isOnInternalLoggerQueue], @"MUST access ivar directly, NOT via self.* syntax.");
|
||||
|
||||
dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
|
||||
|
||||
__block NSUInteger result;
|
||||
|
||||
dispatch_sync(globalLoggingQueue, ^{
|
||||
dispatch_sync(loggerQueue, ^{
|
||||
result = saveThreshold;
|
||||
});
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
- (void)setSaveThreshold:(NSUInteger)threshold
|
||||
{
|
||||
dispatch_block_t block = ^{ @autoreleasepool {
|
||||
|
||||
if (saveThreshold != threshold)
|
||||
{
|
||||
saveThreshold = threshold;
|
||||
|
||||
// Since the saveThreshold has changed,
|
||||
// we check to see if the current unsavedCount has surpassed the new threshold.
|
||||
//
|
||||
// If it has, we immediately save the log.
|
||||
|
||||
if ((unsavedCount >= saveThreshold) && (saveThreshold > 0))
|
||||
{
|
||||
[self performSaveAndSuspendSaveTimer];
|
||||
}
|
||||
}
|
||||
}};
|
||||
|
||||
// The design of the setter logic below is taken from the DDAbstractLogger implementation.
|
||||
// For documentation please refer to the DDAbstractLogger implementation.
|
||||
|
||||
if ([self isOnInternalLoggerQueue])
|
||||
{
|
||||
block();
|
||||
}
|
||||
else
|
||||
{
|
||||
dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
|
||||
NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
|
||||
|
||||
dispatch_async(globalLoggingQueue, ^{
|
||||
dispatch_async(loggerQueue, block);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
- (NSTimeInterval)saveInterval
|
||||
{
|
||||
// The design of this method is taken from the DDAbstractLogger implementation.
|
||||
// For extensive documentation please refer to the DDAbstractLogger implementation.
|
||||
|
||||
// Note: The internal implementation MUST access the colorsEnabled variable directly,
|
||||
// This method is designed explicitly for external access.
|
||||
//
|
||||
// Using "self." syntax to go through this method will cause immediate deadlock.
|
||||
// This is the intended result. Fix it by accessing the ivar directly.
|
||||
// Great strides have been take to ensure this is safe to do. Plus it's MUCH faster.
|
||||
|
||||
NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
|
||||
NSAssert(![self isOnInternalLoggerQueue], @"MUST access ivar directly, NOT via self.* syntax.");
|
||||
|
||||
dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
|
||||
|
||||
__block NSTimeInterval result;
|
||||
|
||||
dispatch_sync(globalLoggingQueue, ^{
|
||||
dispatch_sync(loggerQueue, ^{
|
||||
result = saveInterval;
|
||||
});
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
- (void)setSaveInterval:(NSTimeInterval)interval
|
||||
{
|
||||
dispatch_block_t block = ^{ @autoreleasepool {
|
||||
|
||||
// C99 recommended floating point comparison macro
|
||||
// Read: isLessThanOrGreaterThan(floatA, floatB)
|
||||
|
||||
if (/* saveInterval != interval */ islessgreater(saveInterval, interval))
|
||||
{
|
||||
saveInterval = interval;
|
||||
|
||||
// There are several cases we need to handle here.
|
||||
//
|
||||
// 1. If the saveInterval was previously enabled and it just got disabled,
|
||||
// then we need to stop the saveTimer. (And we might as well release it.)
|
||||
//
|
||||
// 2. If the saveInterval was previously disabled and it just got enabled,
|
||||
// then we need to setup the saveTimer. (Plus we might need to do an immediate save.)
|
||||
//
|
||||
// 3. If the saveInterval increased, then we need to reset the timer so that it fires at the later date.
|
||||
//
|
||||
// 4. If the saveInterval decreased, then we need to reset the timer so that it fires at an earlier date.
|
||||
// (Plus we might need to do an immediate save.)
|
||||
|
||||
if (saveInterval > 0.0)
|
||||
{
|
||||
if (saveTimer == NULL)
|
||||
{
|
||||
// Handles #2
|
||||
//
|
||||
// Since the saveTimer uses the unsavedTime to calculate it's first fireDate,
|
||||
// if a save is needed the timer will fire immediately.
|
||||
|
||||
[self createSuspendedSaveTimer];
|
||||
[self updateAndResumeSaveTimer];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Handles #3
|
||||
// Handles #4
|
||||
//
|
||||
// Since the saveTimer uses the unsavedTime to calculate it's first fireDate,
|
||||
// if a save is needed the timer will fire immediately.
|
||||
|
||||
[self updateAndResumeSaveTimer];
|
||||
}
|
||||
}
|
||||
else if (saveTimer)
|
||||
{
|
||||
// Handles #1
|
||||
|
||||
[self destroySaveTimer];
|
||||
}
|
||||
}
|
||||
}};
|
||||
|
||||
// The design of the setter logic below is taken from the DDAbstractLogger implementation.
|
||||
// For documentation please refer to the DDAbstractLogger implementation.
|
||||
|
||||
if ([self isOnInternalLoggerQueue])
|
||||
{
|
||||
block();
|
||||
}
|
||||
else
|
||||
{
|
||||
dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
|
||||
NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
|
||||
|
||||
dispatch_async(globalLoggingQueue, ^{
|
||||
dispatch_async(loggerQueue, block);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
- (NSTimeInterval)maxAge
|
||||
{
|
||||
// The design of this method is taken from the DDAbstractLogger implementation.
|
||||
// For extensive documentation please refer to the DDAbstractLogger implementation.
|
||||
|
||||
// Note: The internal implementation MUST access the colorsEnabled variable directly,
|
||||
// This method is designed explicitly for external access.
|
||||
//
|
||||
// Using "self." syntax to go through this method will cause immediate deadlock.
|
||||
// This is the intended result. Fix it by accessing the ivar directly.
|
||||
// Great strides have been take to ensure this is safe to do. Plus it's MUCH faster.
|
||||
|
||||
NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
|
||||
NSAssert(![self isOnInternalLoggerQueue], @"MUST access ivar directly, NOT via self.* syntax.");
|
||||
|
||||
dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
|
||||
|
||||
__block NSTimeInterval result;
|
||||
|
||||
dispatch_sync(globalLoggingQueue, ^{
|
||||
dispatch_sync(loggerQueue, ^{
|
||||
result = maxAge;
|
||||
});
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
- (void)setMaxAge:(NSTimeInterval)interval
|
||||
{
|
||||
dispatch_block_t block = ^{ @autoreleasepool {
|
||||
|
||||
// C99 recommended floating point comparison macro
|
||||
// Read: isLessThanOrGreaterThan(floatA, floatB)
|
||||
|
||||
if (/* maxAge != interval */ islessgreater(maxAge, interval))
|
||||
{
|
||||
NSTimeInterval oldMaxAge = maxAge;
|
||||
NSTimeInterval newMaxAge = interval;
|
||||
|
||||
maxAge = interval;
|
||||
|
||||
// There are several cases we need to handle here.
|
||||
//
|
||||
// 1. If the maxAge was previously enabled and it just got disabled,
|
||||
// then we need to stop the deleteTimer. (And we might as well release it.)
|
||||
//
|
||||
// 2. If the maxAge was previously disabled and it just got enabled,
|
||||
// then we need to setup the deleteTimer. (Plus we might need to do an immediate delete.)
|
||||
//
|
||||
// 3. If the maxAge was increased,
|
||||
// then we don't need to do anything.
|
||||
//
|
||||
// 4. If the maxAge was decreased,
|
||||
// then we should do an immediate delete.
|
||||
|
||||
BOOL shouldDeleteNow = NO;
|
||||
|
||||
if (oldMaxAge > 0.0)
|
||||
{
|
||||
if (newMaxAge <= 0.0)
|
||||
{
|
||||
// Handles #1
|
||||
|
||||
[self destroyDeleteTimer];
|
||||
}
|
||||
else if (oldMaxAge > newMaxAge)
|
||||
{
|
||||
// Handles #4
|
||||
shouldDeleteNow = YES;
|
||||
}
|
||||
}
|
||||
else if (newMaxAge > 0.0)
|
||||
{
|
||||
// Handles #2
|
||||
shouldDeleteNow = YES;
|
||||
}
|
||||
|
||||
if (shouldDeleteNow)
|
||||
{
|
||||
[self performDelete];
|
||||
|
||||
if (deleteTimer)
|
||||
[self updateDeleteTimer];
|
||||
else
|
||||
[self createAndStartDeleteTimer];
|
||||
}
|
||||
}
|
||||
}};
|
||||
|
||||
// The design of the setter logic below is taken from the DDAbstractLogger implementation.
|
||||
// For documentation please refer to the DDAbstractLogger implementation.
|
||||
|
||||
if ([self isOnInternalLoggerQueue])
|
||||
{
|
||||
block();
|
||||
}
|
||||
else
|
||||
{
|
||||
dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
|
||||
NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
|
||||
|
||||
dispatch_async(globalLoggingQueue, ^{
|
||||
dispatch_async(loggerQueue, block);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
- (NSTimeInterval)deleteInterval
|
||||
{
|
||||
// The design of this method is taken from the DDAbstractLogger implementation.
|
||||
// For extensive documentation please refer to the DDAbstractLogger implementation.
|
||||
|
||||
// Note: The internal implementation MUST access the colorsEnabled variable directly,
|
||||
// This method is designed explicitly for external access.
|
||||
//
|
||||
// Using "self." syntax to go through this method will cause immediate deadlock.
|
||||
// This is the intended result. Fix it by accessing the ivar directly.
|
||||
// Great strides have been take to ensure this is safe to do. Plus it's MUCH faster.
|
||||
|
||||
NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
|
||||
NSAssert(![self isOnInternalLoggerQueue], @"MUST access ivar directly, NOT via self.* syntax.");
|
||||
|
||||
dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
|
||||
|
||||
__block NSTimeInterval result;
|
||||
|
||||
dispatch_sync(globalLoggingQueue, ^{
|
||||
dispatch_sync(loggerQueue, ^{
|
||||
result = deleteInterval;
|
||||
});
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
- (void)setDeleteInterval:(NSTimeInterval)interval
|
||||
{
|
||||
dispatch_block_t block = ^{ @autoreleasepool {
|
||||
|
||||
// C99 recommended floating point comparison macro
|
||||
// Read: isLessThanOrGreaterThan(floatA, floatB)
|
||||
|
||||
if (/* deleteInterval != interval */ islessgreater(deleteInterval, interval))
|
||||
{
|
||||
deleteInterval = interval;
|
||||
|
||||
// There are several cases we need to handle here.
|
||||
//
|
||||
// 1. If the deleteInterval was previously enabled and it just got disabled,
|
||||
// then we need to stop the deleteTimer. (And we might as well release it.)
|
||||
//
|
||||
// 2. If the deleteInterval was previously disabled and it just got enabled,
|
||||
// then we need to setup the deleteTimer. (Plus we might need to do an immediate delete.)
|
||||
//
|
||||
// 3. If the deleteInterval increased, then we need to reset the timer so that it fires at the later date.
|
||||
//
|
||||
// 4. If the deleteInterval decreased, then we need to reset the timer so that it fires at an earlier date.
|
||||
// (Plus we might need to do an immediate delete.)
|
||||
|
||||
if (deleteInterval > 0.0)
|
||||
{
|
||||
if (deleteTimer == NULL)
|
||||
{
|
||||
// Handles #2
|
||||
//
|
||||
// Since the deleteTimer uses the lastDeleteTime to calculate it's first fireDate,
|
||||
// if a delete is needed the timer will fire immediately.
|
||||
|
||||
[self createAndStartDeleteTimer];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Handles #3
|
||||
// Handles #4
|
||||
//
|
||||
// Since the deleteTimer uses the lastDeleteTime to calculate it's first fireDate,
|
||||
// if a save is needed the timer will fire immediately.
|
||||
|
||||
[self updateDeleteTimer];
|
||||
}
|
||||
}
|
||||
else if (deleteTimer)
|
||||
{
|
||||
// Handles #1
|
||||
|
||||
[self destroyDeleteTimer];
|
||||
}
|
||||
}
|
||||
}};
|
||||
|
||||
// The design of the setter logic below is taken from the DDAbstractLogger implementation.
|
||||
// For documentation please refer to the DDAbstractLogger implementation.
|
||||
|
||||
if ([self isOnInternalLoggerQueue])
|
||||
{
|
||||
block();
|
||||
}
|
||||
else
|
||||
{
|
||||
dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
|
||||
NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
|
||||
|
||||
dispatch_async(globalLoggingQueue, ^{
|
||||
dispatch_async(loggerQueue, block);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)deleteOnEverySave
|
||||
{
|
||||
// The design of this method is taken from the DDAbstractLogger implementation.
|
||||
// For extensive documentation please refer to the DDAbstractLogger implementation.
|
||||
|
||||
// Note: The internal implementation MUST access the colorsEnabled variable directly,
|
||||
// This method is designed explicitly for external access.
|
||||
//
|
||||
// Using "self." syntax to go through this method will cause immediate deadlock.
|
||||
// This is the intended result. Fix it by accessing the ivar directly.
|
||||
// Great strides have been take to ensure this is safe to do. Plus it's MUCH faster.
|
||||
|
||||
NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
|
||||
NSAssert(![self isOnInternalLoggerQueue], @"MUST access ivar directly, NOT via self.* syntax.");
|
||||
|
||||
dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
|
||||
|
||||
__block BOOL result;
|
||||
|
||||
dispatch_sync(globalLoggingQueue, ^{
|
||||
dispatch_sync(loggerQueue, ^{
|
||||
result = deleteOnEverySave;
|
||||
});
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
- (void)setDeleteOnEverySave:(BOOL)flag
|
||||
{
|
||||
dispatch_block_t block = ^{
|
||||
|
||||
deleteOnEverySave = flag;
|
||||
};
|
||||
|
||||
// The design of the setter logic below is taken from the DDAbstractLogger implementation.
|
||||
// For documentation please refer to the DDAbstractLogger implementation.
|
||||
|
||||
if ([self isOnInternalLoggerQueue])
|
||||
{
|
||||
block();
|
||||
}
|
||||
else
|
||||
{
|
||||
dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
|
||||
NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
|
||||
|
||||
dispatch_async(globalLoggingQueue, ^{
|
||||
dispatch_async(loggerQueue, block);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark Public API
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
- (void)savePendingLogEntries
|
||||
{
|
||||
dispatch_block_t block = ^{ @autoreleasepool {
|
||||
|
||||
[self performSaveAndSuspendSaveTimer];
|
||||
}};
|
||||
|
||||
if ([self isOnInternalLoggerQueue])
|
||||
block();
|
||||
else
|
||||
dispatch_async(loggerQueue, block);
|
||||
}
|
||||
|
||||
- (void)deleteOldLogEntries
|
||||
{
|
||||
dispatch_block_t block = ^{ @autoreleasepool {
|
||||
|
||||
[self performDelete];
|
||||
}};
|
||||
|
||||
if ([self isOnInternalLoggerQueue])
|
||||
block();
|
||||
else
|
||||
dispatch_async(loggerQueue, block);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark DDLogger
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
- (void)didAddLogger
|
||||
{
|
||||
// If you override me be sure to invoke [super didAddLogger];
|
||||
|
||||
[self createSuspendedSaveTimer];
|
||||
|
||||
[self createAndStartDeleteTimer];
|
||||
}
|
||||
|
||||
- (void)willRemoveLogger
|
||||
{
|
||||
// If you override me be sure to invoke [super willRemoveLogger];
|
||||
|
||||
[self performSaveAndSuspendSaveTimer];
|
||||
|
||||
[self destroySaveTimer];
|
||||
[self destroyDeleteTimer];
|
||||
}
|
||||
|
||||
- (void)logMessage:(DDLogMessage *)logMessage
|
||||
{
|
||||
if ([self db_log:logMessage])
|
||||
{
|
||||
BOOL firstUnsavedEntry = (++unsavedCount == 1);
|
||||
|
||||
if ((unsavedCount >= saveThreshold) && (saveThreshold > 0))
|
||||
{
|
||||
[self performSaveAndSuspendSaveTimer];
|
||||
}
|
||||
else if (firstUnsavedEntry)
|
||||
{
|
||||
unsavedTime = dispatch_time(DISPATCH_TIME_NOW, 0);
|
||||
[self updateAndResumeSaveTimer];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)flush
|
||||
{
|
||||
// This method is invoked by DDLog's flushLog method.
|
||||
//
|
||||
// It is called automatically when the application quits,
|
||||
// or if the developer invokes DDLog's flushLog method prior to crashing or something.
|
||||
|
||||
[self performSaveAndSuspendSaveTimer];
|
||||
}
|
||||
|
||||
@end
|
||||
16
Example/Pods/CocoaLumberjack/Lumberjack/DDAssert.h
generated
Normal file
16
Example/Pods/CocoaLumberjack/Lumberjack/DDAssert.h
generated
Normal file
@ -0,0 +1,16 @@
|
||||
//
|
||||
// DDAssert.h
|
||||
// CocoaLumberjack
|
||||
//
|
||||
// Created by Ernesto Rivera on 2014/07/07.
|
||||
//
|
||||
//
|
||||
|
||||
#import "DDLog.h"
|
||||
|
||||
#define DDAssert(condition, frmt, ...) if (!(condition)) { \
|
||||
NSString * description = [NSString stringWithFormat:frmt, ##__VA_ARGS__]; \
|
||||
DDLogError(@"%@", description); \
|
||||
NSAssert(NO, description); }
|
||||
#define DDAssertCondition(condition) DDAssert(condition, @"Condition not satisfied: %s", #condition)
|
||||
|
||||
@ -1,74 +1,63 @@
|
||||
// Software License Agreement (BSD License)
|
||||
//
|
||||
// Copyright (c) 2010-2015, Deusty, LLC
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use of this software in source and binary forms,
|
||||
// with or without modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Neither the name of Deusty nor the names of its contributors may be used
|
||||
// to endorse or promote products derived from this software without specific
|
||||
// prior written permission of Deusty, LLC.
|
||||
|
||||
// Disable legacy macros
|
||||
#ifndef DD_LEGACY_MACROS
|
||||
#define DD_LEGACY_MACROS 0
|
||||
#endif
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "DDLog.h"
|
||||
|
||||
@class DDLogFileInfo;
|
||||
|
||||
/**
|
||||
* Welcome to Cocoa Lumberjack!
|
||||
*
|
||||
* The project page has a wealth of documentation if you have any questions.
|
||||
* https://github.com/CocoaLumberjack/CocoaLumberjack
|
||||
*
|
||||
* If you're new to the project you may wish to read the "Getting Started" wiki.
|
||||
* https://github.com/CocoaLumberjack/CocoaLumberjack/wiki/GettingStarted
|
||||
*
|
||||
*
|
||||
* This class provides a logger to write log statements to a file.
|
||||
**/
|
||||
**/
|
||||
|
||||
|
||||
// Default configuration and safety/sanity values.
|
||||
//
|
||||
// maximumFileSize -> kDDDefaultLogMaxFileSize
|
||||
// rollingFrequency -> kDDDefaultLogRollingFrequency
|
||||
// maximumNumberOfLogFiles -> kDDDefaultLogMaxNumLogFiles
|
||||
// logFilesDiskQuota -> kDDDefaultLogFilesDiskQuota
|
||||
//
|
||||
//
|
||||
// maximumFileSize -> DEFAULT_LOG_MAX_FILE_SIZE
|
||||
// rollingFrequency -> DEFAULT_LOG_ROLLING_FREQUENCY
|
||||
// maximumNumberOfLogFiles -> DEFAULT_LOG_MAX_NUM_LOG_FILES
|
||||
// logFilesDiskQuota -> DEFAULT_LOG_FILES_DISK_QUOTA
|
||||
//
|
||||
// You should carefully consider the proper configuration values for your application.
|
||||
|
||||
extern unsigned long long const kDDDefaultLogMaxFileSize;
|
||||
extern NSTimeInterval const kDDDefaultLogRollingFrequency;
|
||||
extern NSUInteger const kDDDefaultLogMaxNumLogFiles;
|
||||
extern unsigned long long const kDDDefaultLogFilesDiskQuota;
|
||||
#define DEFAULT_LOG_MAX_FILE_SIZE (1024 * 1024) // 1 MB
|
||||
#define DEFAULT_LOG_ROLLING_FREQUENCY (60 * 60 * 24) // 24 Hours
|
||||
#define DEFAULT_LOG_MAX_NUM_LOG_FILES (5) // 5 Files
|
||||
#define DEFAULT_LOG_FILES_DISK_QUOTA (20 * 1024 * 1024) // 20 MB
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark -
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* The LogFileManager protocol is designed to allow you to control all aspects of your log files.
|
||||
*
|
||||
* The primary purpose of this is to allow you to do something with the log files after they have been rolled.
|
||||
* Perhaps you want to compress them to save disk space.
|
||||
* Perhaps you want to upload them to an FTP server.
|
||||
* Perhaps you want to run some analytics on the file.
|
||||
*
|
||||
* A default LogFileManager is, of course, provided.
|
||||
* The default LogFileManager simply deletes old log files according to the maximumNumberOfLogFiles property.
|
||||
*
|
||||
* This protocol provides various methods to fetch the list of log files.
|
||||
*
|
||||
* There are two variants: sorted and unsorted.
|
||||
* If sorting is not necessary, the unsorted variant is obviously faster.
|
||||
* The sorted variant will return an array sorted by when the log files were created,
|
||||
* with the most recently created log file at index 0, and the oldest log file at the end of the array.
|
||||
*
|
||||
* You can fetch only the log file paths (full path including name), log file names (name only),
|
||||
* or an array of `DDLogFileInfo` objects.
|
||||
* The `DDLogFileInfo` class is documented below, and provides a handy wrapper that
|
||||
* gives you easy access to various file attributes such as the creation date or the file size.
|
||||
*/
|
||||
// The LogFileManager protocol is designed to allow you to control all aspects of your log files.
|
||||
//
|
||||
// The primary purpose of this is to allow you to do something with the log files after they have been rolled.
|
||||
// Perhaps you want to compress them to save disk space.
|
||||
// Perhaps you want to upload them to an FTP server.
|
||||
// Perhaps you want to run some analytics on the file.
|
||||
//
|
||||
// A default LogFileManager is, of course, provided.
|
||||
// The default LogFileManager simply deletes old log files according to the maximumNumberOfLogFiles property.
|
||||
//
|
||||
// This protocol provides various methods to fetch the list of log files.
|
||||
//
|
||||
// There are two variants: sorted and unsorted.
|
||||
// If sorting is not necessary, the unsorted variant is obviously faster.
|
||||
// The sorted variant will return an array sorted by when the log files were created,
|
||||
// with the most recently created log file at index 0, and the oldest log file at the end of the array.
|
||||
//
|
||||
// You can fetch only the log file paths (full path including name), log file names (name only),
|
||||
// or an array of DDLogFileInfo objects.
|
||||
// The DDLogFileInfo class is documented below, and provides a handy wrapper that
|
||||
// gives you easy access to various file attributes such as the creation date or the file size.
|
||||
|
||||
@protocol DDLogFileManager <NSObject>
|
||||
@required
|
||||
|
||||
@ -79,9 +68,9 @@ extern unsigned long long const kDDDefaultLogFilesDiskQuota;
|
||||
* For example, if this property is set to 3,
|
||||
* then the LogFileManager will only keep 3 archived log files (plus the current active log file) on disk.
|
||||
* Once the active log file is rolled/archived, then the oldest of the existing 3 rolled/archived log files is deleted.
|
||||
*
|
||||
*
|
||||
* You may optionally disable this option by setting it to zero.
|
||||
**/
|
||||
**/
|
||||
@property (readwrite, assign, atomic) NSUInteger maximumNumberOfLogFiles;
|
||||
|
||||
/**
|
||||
@ -89,75 +78,30 @@ extern unsigned long long const kDDDefaultLogFilesDiskQuota;
|
||||
* be deleted.
|
||||
*
|
||||
* You may optionally disable this option by setting it to zero.
|
||||
**/
|
||||
**/
|
||||
@property (readwrite, assign, atomic) unsigned long long logFilesDiskQuota;
|
||||
|
||||
// Public methods
|
||||
|
||||
/**
|
||||
* Returns the logs directory (path)
|
||||
*/
|
||||
- (NSString *)logsDirectory;
|
||||
|
||||
/**
|
||||
* Returns an array of `NSString` objects,
|
||||
* each of which is the filePath to an existing log file on disk.
|
||||
**/
|
||||
- (NSArray *)unsortedLogFilePaths;
|
||||
|
||||
/**
|
||||
* Returns an array of `NSString` objects,
|
||||
* each of which is the fileName of an existing log file on disk.
|
||||
**/
|
||||
- (NSArray *)unsortedLogFileNames;
|
||||
|
||||
/**
|
||||
* Returns an array of `DDLogFileInfo` objects,
|
||||
* each representing an existing log file on disk,
|
||||
* and containing important information about the log file such as it's modification date and size.
|
||||
**/
|
||||
- (NSArray *)unsortedLogFileInfos;
|
||||
|
||||
/**
|
||||
* Just like the `unsortedLogFilePaths` method, but sorts the array.
|
||||
* The items in the array are sorted by creation date.
|
||||
* The first item in the array will be the most recently created log file.
|
||||
**/
|
||||
- (NSArray *)sortedLogFilePaths;
|
||||
|
||||
/**
|
||||
* Just like the `unsortedLogFileNames` method, but sorts the array.
|
||||
* The items in the array are sorted by creation date.
|
||||
* The first item in the array will be the most recently created log file.
|
||||
**/
|
||||
- (NSArray *)sortedLogFileNames;
|
||||
|
||||
/**
|
||||
* Just like the `unsortedLogFileInfos` method, but sorts the array.
|
||||
* The items in the array are sorted by creation date.
|
||||
* The first item in the array will be the most recently created log file.
|
||||
**/
|
||||
- (NSArray *)sortedLogFileInfos;
|
||||
|
||||
// Private methods (only to be used by DDFileLogger)
|
||||
|
||||
/**
|
||||
* Generates a new unique log file path, and creates the corresponding log file.
|
||||
**/
|
||||
- (NSString *)createNewLogFile;
|
||||
|
||||
@optional
|
||||
|
||||
// Notifications from DDFileLogger
|
||||
|
||||
/**
|
||||
* Called when a log file was archieved
|
||||
*/
|
||||
- (void)didArchiveLogFile:(NSString *)logFilePath;
|
||||
|
||||
/**
|
||||
* Called when the roll action was executed and the log was archieved
|
||||
*/
|
||||
- (void)didRollAndArchiveLogFile:(NSString *)logFilePath;
|
||||
|
||||
@end
|
||||
@ -168,29 +112,29 @@ extern unsigned long long const kDDDefaultLogFilesDiskQuota;
|
||||
|
||||
/**
|
||||
* Default log file manager.
|
||||
*
|
||||
*
|
||||
* All log files are placed inside the logsDirectory.
|
||||
* If a specific logsDirectory isn't specified, the default directory is used.
|
||||
* On Mac, this is in `~/Library/Logs/<Application Name>`.
|
||||
* On iPhone, this is in `~/Library/Caches/Logs`.
|
||||
*
|
||||
* Log files are named `"<bundle identifier> <date> <time>.log"`
|
||||
* Example: `com.organization.myapp 2013-12-03 17-14.log`
|
||||
*
|
||||
* Archived log files are automatically deleted according to the `maximumNumberOfLogFiles` property.
|
||||
**/
|
||||
* On Mac, this is in ~/Library/Logs/<Application Name>.
|
||||
* On iPhone, this is in ~/Library/Caches/Logs.
|
||||
*
|
||||
* Log files are named "<bundle identifier> <date> <time>.log"
|
||||
* Example: com.organization.myapp 2013-12-03 17-14.log
|
||||
*
|
||||
* Archived log files are automatically deleted according to the maximumNumberOfLogFiles property.
|
||||
**/
|
||||
@interface DDLogFileManagerDefault : NSObject <DDLogFileManager>
|
||||
{
|
||||
NSUInteger maximumNumberOfLogFiles;
|
||||
unsigned long long logFilesDiskQuota;
|
||||
NSString *_logsDirectory;
|
||||
#if TARGET_OS_IPHONE
|
||||
NSString* _defaultFileProtectionLevel;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Default initializer
|
||||
*/
|
||||
- (instancetype)init;
|
||||
|
||||
/**
|
||||
* Designated initialized, requires the logs directory
|
||||
*/
|
||||
- (instancetype)initWithLogsDirectory:(NSString *)logsDirectory NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
- (id)init;
|
||||
- (instancetype)initWithLogsDirectory:(NSString *)logsDirectory;
|
||||
#if TARGET_OS_IPHONE
|
||||
/*
|
||||
* Calling this constructor you can override the default "automagically" chosen NSFileProtection level.
|
||||
@ -203,67 +147,53 @@ extern unsigned long long const kDDDefaultLogFilesDiskQuota;
|
||||
* null
|
||||
* cy#
|
||||
**/
|
||||
- (instancetype)initWithLogsDirectory:(NSString *)logsDirectory defaultFileProtectionLevel:(NSString *)fileProtectionLevel;
|
||||
- (instancetype)initWithLogsDirectory:(NSString *)logsDirectory defaultFileProtectionLevel:(NSString*)fileProtectionLevel;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Methods to override.
|
||||
*
|
||||
* Log files are named `"<bundle identifier> <date> <time>.log"`
|
||||
* Example: `com.organization.myapp 2013-12-03 17-14.log`
|
||||
* Log files are named "<bundle identifier> <date> <time>.log"
|
||||
* Example: com.organization.myapp 2013-12-03 17-14.log
|
||||
*
|
||||
* If you wish to change default filename, you can override following two methods.
|
||||
* - `newLogFileName` method would be called on new logfile creation.
|
||||
* - `isLogFile:` method would be called to filter logfiles from all other files in logsDirectory.
|
||||
* - newLogFileName method would be called on new logfile creation.
|
||||
* - isLogFile: method would be called to filter logfiles from all other files in logsDirectory.
|
||||
* You have to parse given filename and return YES if it is logFile.
|
||||
*
|
||||
* **NOTE**
|
||||
* `newLogFileName` returns filename. If appropriate file already exists, number would be added
|
||||
* newLogFileName returns filename. If appropriate file already exists, number would be added
|
||||
* to filename before extension. You have to handle this case in isLogFile: method.
|
||||
*
|
||||
* Example:
|
||||
* - newLogFileName returns `"com.organization.myapp 2013-12-03.log"`,
|
||||
* file `"com.organization.myapp 2013-12-03.log"` would be created.
|
||||
* - after some time `"com.organization.myapp 2013-12-03.log"` is archived
|
||||
* - newLogFileName again returns `"com.organization.myapp 2013-12-03.log"`,
|
||||
* file `"com.organization.myapp 2013-12-03 2.log"` would be created.
|
||||
* - after some time `"com.organization.myapp 2013-12-03 1.log"` is archived
|
||||
* - newLogFileName again returns `"com.organization.myapp 2013-12-03.log"`,
|
||||
* file `"com.organization.myapp 2013-12-03 3.log"` would be created.
|
||||
**/
|
||||
|
||||
/**
|
||||
* Generates log file name with default format `"<bundle identifier> <date> <time>.log"`
|
||||
* Example: `MobileSafari 2013-12-03 17-14.log`
|
||||
*
|
||||
* You can change it by overriding `newLogFileName` and `isLogFile:` methods.
|
||||
**/
|
||||
@property (readonly, copy) NSString *newLogFileName;
|
||||
|
||||
/**
|
||||
* Default log file name is `"<bundle identifier> <date> <time>.log"`.
|
||||
* Example: `MobileSafari 2013-12-03 17-14.log`
|
||||
*
|
||||
* You can change it by overriding `newLogFileName` and `isLogFile:` methods.
|
||||
**/
|
||||
* - newLogFileName returns "com.organization.myapp 2013-12-03.log",
|
||||
* file "com.organization.myapp 2013-12-03.log" would be created.
|
||||
* - after some time "com.organization.myapp 2013-12-03.log" is archived
|
||||
* - newLogFileName again returns "com.organization.myapp 2013-12-03.log",
|
||||
* file "com.organization.myapp 2013-12-03 2.log" would be created.
|
||||
* - after some time "com.organization.myapp 2013-12-03 1.log" is archived
|
||||
* - newLogFileName again returns "com.organization.myapp 2013-12-03.log",
|
||||
* file "com.organization.myapp 2013-12-03 3.log" would be created.
|
||||
**/
|
||||
- (NSString *)newLogFileName;
|
||||
- (BOOL)isLogFile:(NSString *)fileName;
|
||||
|
||||
/* Inherited from DDLogFileManager protocol:
|
||||
|
||||
@property (readwrite, assign, atomic) NSUInteger maximumNumberOfLogFiles;
|
||||
@property (readwrite, assign, atomic) NSUInteger logFilesDiskQuota;
|
||||
@property (readwrite, assign, atomic) NSUInteger maximumNumberOfLogFiles;
|
||||
@property (readwrite, assign, atomic) NSUInteger logFilesDiskQuota;
|
||||
|
||||
- (NSString *)logsDirectory;
|
||||
- (NSString *)logsDirectory;
|
||||
|
||||
- (NSArray *)unsortedLogFilePaths;
|
||||
- (NSArray *)unsortedLogFileNames;
|
||||
- (NSArray *)unsortedLogFileInfos;
|
||||
- (NSArray *)unsortedLogFilePaths;
|
||||
- (NSArray *)unsortedLogFileNames;
|
||||
- (NSArray *)unsortedLogFileInfos;
|
||||
|
||||
- (NSArray *)sortedLogFilePaths;
|
||||
- (NSArray *)sortedLogFileNames;
|
||||
- (NSArray *)sortedLogFileInfos;
|
||||
- (NSArray *)sortedLogFilePaths;
|
||||
- (NSArray *)sortedLogFileNames;
|
||||
- (NSArray *)sortedLogFileInfos;
|
||||
|
||||
*/
|
||||
*/
|
||||
|
||||
@end
|
||||
|
||||
@ -275,23 +205,19 @@ extern unsigned long long const kDDDefaultLogFilesDiskQuota;
|
||||
* Most users will want file log messages to be prepended with the date and time.
|
||||
* Rather than forcing the majority of users to write their own formatter,
|
||||
* we will supply a logical default formatter.
|
||||
* Users can easily replace this formatter with their own by invoking the `setLogFormatter:` method.
|
||||
* It can also be removed by calling `setLogFormatter:`, and passing a nil parameter.
|
||||
*
|
||||
* Users can easily replace this formatter with their own by invoking the setLogFormatter method.
|
||||
* It can also be removed by calling setLogFormatter, and passing a nil parameter.
|
||||
*
|
||||
* In addition to the convenience of having a logical default formatter,
|
||||
* it will also provide a template that makes it easy for developers to copy and change.
|
||||
**/
|
||||
**/
|
||||
@interface DDLogFileFormatterDefault : NSObject <DDLogFormatter>
|
||||
{
|
||||
NSDateFormatter *dateFormatter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Default initializer
|
||||
*/
|
||||
- (instancetype)init;
|
||||
|
||||
/**
|
||||
* Designated initializer, requires a date formatter
|
||||
*/
|
||||
- (instancetype)initWithDateFormatter:(NSDateFormatter *)dateFormatter NS_DESIGNATED_INITIALIZER;
|
||||
- (id)init;
|
||||
- (instancetype)initWithDateFormatter:(NSDateFormatter *)dateFormatter;
|
||||
|
||||
@end
|
||||
|
||||
@ -299,89 +225,81 @@ extern unsigned long long const kDDDefaultLogFilesDiskQuota;
|
||||
#pragma mark -
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* The standard implementation for a file logger
|
||||
*/
|
||||
@interface DDFileLogger : DDAbstractLogger <DDLogger>
|
||||
{
|
||||
__strong id <DDLogFileManager> logFileManager;
|
||||
|
||||
DDLogFileInfo *currentLogFileInfo;
|
||||
NSFileHandle *currentLogFileHandle;
|
||||
|
||||
dispatch_source_t currentLogFileVnode;
|
||||
dispatch_source_t rollingTimer;
|
||||
|
||||
unsigned long long maximumFileSize;
|
||||
NSTimeInterval rollingFrequency;
|
||||
}
|
||||
|
||||
/**
|
||||
* Default initializer
|
||||
*/
|
||||
- (instancetype)init;
|
||||
|
||||
/**
|
||||
* Designated initializer, requires a `DDLogFileManager` instance
|
||||
*/
|
||||
- (instancetype)initWithLogFileManager:(id <DDLogFileManager>)logFileManager NS_DESIGNATED_INITIALIZER;
|
||||
- (id)init;
|
||||
- (instancetype)initWithLogFileManager:(id <DDLogFileManager>)logFileManager;
|
||||
|
||||
/**
|
||||
* Log File Rolling:
|
||||
*
|
||||
* `maximumFileSize`:
|
||||
*
|
||||
* maximumFileSize:
|
||||
* The approximate maximum size to allow log files to grow.
|
||||
* If a log file is larger than this value after a log statement is appended,
|
||||
* then the log file is rolled.
|
||||
*
|
||||
* `rollingFrequency`
|
||||
*
|
||||
* rollingFrequency
|
||||
* How often to roll the log file.
|
||||
* The frequency is given as an `NSTimeInterval`, which is a double that specifies the interval in seconds.
|
||||
* The frequency is given as an NSTimeInterval, which is a double that specifies the interval in seconds.
|
||||
* Once the log file gets to be this old, it is rolled.
|
||||
*
|
||||
* Both the `maximumFileSize` and the `rollingFrequency` are used to manage rolling.
|
||||
*
|
||||
* Both the maximumFileSize and the rollingFrequency are used to manage rolling.
|
||||
* Whichever occurs first will cause the log file to be rolled.
|
||||
*
|
||||
*
|
||||
* For example:
|
||||
* The `rollingFrequency` is 24 hours,
|
||||
* but the log file surpasses the `maximumFileSize` after only 20 hours.
|
||||
* The rollingFrequency is 24 hours,
|
||||
* but the log file surpasses the maximumFileSize after only 20 hours.
|
||||
* The log file will be rolled at that 20 hour mark.
|
||||
* A new log file will be created, and the 24 hour timer will be restarted.
|
||||
*
|
||||
* You may optionally disable rolling due to filesize by setting `maximumFileSize` to zero.
|
||||
* If you do so, rolling is based solely on `rollingFrequency`.
|
||||
*
|
||||
* You may optionally disable rolling due to time by setting `rollingFrequency` to zero (or any non-positive number).
|
||||
* If you do so, rolling is based solely on `maximumFileSize`.
|
||||
*
|
||||
* If you disable both `maximumFileSize` and `rollingFrequency`, then the log file won't ever be rolled.
|
||||
*
|
||||
* You may optionally disable rolling due to filesize by setting maximumFileSize to zero.
|
||||
* If you do so, rolling is based solely on rollingFrequency.
|
||||
*
|
||||
* You may optionally disable rolling due to time by setting rollingFrequency to zero (or any non-positive number).
|
||||
* If you do so, rolling is based solely on maximumFileSize.
|
||||
*
|
||||
* If you disable both maximumFileSize and rollingFrequency, then the log file won't ever be rolled.
|
||||
* This is strongly discouraged.
|
||||
**/
|
||||
**/
|
||||
@property (readwrite, assign) unsigned long long maximumFileSize;
|
||||
|
||||
/**
|
||||
* See description for `maximumFileSize`
|
||||
*/
|
||||
@property (readwrite, assign) NSTimeInterval rollingFrequency;
|
||||
|
||||
/**
|
||||
* See description for `maximumFileSize`
|
||||
*/
|
||||
@property (readwrite, assign, atomic) BOOL doNotReuseLogFiles;
|
||||
|
||||
/**
|
||||
* The DDLogFileManager instance can be used to retrieve the list of log files,
|
||||
* and configure the maximum number of archived log files to keep.
|
||||
*
|
||||
*
|
||||
* @see DDLogFileManager.maximumNumberOfLogFiles
|
||||
**/
|
||||
**/
|
||||
@property (strong, nonatomic, readonly) id <DDLogFileManager> logFileManager;
|
||||
|
||||
/**
|
||||
* When using a custom formatter you can set the `logMessage` method not to append
|
||||
* `\n` character after each output. This allows for some greater flexibility with
|
||||
* When using a custom formatter you can set the logMessage method not to append
|
||||
* '\n' character after each output. This allows for some greater flexibility with
|
||||
* custom formatters. Default value is YES.
|
||||
**/
|
||||
@property (nonatomic, readwrite, assign) BOOL automaticallyAppendNewlineForCustomFormatters;
|
||||
**/
|
||||
|
||||
@property (readwrite, assign) BOOL automaticallyAppendNewlineForCustomFormatters;
|
||||
|
||||
// You can optionally force the current log file to be rolled with this method.
|
||||
// CompletionBlock will be called on main queue.
|
||||
|
||||
/**
|
||||
* You can optionally force the current log file to be rolled with this method.
|
||||
* CompletionBlock will be called on main queue.
|
||||
*/
|
||||
- (void)rollLogFileWithCompletionBlock:(void (^)())completionBlock;
|
||||
|
||||
/**
|
||||
* Method is deprecated.
|
||||
* @deprecated Use `rollLogFileWithCompletionBlock:` method instead.
|
||||
*/
|
||||
// Method is deprecated. Use rollLogFileWithCompletionBlock: method instead.
|
||||
|
||||
- (void)rollLogFile __attribute((deprecated));
|
||||
|
||||
// Inherited from DDAbstractLogger
|
||||
@ -389,15 +307,6 @@ extern unsigned long long const kDDDefaultLogFilesDiskQuota;
|
||||
// - (id <DDLogFormatter>)logFormatter;
|
||||
// - (void)setLogFormatter:(id <DDLogFormatter>)formatter;
|
||||
|
||||
/**
|
||||
* Returns the log file that should be used.
|
||||
* If there is an existing log file that is suitable,
|
||||
* within the constraints of `maximumFileSize` and `rollingFrequency`, then it is returned.
|
||||
*
|
||||
* Otherwise a new file is created and returned.
|
||||
**/
|
||||
- (DDLogFileInfo *)currentLogFileInfo;
|
||||
|
||||
@end
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@ -405,20 +314,31 @@ extern unsigned long long const kDDDefaultLogFilesDiskQuota;
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* `DDLogFileInfo` is a simple class that provides access to various file attributes.
|
||||
* DDLogFileInfo is a simple class that provides access to various file attributes.
|
||||
* It provides good performance as it only fetches the information if requested,
|
||||
* and it caches the information to prevent duplicate fetches.
|
||||
*
|
||||
*
|
||||
* It was designed to provide quick snapshots of the current state of log files,
|
||||
* and to help sort log files in an array.
|
||||
*
|
||||
*
|
||||
* This class does not monitor the files, or update it's cached attribute values if the file changes on disk.
|
||||
* This is not what the class was designed for.
|
||||
*
|
||||
*
|
||||
* If you absolutely must get updated values,
|
||||
* you can invoke the reset method which will clear the cache.
|
||||
**/
|
||||
**/
|
||||
@interface DDLogFileInfo : NSObject
|
||||
{
|
||||
__strong NSString *filePath;
|
||||
__strong NSString *fileName;
|
||||
|
||||
__strong NSDictionary *fileAttributes;
|
||||
|
||||
__strong NSDate *creationDate;
|
||||
__strong NSDate *modificationDate;
|
||||
|
||||
unsigned long long fileSize;
|
||||
}
|
||||
|
||||
@property (strong, nonatomic, readonly) NSString *filePath;
|
||||
@property (strong, nonatomic, readonly) NSString *fileName;
|
||||
@ -436,8 +356,7 @@ extern unsigned long long const kDDDefaultLogFilesDiskQuota;
|
||||
|
||||
+ (instancetype)logFileWithPath:(NSString *)filePath;
|
||||
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
- (instancetype)initWithFilePath:(NSString *)filePath NS_DESIGNATED_INITIALIZER;
|
||||
- (instancetype)initWithFilePath:(NSString *)filePath;
|
||||
|
||||
- (void)reset;
|
||||
- (void)renameFile:(NSString *)newFileName;
|
||||
@ -447,19 +366,19 @@ extern unsigned long long const kDDDefaultLogFilesDiskQuota;
|
||||
// So here's the situation.
|
||||
// Extended attributes are perfect for what we're trying to do here (marking files as archived).
|
||||
// This is exactly what extended attributes were designed for.
|
||||
//
|
||||
//
|
||||
// But Apple screws us over on the simulator.
|
||||
// Everytime you build-and-go, they copy the application into a new folder on the hard drive,
|
||||
// and as part of the process they strip extended attributes from our log files.
|
||||
// Normally, a copy of a file preserves extended attributes.
|
||||
// So obviously Apple has gone to great lengths to piss us off.
|
||||
//
|
||||
//
|
||||
// Thus we use a slightly different tactic for marking log files as archived in the simulator.
|
||||
// That way it "just works" and there's no confusion when testing.
|
||||
//
|
||||
//
|
||||
// The difference in method names is indicative of the difference in functionality.
|
||||
// On the simulator we add an attribute by appending a filename extension.
|
||||
//
|
||||
//
|
||||
// For example:
|
||||
// "mylog.txt" -> "mylog.archived.txt"
|
||||
// "mylog" -> "mylog.archived"
|
||||
@ -469,7 +388,7 @@ extern unsigned long long const kDDDefaultLogFilesDiskQuota;
|
||||
- (void)addExtensionAttributeWithName:(NSString *)attrName;
|
||||
- (void)removeExtensionAttributeWithName:(NSString *)attrName;
|
||||
|
||||
#else /* if TARGET_IPHONE_SIMULATOR */
|
||||
#else
|
||||
|
||||
// Normal use of extended attributes used everywhere else,
|
||||
// such as on Macs and on iPhone devices.
|
||||
@ -479,7 +398,7 @@ extern unsigned long long const kDDDefaultLogFilesDiskQuota;
|
||||
- (void)addExtendedAttributeWithName:(NSString *)attrName;
|
||||
- (void)removeExtendedAttributeWithName:(NSString *)attrName;
|
||||
|
||||
#endif /* if TARGET_IPHONE_SIMULATOR */
|
||||
#endif
|
||||
|
||||
- (NSComparisonResult)reverseCompareByCreationDate:(DDLogFileInfo *)another;
|
||||
- (NSComparisonResult)reverseCompareByModificationDate:(DDLogFileInfo *)another;
|
||||
File diff suppressed because it is too large
Load Diff
99
Example/Pods/CocoaLumberjack/Lumberjack/DDLog+LOGV.h
generated
Normal file
99
Example/Pods/CocoaLumberjack/Lumberjack/DDLog+LOGV.h
generated
Normal file
@ -0,0 +1,99 @@
|
||||
//
|
||||
// DDLog+LOGV.h
|
||||
// Lumberjack
|
||||
//
|
||||
// Created by Mike Pontillo on 11/20/12.
|
||||
//
|
||||
//
|
||||
|
||||
#ifndef Lumberjack_DDLog_LOGV_h
|
||||
#define Lumberjack_DDLog_LOGV_h
|
||||
|
||||
#import "DDLog.h"
|
||||
|
||||
|
||||
#define LOGV_MACRO(isAsynchronous, lvl, flg, ctx, atag, fnct, frmt, avalist) \
|
||||
[DDLog log:isAsynchronous \
|
||||
level:lvl \
|
||||
flag:flg \
|
||||
context:ctx \
|
||||
file:__FILE__ \
|
||||
function:fnct \
|
||||
line:__LINE__ \
|
||||
tag:atag \
|
||||
format:frmt \
|
||||
args:avalist]
|
||||
|
||||
#define LOGV_OBJC_MACRO(async, lvl, flg, ctx, frmt, avalist) \
|
||||
LOGV_MACRO(async, lvl, flg, ctx, nil, sel_getName(_cmd), frmt, avalist)
|
||||
|
||||
#define LOGV_C_MACRO(async, lvl, flg, ctx, frmt, avalist) \
|
||||
LOGV_MACRO(async, lvl, flg, ctx, nil, __FUNCTION__, frmt, avalist)
|
||||
|
||||
|
||||
|
||||
#define SYNC_LOGV_OBJC_MACRO(lvl, flg, ctx, frmt, avalist) \
|
||||
LOGV_OBJC_MACRO( NO, lvl, flg, ctx, frmt, avalist)
|
||||
|
||||
#define ASYNC_LOGV_OBJC_MACRO(lvl, flg, ctx, frmt, avalist) \
|
||||
LOGV_OBJC_MACRO(YES, lvl, flg, ctx, frmt, avalist)
|
||||
|
||||
#define SYNC_LOGV_C_MACRO(lvl, flg, ctx, frmt, avalist) \
|
||||
LOGV_C_MACRO( NO, lvl, flg, ctx, frmt, avalist)
|
||||
|
||||
#define ASYNC_LOGV_C_MACRO(lvl, flg, ctx, frmt, avalist) \
|
||||
LOGV_C_MACRO(YES, lvl, flg, ctx, frmt, avalist)
|
||||
|
||||
|
||||
|
||||
#define LOGV_MAYBE(async, lvl, flg, ctx, fnct, frmt, avalist) \
|
||||
do { if(lvl & flg) LOGV_MACRO(async, lvl, flg, ctx, nil, fnct, frmt, avalist); } while(0)
|
||||
|
||||
#define LOGV_OBJC_MAYBE(async, lvl, flg, ctx, frmt, avalist) \
|
||||
LOGV_MAYBE(async, lvl, flg, ctx, sel_getName(_cmd), frmt, avalist)
|
||||
|
||||
#define LOGV_C_MAYBE(async, lvl, flg, ctx, frmt, avalist) \
|
||||
LOGV_MAYBE(async, lvl, flg, ctx, __FUNCTION__, frmt, avalist)
|
||||
|
||||
#define SYNC_LOGV_OBJC_MAYBE(lvl, flg, ctx, frmt, avalist) \
|
||||
LOGV_OBJC_MAYBE( NO, lvl, flg, ctx, frmt, avalist)
|
||||
|
||||
#define ASYNC_LOGV_OBJC_MAYBE(lvl, flg, ctx, frmt, avalist) \
|
||||
LOGV_OBJC_MAYBE(YES, lvl, flg, ctx, frmt, avalist)
|
||||
|
||||
#define SYNC_LOGV_C_MAYBE(lvl, flg, ctx, frmt, avalist) \
|
||||
LOGV_C_MAYBE( NO, lvl, flg, ctx, frmt, avalist)
|
||||
|
||||
#define ASYNC_LOGV_C_MAYBE(lvl, flg, ctx, frmt, avalist) \
|
||||
LOGV_C_MAYBE(YES, lvl, flg, ctx, frmt, avalist)
|
||||
|
||||
|
||||
|
||||
#define LOGV_OBJC_TAG_MACRO(async, lvl, flg, ctx, tag, frmt, avalist) \
|
||||
LOGV_MACRO(async, lvl, flg, ctx, tag, sel_getName(_cmd), frmt, avalist)
|
||||
|
||||
#define LOGV_C_TAG_MACRO(async, lvl, flg, ctx, tag, frmt, avalist) \
|
||||
LOGV_MACRO(async, lvl, flg, ctx, tag, __FUNCTION__, frmt, avalist)
|
||||
|
||||
#define LOGV_TAG_MAYBE(async, lvl, flg, ctx, tag, fnct, frmt, avalist) \
|
||||
do { if(lvl & flg) LOGV_MACRO(async, lvl, flg, ctx, tag, fnct, frmt, avalist); } while(0)
|
||||
|
||||
#define LOGV_OBJC_TAG_MAYBE(async, lvl, flg, ctx, tag, frmt, avalist) \
|
||||
LOGV_TAG_MAYBE(async, lvl, flg, ctx, tag, sel_getName(_cmd), frmt, avalist)
|
||||
|
||||
#define LOGV_C_TAG_MAYBE(async, lvl, flg, ctx, tag, frmt, avalist) \
|
||||
LOGV_TAG_MAYBE(async, lvl, flg, ctx, tag, __FUNCTION__, frmt, avalist)
|
||||
|
||||
|
||||
|
||||
#define DDLogvError(frmt, avalist) LOGV_OBJC_MAYBE(LOG_ASYNC_ERROR, ddLogLevel, LOG_FLAG_ERROR, 0, frmt, avalist)
|
||||
#define DDLogvWarn(frmt, avalist) LOGV_OBJC_MAYBE(LOG_ASYNC_WARN, ddLogLevel, LOG_FLAG_WARN, 0, frmt, avalist)
|
||||
#define DDLogvInfo(frmt, avalist) LOGV_OBJC_MAYBE(LOG_ASYNC_INFO, ddLogLevel, LOG_FLAG_INFO, 0, frmt, avalist)
|
||||
#define DDLogvVerbose(frmt, avalist) LOGV_OBJC_MAYBE(LOG_ASYNC_VERBOSE, ddLogLevel, LOG_FLAG_VERBOSE, 0, frmt, avalist)
|
||||
|
||||
#define DDLogvCError(frmt, avalist) LOGV_C_MAYBE(LOG_ASYNC_ERROR, ddLogLevel, LOG_FLAG_ERROR, 0, frmt, avalist)
|
||||
#define DDLogvCWarn(frmt, avalist) LOGV_C_MAYBE(LOG_ASYNC_WARN, ddLogLevel, LOG_FLAG_WARN, 0, frmt, avalist)
|
||||
#define DDLogvCInfo(frmt, avalist) LOGV_C_MAYBE(LOG_ASYNC_INFO, ddLogLevel, LOG_FLAG_INFO, 0, frmt, avalist)
|
||||
#define DDLogvCVerbose(frmt, avalist) LOGV_C_MAYBE(LOG_ASYNC_VERBOSE, ddLogLevel, LOG_FLAG_VERBOSE, 0, frmt, avalist)
|
||||
|
||||
#endif
|
||||
692
Example/Pods/CocoaLumberjack/Lumberjack/DDLog.h
generated
Executable file
692
Example/Pods/CocoaLumberjack/Lumberjack/DDLog.h
generated
Executable file
@ -0,0 +1,692 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
/**
|
||||
* Welcome to Cocoa Lumberjack!
|
||||
*
|
||||
* The project page has a wealth of documentation if you have any questions.
|
||||
* https://github.com/CocoaLumberjack/CocoaLumberjack
|
||||
*
|
||||
* If you're new to the project you may wish to read the "Getting Started" wiki.
|
||||
* https://github.com/CocoaLumberjack/CocoaLumberjack/wiki/GettingStarted
|
||||
*
|
||||
* Otherwise, here is a quick refresher.
|
||||
* There are three steps to using the macros:
|
||||
*
|
||||
* Step 1:
|
||||
* Import the header in your implementation file:
|
||||
*
|
||||
* #import "DDLog.h"
|
||||
*
|
||||
* Step 2:
|
||||
* Define your logging level in your implementation file:
|
||||
*
|
||||
* // Log levels: off, error, warn, info, verbose
|
||||
* static const int ddLogLevel = LOG_LEVEL_VERBOSE;
|
||||
*
|
||||
* Step 2 [3rd party frameworks]:
|
||||
*
|
||||
* Define your LOG_LEVEL_DEF to a different variable/function than ddLogLevel:
|
||||
*
|
||||
* // #undef LOG_LEVEL_DEF // Undefine first only if needed
|
||||
* #define LOG_LEVEL_DEF myLibLogLevel
|
||||
*
|
||||
* Define your logging level in your implementation file:
|
||||
*
|
||||
* // Log levels: off, error, warn, info, verbose
|
||||
* static const int myLibLogLevel = LOG_LEVEL_VERBOSE;
|
||||
*
|
||||
* Step 3:
|
||||
* Replace your NSLog statements with DDLog statements according to the severity of the message.
|
||||
*
|
||||
* NSLog(@"Fatal error, no dohickey found!"); -> DDLogError(@"Fatal error, no dohickey found!");
|
||||
*
|
||||
* DDLog works exactly the same as NSLog.
|
||||
* This means you can pass it multiple variables just like NSLog.
|
||||
**/
|
||||
|
||||
#ifndef LOG_LEVEL_DEF
|
||||
#define LOG_LEVEL_DEF ddLogLevel
|
||||
#endif
|
||||
|
||||
@class DDLogMessage;
|
||||
|
||||
@protocol DDLogger;
|
||||
@protocol DDLogFormatter;
|
||||
|
||||
/**
|
||||
* This is the single macro that all other macros below compile into.
|
||||
* This big multiline macro makes all the other macros easier to read.
|
||||
**/
|
||||
|
||||
#define LOG_MACRO(isAsynchronous, lvl, flg, ctx, atag, fnct, frmt, ...) \
|
||||
[DDLog log:isAsynchronous \
|
||||
level:lvl \
|
||||
flag:flg \
|
||||
context:ctx \
|
||||
file:__FILE__ \
|
||||
function:fnct \
|
||||
line:__LINE__ \
|
||||
tag:atag \
|
||||
format:(frmt), ##__VA_ARGS__]
|
||||
|
||||
/**
|
||||
* Define the Objective-C and C versions of the macro.
|
||||
* These automatically inject the proper function name for either an objective-c method or c function.
|
||||
*
|
||||
* We also define shorthand versions for asynchronous and synchronous logging.
|
||||
**/
|
||||
|
||||
#define LOG_OBJC_MACRO(async, lvl, flg, ctx, frmt, ...) \
|
||||
LOG_MACRO(async, lvl, flg, ctx, nil, sel_getName(_cmd), frmt, ##__VA_ARGS__)
|
||||
|
||||
#define LOG_C_MACRO(async, lvl, flg, ctx, frmt, ...) \
|
||||
LOG_MACRO(async, lvl, flg, ctx, nil, __FUNCTION__, frmt, ##__VA_ARGS__)
|
||||
|
||||
#define SYNC_LOG_OBJC_MACRO(lvl, flg, ctx, frmt, ...) \
|
||||
LOG_OBJC_MACRO(NO, lvl, flg, ctx, frmt, ##__VA_ARGS__)
|
||||
|
||||
#define ASYNC_LOG_OBJC_MACRO(lvl, flg, ctx, frmt, ...) \
|
||||
LOG_OBJC_MACRO(YES, lvl, flg, ctx, frmt, ##__VA_ARGS__)
|
||||
|
||||
#define SYNC_LOG_C_MACRO(lvl, flg, ctx, frmt, ...) \
|
||||
LOG_C_MACRO(NO, lvl, flg, ctx, frmt, ##__VA_ARGS__)
|
||||
|
||||
#define ASYNC_LOG_C_MACRO(lvl, flg, ctx, frmt, ...) \
|
||||
LOG_C_MACRO(YES, lvl, flg, ctx, frmt, ##__VA_ARGS__)
|
||||
|
||||
/**
|
||||
* Define version of the macro that only execute if the logLevel is above the threshold.
|
||||
* The compiled versions essentially look like this:
|
||||
*
|
||||
* if (logFlagForThisLogMsg & ddLogLevel) { execute log message }
|
||||
*
|
||||
* When LOG_LEVEL_DEF is defined as ddLogLevel.
|
||||
*
|
||||
* As shown further below, Lumberjack actually uses a bitmask as opposed to primitive log levels.
|
||||
* This allows for a great amount of flexibility and some pretty advanced fine grained logging techniques.
|
||||
*
|
||||
* Note that when compiler optimizations are enabled (as they are for your release builds),
|
||||
* the log messages above your logging threshold will automatically be compiled out.
|
||||
*
|
||||
* (If the compiler sees LOG_LEVEL_DEF/ddLogLevel declared as a constant, the compiler simply checks to see
|
||||
* if the 'if' statement would execute, and if not it strips it from the binary.)
|
||||
*
|
||||
* We also define shorthand versions for asynchronous and synchronous logging.
|
||||
**/
|
||||
|
||||
#define LOG_MAYBE(async, lvl, flg, ctx, fnct, frmt, ...) \
|
||||
do { if(lvl & flg) LOG_MACRO(async, lvl, flg, ctx, nil, fnct, frmt, ##__VA_ARGS__); } while(0)
|
||||
|
||||
#define LOG_OBJC_MAYBE(async, lvl, flg, ctx, frmt, ...) \
|
||||
LOG_MAYBE(async, lvl, flg, ctx, sel_getName(_cmd), frmt, ##__VA_ARGS__)
|
||||
|
||||
#define LOG_C_MAYBE(async, lvl, flg, ctx, frmt, ...) \
|
||||
LOG_MAYBE(async, lvl, flg, ctx, __FUNCTION__, frmt, ##__VA_ARGS__)
|
||||
|
||||
#define SYNC_LOG_OBJC_MAYBE(lvl, flg, ctx, frmt, ...) \
|
||||
LOG_OBJC_MAYBE(NO, lvl, flg, ctx, frmt, ##__VA_ARGS__)
|
||||
|
||||
#define ASYNC_LOG_OBJC_MAYBE(lvl, flg, ctx, frmt, ...) \
|
||||
LOG_OBJC_MAYBE(YES, lvl, flg, ctx, frmt, ##__VA_ARGS__)
|
||||
|
||||
#define SYNC_LOG_C_MAYBE(lvl, flg, ctx, frmt, ...) \
|
||||
LOG_C_MAYBE(NO, lvl, flg, ctx, frmt, ##__VA_ARGS__)
|
||||
|
||||
#define ASYNC_LOG_C_MAYBE(lvl, flg, ctx, frmt, ...) \
|
||||
LOG_C_MAYBE(YES, lvl, flg, ctx, frmt, ##__VA_ARGS__)
|
||||
|
||||
/**
|
||||
* Define versions of the macros that also accept tags.
|
||||
*
|
||||
* The DDLogMessage object includes a 'tag' ivar that may be used for a variety of purposes.
|
||||
* It may be used to pass custom information to loggers or formatters.
|
||||
* Or it may be used by 3rd party extensions to the framework.
|
||||
*
|
||||
* Thes macros just make it a little easier to extend logging functionality.
|
||||
**/
|
||||
|
||||
#define LOG_OBJC_TAG_MACRO(async, lvl, flg, ctx, tag, frmt, ...) \
|
||||
LOG_MACRO(async, lvl, flg, ctx, tag, sel_getName(_cmd), frmt, ##__VA_ARGS__)
|
||||
|
||||
#define LOG_C_TAG_MACRO(async, lvl, flg, ctx, tag, frmt, ...) \
|
||||
LOG_MACRO(async, lvl, flg, ctx, tag, __FUNCTION__, frmt, ##__VA_ARGS__)
|
||||
|
||||
#define LOG_TAG_MAYBE(async, lvl, flg, ctx, tag, fnct, frmt, ...) \
|
||||
do { if(lvl & flg) LOG_MACRO(async, lvl, flg, ctx, tag, fnct, frmt, ##__VA_ARGS__); } while(0)
|
||||
|
||||
#define LOG_OBJC_TAG_MAYBE(async, lvl, flg, ctx, tag, frmt, ...) \
|
||||
LOG_TAG_MAYBE(async, lvl, flg, ctx, tag, sel_getName(_cmd), frmt, ##__VA_ARGS__)
|
||||
|
||||
#define LOG_C_TAG_MAYBE(async, lvl, flg, ctx, tag, frmt, ...) \
|
||||
LOG_TAG_MAYBE(async, lvl, flg, ctx, tag, __FUNCTION__, frmt, ##__VA_ARGS__)
|
||||
|
||||
/**
|
||||
* Define the standard options.
|
||||
*
|
||||
* We default to only 4 levels because it makes it easier for beginners
|
||||
* to make the transition to a logging framework.
|
||||
*
|
||||
* More advanced users may choose to completely customize the levels (and level names) to suite their needs.
|
||||
* For more information on this see the "Custom Log Levels" page:
|
||||
* https://github.com/CocoaLumberjack/CocoaLumberjack/wiki/CustomLogLevels
|
||||
*
|
||||
* Advanced users may also notice that we're using a bitmask.
|
||||
* This is to allow for custom fine grained logging:
|
||||
* https://github.com/CocoaLumberjack/CocoaLumberjack/wiki/FineGrainedLogging
|
||||
*
|
||||
* -- Flags --
|
||||
*
|
||||
* Typically you will use the LOG_LEVELS (see below), but the flags may be used directly in certain situations.
|
||||
* For example, say you have a lot of warning log messages, and you wanted to disable them.
|
||||
* However, you still needed to see your error and info log messages.
|
||||
* You could accomplish that with the following:
|
||||
*
|
||||
* static const int ddLogLevel = LOG_FLAG_ERROR | LOG_FLAG_INFO;
|
||||
*
|
||||
* When LOG_LEVEL_DEF is defined as ddLogLevel.
|
||||
*
|
||||
* Flags may also be consulted when writing custom log formatters,
|
||||
* as the DDLogMessage class captures the individual flag that caused the log message to fire.
|
||||
*
|
||||
* -- Levels --
|
||||
*
|
||||
* Log levels are simply the proper bitmask of the flags.
|
||||
*
|
||||
* -- Booleans --
|
||||
*
|
||||
* The booleans may be used when your logging code involves more than one line.
|
||||
* For example:
|
||||
*
|
||||
* if (LOG_VERBOSE) {
|
||||
* for (id sprocket in sprockets)
|
||||
* DDLogVerbose(@"sprocket: %@", [sprocket description])
|
||||
* }
|
||||
*
|
||||
* -- Async --
|
||||
*
|
||||
* Defines the default asynchronous options.
|
||||
* The default philosophy for asynchronous logging is very simple:
|
||||
*
|
||||
* Log messages with errors should be executed synchronously.
|
||||
* After all, an error just occurred. The application could be unstable.
|
||||
*
|
||||
* All other log messages, such as debug output, are executed asynchronously.
|
||||
* After all, if it wasn't an error, then it was just informational output,
|
||||
* or something the application was easily able to recover from.
|
||||
*
|
||||
* -- Changes --
|
||||
*
|
||||
* You are strongly discouraged from modifying this file.
|
||||
* If you do, you make it more difficult on yourself to merge future bug fixes and improvements from the project.
|
||||
* Instead, create your own MyLogging.h or ApplicationNameLogging.h or CompanyLogging.h
|
||||
*
|
||||
* For an example of customizing your logging experience, see the "Custom Log Levels" page:
|
||||
* https://github.com/CocoaLumberjack/CocoaLumberjack/wiki/CustomLogLevels
|
||||
**/
|
||||
|
||||
#define LOG_FLAG_ERROR (1 << 0) // 0...00001
|
||||
#define LOG_FLAG_WARN (1 << 1) // 0...00010
|
||||
#define LOG_FLAG_INFO (1 << 2) // 0...00100
|
||||
#define LOG_FLAG_DEBUG (1 << 3) // 0...01000
|
||||
#define LOG_FLAG_VERBOSE (1 << 4) // 0...10000
|
||||
|
||||
#define LOG_LEVEL_OFF 0
|
||||
#define LOG_LEVEL_ERROR (LOG_FLAG_ERROR) // 0...00001
|
||||
#define LOG_LEVEL_WARN (LOG_FLAG_ERROR | LOG_FLAG_WARN) // 0...00011
|
||||
#define LOG_LEVEL_INFO (LOG_FLAG_ERROR | LOG_FLAG_WARN | LOG_FLAG_INFO) // 0...00111
|
||||
#define LOG_LEVEL_DEBUG (LOG_FLAG_ERROR | LOG_FLAG_WARN | LOG_FLAG_INFO | LOG_FLAG_DEBUG) // 0...01111
|
||||
#define LOG_LEVEL_VERBOSE (LOG_FLAG_ERROR | LOG_FLAG_WARN | LOG_FLAG_INFO | LOG_FLAG_DEBUG | LOG_FLAG_VERBOSE) // 0...11111
|
||||
#define LOG_LEVEL_ALL 0xFFFFFFFF // 1111....11111 (LOG_LEVEL_VERBOSE plus any other flags)
|
||||
|
||||
#define LOG_ERROR (LOG_LEVEL_DEF & LOG_FLAG_ERROR)
|
||||
#define LOG_WARN (LOG_LEVEL_DEF & LOG_FLAG_WARN)
|
||||
#define LOG_INFO (LOG_LEVEL_DEF & LOG_FLAG_INFO)
|
||||
#define LOG_DEBUG (LOG_LEVEL_DEF & LOG_FLAG_DEBUG)
|
||||
#define LOG_VERBOSE (LOG_LEVEL_DEF & LOG_FLAG_VERBOSE)
|
||||
|
||||
#define LOG_ASYNC_ENABLED YES
|
||||
|
||||
#define LOG_ASYNC_ERROR ( NO && LOG_ASYNC_ENABLED)
|
||||
#define LOG_ASYNC_WARN (YES && LOG_ASYNC_ENABLED)
|
||||
#define LOG_ASYNC_INFO (YES && LOG_ASYNC_ENABLED)
|
||||
#define LOG_ASYNC_DEBUG (YES && LOG_ASYNC_ENABLED)
|
||||
#define LOG_ASYNC_VERBOSE (YES && LOG_ASYNC_ENABLED)
|
||||
|
||||
#define DDLogError(frmt, ...) LOG_OBJC_MAYBE(LOG_ASYNC_ERROR, LOG_LEVEL_DEF, LOG_FLAG_ERROR, 0, frmt, ##__VA_ARGS__)
|
||||
#define DDLogWarn(frmt, ...) LOG_OBJC_MAYBE(LOG_ASYNC_WARN, LOG_LEVEL_DEF, LOG_FLAG_WARN, 0, frmt, ##__VA_ARGS__)
|
||||
#define DDLogInfo(frmt, ...) LOG_OBJC_MAYBE(LOG_ASYNC_INFO, LOG_LEVEL_DEF, LOG_FLAG_INFO, 0, frmt, ##__VA_ARGS__)
|
||||
#define DDLogDebug(frmt, ...) LOG_OBJC_MAYBE(LOG_ASYNC_DEBUG, LOG_LEVEL_DEF, LOG_FLAG_DEBUG, 0, frmt, ##__VA_ARGS__)
|
||||
#define DDLogVerbose(frmt, ...) LOG_OBJC_MAYBE(LOG_ASYNC_VERBOSE, LOG_LEVEL_DEF, LOG_FLAG_VERBOSE, 0, frmt, ##__VA_ARGS__)
|
||||
|
||||
#define DDLogCError(frmt, ...) LOG_C_MAYBE(LOG_ASYNC_ERROR, LOG_LEVEL_DEF, LOG_FLAG_ERROR, 0, frmt, ##__VA_ARGS__)
|
||||
#define DDLogCWarn(frmt, ...) LOG_C_MAYBE(LOG_ASYNC_WARN, LOG_LEVEL_DEF, LOG_FLAG_WARN, 0, frmt, ##__VA_ARGS__)
|
||||
#define DDLogCInfo(frmt, ...) LOG_C_MAYBE(LOG_ASYNC_INFO, LOG_LEVEL_DEF, LOG_FLAG_INFO, 0, frmt, ##__VA_ARGS__)
|
||||
#define DDLogCDebug(frmt, ...) LOG_C_MAYBE(LOG_ASYNC_DEBUG, LOG_LEVEL_DEF, LOG_FLAG_DEBUG, 0, frmt, ##__VA_ARGS__)
|
||||
#define DDLogCVerbose(frmt, ...) LOG_C_MAYBE(LOG_ASYNC_VERBOSE, LOG_LEVEL_DEF, LOG_FLAG_VERBOSE, 0, frmt, ##__VA_ARGS__)
|
||||
|
||||
/**
|
||||
* The THIS_FILE macro gives you an NSString of the file name.
|
||||
* For simplicity and clarity, the file name does not include the full path or file extension.
|
||||
*
|
||||
* For example: DDLogWarn(@"%@: Unable to find thingy", THIS_FILE) -> @"MyViewController: Unable to find thingy"
|
||||
**/
|
||||
|
||||
NSString *DDExtractFileNameWithoutExtension(const char *filePath, BOOL copy);
|
||||
|
||||
#define THIS_FILE (DDExtractFileNameWithoutExtension(__FILE__, NO))
|
||||
|
||||
/**
|
||||
* The THIS_METHOD macro gives you the name of the current objective-c method.
|
||||
*
|
||||
* For example: DDLogWarn(@"%@ - Requires non-nil strings", THIS_METHOD) -> @"setMake:model: requires non-nil strings"
|
||||
*
|
||||
* Note: This does NOT work in straight C functions (non objective-c).
|
||||
* Instead you should use the predefined __FUNCTION__ macro.
|
||||
**/
|
||||
|
||||
#define THIS_METHOD NSStringFromSelector(_cmd)
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark -
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@interface DDLog : NSObject
|
||||
|
||||
/**
|
||||
* Provides access to the underlying logging queue.
|
||||
* This may be helpful to Logger classes for things like thread synchronization.
|
||||
**/
|
||||
|
||||
+ (dispatch_queue_t)loggingQueue;
|
||||
|
||||
/**
|
||||
* Logging Primitive.
|
||||
*
|
||||
* This method is used by the macros above.
|
||||
* It is suggested you stick with the macros as they're easier to use.
|
||||
**/
|
||||
|
||||
+ (void)log:(BOOL)synchronous
|
||||
level:(int)level
|
||||
flag:(int)flag
|
||||
context:(int)context
|
||||
file:(const char *)file
|
||||
function:(const char *)function
|
||||
line:(int)line
|
||||
tag:(id)tag
|
||||
format:(NSString *)format, ... __attribute__ ((format (__NSString__, 9, 10)));
|
||||
|
||||
/**
|
||||
* Logging Primitive.
|
||||
*
|
||||
* This method can be used if you have a prepared va_list.
|
||||
**/
|
||||
|
||||
+ (void)log:(BOOL)asynchronous
|
||||
level:(int)level
|
||||
flag:(int)flag
|
||||
context:(int)context
|
||||
file:(const char *)file
|
||||
function:(const char *)function
|
||||
line:(int)line
|
||||
tag:(id)tag
|
||||
format:(NSString *)format
|
||||
args:(va_list)argList;
|
||||
|
||||
/**
|
||||
* Logging Primitive.
|
||||
*
|
||||
* This method can be used if you manualy prepared DDLogMessage.
|
||||
**/
|
||||
|
||||
+ (void)log:(BOOL)asynchronous
|
||||
message:(DDLogMessage *)logMessage;
|
||||
|
||||
/**
|
||||
* Since logging can be asynchronous, there may be times when you want to flush the logs.
|
||||
* The framework invokes this automatically when the application quits.
|
||||
**/
|
||||
|
||||
+ (void)flushLog;
|
||||
|
||||
/**
|
||||
* Loggers
|
||||
*
|
||||
* In order for your log statements to go somewhere, you should create and add a logger.
|
||||
*
|
||||
* You can add multiple loggers in order to direct your log statements to multiple places.
|
||||
* And each logger can be configured separately.
|
||||
* So you could have, for example, verbose logging to the console, but a concise log file with only warnings & errors.
|
||||
**/
|
||||
|
||||
/**
|
||||
* Adds the logger to the system.
|
||||
*
|
||||
* This is equivalent to invoking [DDLog addLogger:logger withLogLevel:LOG_LEVEL_ALL].
|
||||
**/
|
||||
+ (void)addLogger:(id <DDLogger>)logger;
|
||||
|
||||
/**
|
||||
* Adds the logger to the system.
|
||||
*
|
||||
* The logLevel that you provide here is a preemptive filter (for performance).
|
||||
* That is, the logLevel specified here will be used to filter out logMessages so that
|
||||
* the logger is never even invoked for the messages.
|
||||
*
|
||||
* More information:
|
||||
* When you issue a log statement, the logging framework iterates over each logger,
|
||||
* and checks to see if it should forward the logMessage to the logger.
|
||||
* This check is done using the logLevel parameter passed to this method.
|
||||
*
|
||||
* For example:
|
||||
* [DDLog addLogger:consoleLogger withLogLevel:LOG_LEVEL_VERBOSE];
|
||||
* [DDLog addLogger:fileLogger withLogLevel:LOG_LEVEL_WARN];
|
||||
*
|
||||
* DDLogError(@"oh no"); => gets forwarded to consoleLogger & fileLogger
|
||||
* DDLogInfo(@"hi"); => gets forwarded to consoleLogger only
|
||||
*
|
||||
* It is important to remember that Lumberjack uses a BITMASK.
|
||||
* Many developers & third party frameworks may define extra log levels & flags.
|
||||
* For example:
|
||||
*
|
||||
* #define SOME_FRAMEWORK_LOG_FLAG_TRACE (1 << 6) // 0...1000000
|
||||
*
|
||||
* So if you specify LOG_LEVEL_VERBOSE to this method, you won't see the framework's trace messages.
|
||||
*
|
||||
* (SOME_FRAMEWORK_LOG_FLAG_TRACE & LOG_LEVEL_VERBOSE) => (01000000 & 00011111) => NO
|
||||
*
|
||||
* Consider passing LOG_LEVEL_ALL to this method, which has all bits set.
|
||||
* You can also use the exclusive-or bitwise operator to get a bitmask that has all flags set,
|
||||
* except the ones you explicitly don't want. For example, if you wanted everything except verbose & debug:
|
||||
*
|
||||
* ((LOG_LEVEL_ALL ^ LOG_LEVEL_VERBOSE) | LOG_LEVEL_INFO)
|
||||
**/
|
||||
+ (void)addLogger:(id <DDLogger>)logger withLogLevel:(int)logLevel;
|
||||
|
||||
+ (void)removeLogger:(id <DDLogger>)logger;
|
||||
+ (void)removeAllLoggers;
|
||||
|
||||
+ (NSArray *)allLoggers;
|
||||
|
||||
/**
|
||||
* Registered Dynamic Logging
|
||||
*
|
||||
* These methods allow you to obtain a list of classes that are using registered dynamic logging,
|
||||
* and also provides methods to get and set their log level during run time.
|
||||
**/
|
||||
|
||||
+ (NSArray *)registeredClasses;
|
||||
+ (NSArray *)registeredClassNames;
|
||||
|
||||
+ (int)logLevelForClass:(Class)aClass;
|
||||
+ (int)logLevelForClassWithName:(NSString *)aClassName;
|
||||
|
||||
+ (void)setLogLevel:(int)logLevel forClass:(Class)aClass;
|
||||
+ (void)setLogLevel:(int)logLevel forClassWithName:(NSString *)aClassName;
|
||||
|
||||
@end
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark -
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@protocol DDLogger <NSObject>
|
||||
@required
|
||||
|
||||
- (void)logMessage:(DDLogMessage *)logMessage;
|
||||
|
||||
/**
|
||||
* Formatters may optionally be added to any logger.
|
||||
*
|
||||
* If no formatter is set, the logger simply logs the message as it is given in logMessage,
|
||||
* or it may use its own built in formatting style.
|
||||
**/
|
||||
- (id <DDLogFormatter>)logFormatter;
|
||||
- (void)setLogFormatter:(id <DDLogFormatter>)formatter;
|
||||
|
||||
@optional
|
||||
|
||||
/**
|
||||
* Since logging is asynchronous, adding and removing loggers is also asynchronous.
|
||||
* In other words, the loggers are added and removed at appropriate times with regards to log messages.
|
||||
*
|
||||
* - Loggers will not receive log messages that were executed prior to when they were added.
|
||||
* - Loggers will not receive log messages that were executed after they were removed.
|
||||
*
|
||||
* These methods are executed in the logging thread/queue.
|
||||
* This is the same thread/queue that will execute every logMessage: invocation.
|
||||
* Loggers may use these methods for thread synchronization or other setup/teardown tasks.
|
||||
**/
|
||||
- (void)didAddLogger;
|
||||
- (void)willRemoveLogger;
|
||||
|
||||
/**
|
||||
* Some loggers may buffer IO for optimization purposes.
|
||||
* For example, a database logger may only save occasionaly as the disk IO is slow.
|
||||
* In such loggers, this method should be implemented to flush any pending IO.
|
||||
*
|
||||
* This allows invocations of DDLog's flushLog method to be propogated to loggers that need it.
|
||||
*
|
||||
* Note that DDLog's flushLog method is invoked automatically when the application quits,
|
||||
* and it may be also invoked manually by the developer prior to application crashes, or other such reasons.
|
||||
**/
|
||||
- (void)flush;
|
||||
|
||||
/**
|
||||
* Each logger is executed concurrently with respect to the other loggers.
|
||||
* Thus, a dedicated dispatch queue is used for each logger.
|
||||
* Logger implementations may optionally choose to provide their own dispatch queue.
|
||||
**/
|
||||
- (dispatch_queue_t)loggerQueue;
|
||||
|
||||
/**
|
||||
* If the logger implementation does not choose to provide its own queue,
|
||||
* one will automatically be created for it.
|
||||
* The created queue will receive its name from this method.
|
||||
* This may be helpful for debugging or profiling reasons.
|
||||
**/
|
||||
- (NSString *)loggerName;
|
||||
|
||||
@end
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark -
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@protocol DDLogFormatter <NSObject>
|
||||
@required
|
||||
|
||||
/**
|
||||
* Formatters may optionally be added to any logger.
|
||||
* This allows for increased flexibility in the logging environment.
|
||||
* For example, log messages for log files may be formatted differently than log messages for the console.
|
||||
*
|
||||
* For more information about formatters, see the "Custom Formatters" page:
|
||||
* https://github.com/CocoaLumberjack/CocoaLumberjack/wiki/CustomFormatters
|
||||
*
|
||||
* The formatter may also optionally filter the log message by returning nil,
|
||||
* in which case the logger will not log the message.
|
||||
**/
|
||||
- (NSString *)formatLogMessage:(DDLogMessage *)logMessage;
|
||||
|
||||
@optional
|
||||
|
||||
/**
|
||||
* A single formatter instance can be added to multiple loggers.
|
||||
* These methods provides hooks to notify the formatter of when it's added/removed.
|
||||
*
|
||||
* This is primarily for thread-safety.
|
||||
* If a formatter is explicitly not thread-safe, it may wish to throw an exception if added to multiple loggers.
|
||||
* Or if a formatter has potentially thread-unsafe code (e.g. NSDateFormatter),
|
||||
* it could possibly use these hooks to switch to thread-safe versions of the code.
|
||||
**/
|
||||
- (void)didAddToLogger:(id <DDLogger>)logger;
|
||||
- (void)willRemoveFromLogger:(id <DDLogger>)logger;
|
||||
|
||||
@end
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark -
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@protocol DDRegisteredDynamicLogging
|
||||
|
||||
/**
|
||||
* Implement these methods to allow a file's log level to be managed from a central location.
|
||||
*
|
||||
* This is useful if you'd like to be able to change log levels for various parts
|
||||
* of your code from within the running application.
|
||||
*
|
||||
* Imagine pulling up the settings for your application,
|
||||
* and being able to configure the logging level on a per file basis.
|
||||
*
|
||||
* The implementation can be very straight-forward:
|
||||
*
|
||||
* + (int)ddLogLevel
|
||||
* {
|
||||
* return ddLogLevel;
|
||||
* }
|
||||
*
|
||||
* + (void)ddSetLogLevel:(int)logLevel
|
||||
* {
|
||||
* ddLogLevel = logLevel;
|
||||
* }
|
||||
**/
|
||||
|
||||
+ (int)ddLogLevel;
|
||||
+ (void)ddSetLogLevel:(int)logLevel;
|
||||
|
||||
@end
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark -
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* The DDLogMessage class encapsulates information about the log message.
|
||||
* If you write custom loggers or formatters, you will be dealing with objects of this class.
|
||||
**/
|
||||
|
||||
enum {
|
||||
DDLogMessageCopyFile = 1 << 0,
|
||||
DDLogMessageCopyFunction = 1 << 1
|
||||
};
|
||||
typedef int DDLogMessageOptions;
|
||||
|
||||
@interface DDLogMessage : NSObject <NSCopying>
|
||||
{
|
||||
|
||||
// The public variables below can be accessed directly (for speed).
|
||||
// For example: logMessage->logLevel
|
||||
|
||||
@public
|
||||
int logLevel;
|
||||
int logFlag;
|
||||
int logContext;
|
||||
NSString *logMsg;
|
||||
NSDate *timestamp;
|
||||
char *file;
|
||||
char *function;
|
||||
int lineNumber;
|
||||
mach_port_t machThreadID;
|
||||
char *queueLabel;
|
||||
NSString *threadName;
|
||||
|
||||
// For 3rd party extensions to the framework, where flags and contexts aren't enough.
|
||||
id tag;
|
||||
|
||||
// For 3rd party extensions that manually create DDLogMessage instances.
|
||||
DDLogMessageOptions options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Standard init method for a log message object.
|
||||
* Used by the logging primitives. (And the macros use the logging primitives.)
|
||||
*
|
||||
* If you find need to manually create logMessage objects, there is one thing you should be aware of:
|
||||
*
|
||||
* If no flags are passed, the method expects the file and function parameters to be string literals.
|
||||
* That is, it expects the given strings to exist for the duration of the object's lifetime,
|
||||
* and it expects the given strings to be immutable.
|
||||
* In other words, it does not copy these strings, it simply points to them.
|
||||
* This is due to the fact that __FILE__ and __FUNCTION__ are usually used to specify these parameters,
|
||||
* so it makes sense to optimize and skip the unnecessary allocations.
|
||||
* However, if you need them to be copied you may use the options parameter to specify this.
|
||||
* Options is a bitmask which supports DDLogMessageCopyFile and DDLogMessageCopyFunction.
|
||||
**/
|
||||
- (instancetype)initWithLogMsg:(NSString *)logMsg
|
||||
level:(int)logLevel
|
||||
flag:(int)logFlag
|
||||
context:(int)logContext
|
||||
file:(const char *)file
|
||||
function:(const char *)function
|
||||
line:(int)line
|
||||
tag:(id)tag
|
||||
options:(DDLogMessageOptions)optionsMask;
|
||||
- (instancetype)initWithLogMsg:(NSString *)logMsg
|
||||
level:(int)logLevel
|
||||
flag:(int)logFlag
|
||||
context:(int)logContext
|
||||
file:(const char *)file
|
||||
function:(const char *)function
|
||||
line:(int)line
|
||||
tag:(id)tag
|
||||
options:(DDLogMessageOptions)optionsMask
|
||||
timestamp:(NSDate *)aTimestamp;
|
||||
|
||||
/**
|
||||
* Returns the threadID as it appears in NSLog.
|
||||
* That is, it is a hexadecimal value which is calculated from the machThreadID.
|
||||
**/
|
||||
- (NSString *)threadID;
|
||||
|
||||
/**
|
||||
* Convenience property to get just the file name, as the file variable is generally the full file path.
|
||||
* This method does not include the file extension, which is generally unwanted for logging purposes.
|
||||
**/
|
||||
- (NSString *)fileName;
|
||||
|
||||
/**
|
||||
* Returns the function variable in NSString form.
|
||||
**/
|
||||
- (NSString *)methodName;
|
||||
|
||||
@end
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark -
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* The DDLogger protocol specifies that an optional formatter can be added to a logger.
|
||||
* Most (but not all) loggers will want to support formatters.
|
||||
*
|
||||
* However, writting getters and setters in a thread safe manner,
|
||||
* while still maintaining maximum speed for the logging process, is a difficult task.
|
||||
*
|
||||
* To do it right, the implementation of the getter/setter has strict requiremenets:
|
||||
* - Must NOT require the logMessage method to acquire a lock.
|
||||
* - Must NOT require the logMessage method to access an atomic property (also a lock of sorts).
|
||||
*
|
||||
* To simplify things, an abstract logger is provided that implements the getter and setter.
|
||||
*
|
||||
* Logger implementations may simply extend this class,
|
||||
* and they can ACCESS THE FORMATTER VARIABLE DIRECTLY from within their logMessage method!
|
||||
**/
|
||||
|
||||
@interface DDAbstractLogger : NSObject <DDLogger>
|
||||
{
|
||||
id <DDLogFormatter> formatter;
|
||||
|
||||
dispatch_queue_t loggerQueue;
|
||||
}
|
||||
|
||||
- (id <DDLogFormatter>)logFormatter;
|
||||
- (void)setLogFormatter:(id <DDLogFormatter>)formatter;
|
||||
|
||||
// For thread-safety assertions
|
||||
- (BOOL)isOnGlobalLoggingQueue;
|
||||
- (BOOL)isOnInternalLoggerQueue;
|
||||
|
||||
@end
|
||||
1250
Example/Pods/CocoaLumberjack/Classes/DDLog.m → Example/Pods/CocoaLumberjack/Lumberjack/DDLog.m
generated
Normal file → Executable file
1250
Example/Pods/CocoaLumberjack/Classes/DDLog.m → Example/Pods/CocoaLumberjack/Lumberjack/DDLog.m
generated
Normal file → Executable file
File diff suppressed because it is too large
Load Diff
188
Example/Pods/CocoaLumberjack/Lumberjack/DDTTYLogger.h
generated
Executable file
188
Example/Pods/CocoaLumberjack/Lumberjack/DDTTYLogger.h
generated
Executable file
@ -0,0 +1,188 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
#if TARGET_OS_IPHONE
|
||||
#import <UIKit/UIColor.h> // iOS
|
||||
#elif !defined (COCOAPODS_POD_AVAILABLE_CocoaLumberjack_CLI)
|
||||
#import <AppKit/NSColor.h> // OS X with AppKit
|
||||
#else
|
||||
#import "CLIColor.h" // OS X without AppKit
|
||||
#endif
|
||||
|
||||
#import "DDLog.h"
|
||||
|
||||
#define LOG_CONTEXT_ALL INT_MAX
|
||||
|
||||
/**
|
||||
* Welcome to Cocoa Lumberjack!
|
||||
*
|
||||
* The project page has a wealth of documentation if you have any questions.
|
||||
* https://github.com/CocoaLumberjack/CocoaLumberjack
|
||||
*
|
||||
* If you're new to the project you may wish to read the "Getting Started" wiki.
|
||||
* https://github.com/CocoaLumberjack/CocoaLumberjack/wiki/GettingStarted
|
||||
*
|
||||
*
|
||||
* This class provides a logger for Terminal output or Xcode console output,
|
||||
* depending on where you are running your code.
|
||||
*
|
||||
* As described in the "Getting Started" page,
|
||||
* the traditional NSLog() function directs it's output to two places:
|
||||
*
|
||||
* - Apple System Log (so it shows up in Console.app)
|
||||
* - StdErr (if stderr is a TTY, so log statements show up in Xcode console)
|
||||
*
|
||||
* To duplicate NSLog() functionality you can simply add this logger and an asl logger.
|
||||
* However, if you instead choose to use file logging (for faster performance),
|
||||
* you may choose to use only a file logger and a tty logger.
|
||||
**/
|
||||
|
||||
@interface DDTTYLogger : DDAbstractLogger <DDLogger>
|
||||
{
|
||||
NSUInteger calendarUnitFlags;
|
||||
|
||||
NSString *appName;
|
||||
char *app;
|
||||
size_t appLen;
|
||||
|
||||
NSString *processID;
|
||||
char *pid;
|
||||
size_t pidLen;
|
||||
|
||||
BOOL colorsEnabled;
|
||||
NSMutableArray *colorProfilesArray;
|
||||
NSMutableDictionary *colorProfilesDict;
|
||||
}
|
||||
|
||||
+ (instancetype)sharedInstance;
|
||||
|
||||
/* Inherited from the DDLogger protocol:
|
||||
*
|
||||
* Formatters may optionally be added to any logger.
|
||||
*
|
||||
* If no formatter is set, the logger simply logs the message as it is given in logMessage,
|
||||
* or it may use its own built in formatting style.
|
||||
*
|
||||
* More information about formatters can be found here:
|
||||
* https://github.com/CocoaLumberjack/CocoaLumberjack/wiki/CustomFormatters
|
||||
*
|
||||
* The actual implementation of these methods is inherited from DDAbstractLogger.
|
||||
|
||||
- (id <DDLogFormatter>)logFormatter;
|
||||
- (void)setLogFormatter:(id <DDLogFormatter>)formatter;
|
||||
|
||||
*/
|
||||
|
||||
/**
|
||||
* Want to use different colors for different log levels?
|
||||
* Enable this property.
|
||||
*
|
||||
* If you run the application via the Terminal (not Xcode),
|
||||
* the logger will map colors to xterm-256color or xterm-color (if available).
|
||||
*
|
||||
* Xcode does NOT natively support colors in the Xcode debugging console.
|
||||
* You'll need to install the XcodeColors plugin to see colors in the Xcode console.
|
||||
* https://github.com/robbiehanson/XcodeColors
|
||||
*
|
||||
* The default value is NO.
|
||||
**/
|
||||
@property (readwrite, assign) BOOL colorsEnabled;
|
||||
|
||||
/**
|
||||
* When using a custom formatter you can set the logMessage method not to append
|
||||
* '\n' character after each output. This allows for some greater flexibility with
|
||||
* custom formatters. Default value is YES.
|
||||
**/
|
||||
|
||||
@property (readwrite, assign) BOOL automaticallyAppendNewlineForCustomFormatters;
|
||||
|
||||
/**
|
||||
* The default color set (foregroundColor, backgroundColor) is:
|
||||
*
|
||||
* - LOG_FLAG_ERROR = (red, nil)
|
||||
* - LOG_FLAG_WARN = (orange, nil)
|
||||
*
|
||||
* You can customize the colors however you see fit.
|
||||
* Please note that you are passing a flag, NOT a level.
|
||||
*
|
||||
* GOOD : [ttyLogger setForegroundColor:pink backgroundColor:nil forFlag:LOG_FLAG_INFO]; // <- Good :)
|
||||
* BAD : [ttyLogger setForegroundColor:pink backgroundColor:nil forFlag:LOG_LEVEL_INFO]; // <- BAD! :(
|
||||
*
|
||||
* LOG_FLAG_INFO = 0...00100
|
||||
* LOG_LEVEL_INFO = 0...00111 <- Would match LOG_FLAG_INFO and LOG_FLAG_WARN and LOG_FLAG_ERROR
|
||||
*
|
||||
* If you run the application within Xcode, then the XcodeColors plugin is required.
|
||||
*
|
||||
* If you run the application from a shell, then DDTTYLogger will automatically map the given color to
|
||||
* the closest available color. (xterm-256color or xterm-color which have 256 and 16 supported colors respectively.)
|
||||
*
|
||||
* This method invokes setForegroundColor:backgroundColor:forFlag:context: and applies it to `LOG_CONTEXT_ALL`.
|
||||
**/
|
||||
#if TARGET_OS_IPHONE
|
||||
- (void)setForegroundColor:(UIColor *)txtColor backgroundColor:(UIColor *)bgColor forFlag:(int)mask;
|
||||
#elif !defined (COCOAPODS_POD_AVAILABLE_CocoaLumberjack_CLI)
|
||||
- (void)setForegroundColor:(NSColor *)txtColor backgroundColor:(NSColor *)bgColor forFlag:(int)mask;
|
||||
#else
|
||||
- (void)setForegroundColor:(CLIColor *)txtColor backgroundColor:(CLIColor *)bgColor forFlag:(int)mask;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Just like setForegroundColor:backgroundColor:flag, but allows you to specify a particular logging context.
|
||||
*
|
||||
* A logging context is often used to identify log messages coming from a 3rd party framework,
|
||||
* although logging context's can be used for many different functions.
|
||||
*
|
||||
* Use LOG_CONTEXT_ALL to set the deafult color for all contexts that have no specific color set defined.
|
||||
*
|
||||
* Logging context's are explained in further detail here:
|
||||
* https://github.com/CocoaLumberjack/CocoaLumberjack/wiki/CustomContext
|
||||
**/
|
||||
#if TARGET_OS_IPHONE
|
||||
- (void)setForegroundColor:(UIColor *)txtColor backgroundColor:(UIColor *)bgColor forFlag:(int)mask context:(int)ctxt;
|
||||
#elif !defined (COCOAPODS_POD_AVAILABLE_CocoaLumberjack_CLI)
|
||||
- (void)setForegroundColor:(NSColor *)txtColor backgroundColor:(NSColor *)bgColor forFlag:(int)mask context:(int)ctxt;
|
||||
#else
|
||||
- (void)setForegroundColor:(CLIColor *)txtColor backgroundColor:(CLIColor *)bgColor forFlag:(int)mask context:(int)ctxt;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Similar to the methods above, but allows you to map DDLogMessage->tag to a particular color profile.
|
||||
* For example, you could do something like this:
|
||||
*
|
||||
* static NSString *const PurpleTag = @"PurpleTag";
|
||||
*
|
||||
* #define DDLogPurple(frmt, ...) LOG_OBJC_TAG_MACRO(NO, 0, 0, 0, PurpleTag, frmt, ##__VA_ARGS__)
|
||||
*
|
||||
* And then in your applicationDidFinishLaunching, or wherever you configure Lumberjack:
|
||||
*
|
||||
* #if TARGET_OS_IPHONE
|
||||
* UIColor *purple = [UIColor colorWithRed:(64/255.0) green:(0/255.0) blue:(128/255.0) alpha:1.0];
|
||||
* #else
|
||||
* NSColor *purple = [NSColor colorWithCalibratedRed:(64/255.0) green:(0/255.0) blue:(128/255.0) alpha:1.0];
|
||||
*
|
||||
* Note: For CLI OS X projects that don't link with AppKit use CLIColor objects instead
|
||||
*
|
||||
* [[DDTTYLogger sharedInstance] setForegroundColor:purple backgroundColor:nil forTag:PurpleTag];
|
||||
* [DDLog addLogger:[DDTTYLogger sharedInstance]];
|
||||
*
|
||||
* This would essentially give you a straight NSLog replacement that prints in purple:
|
||||
*
|
||||
* DDLogPurple(@"I'm a purple log message!");
|
||||
**/
|
||||
#if TARGET_OS_IPHONE
|
||||
- (void)setForegroundColor:(UIColor *)txtColor backgroundColor:(UIColor *)bgColor forTag:(id <NSCopying>)tag;
|
||||
#elif !defined (COCOAPODS_POD_AVAILABLE_CocoaLumberjack_CLI)
|
||||
- (void)setForegroundColor:(NSColor *)txtColor backgroundColor:(NSColor *)bgColor forTag:(id <NSCopying>)tag;
|
||||
#else
|
||||
- (void)setForegroundColor:(CLIColor *)txtColor backgroundColor:(CLIColor *)bgColor forTag:(id <NSCopying>)tag;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Clearing color profiles.
|
||||
**/
|
||||
- (void)clearColorsForFlag:(int)mask;
|
||||
- (void)clearColorsForFlag:(int)mask context:(int)context;
|
||||
- (void)clearColorsForTag:(id <NSCopying>)tag;
|
||||
- (void)clearColorsForAllFlags;
|
||||
- (void)clearColorsForAllTags;
|
||||
- (void)clearAllColors;
|
||||
|
||||
@end
|
||||
1510
Example/Pods/CocoaLumberjack/Lumberjack/DDTTYLogger.m
generated
Executable file
1510
Example/Pods/CocoaLumberjack/Lumberjack/DDTTYLogger.m
generated
Executable file
File diff suppressed because it is too large
Load Diff
63
Example/Pods/CocoaLumberjack/Lumberjack/Extensions/DDContextFilterLogFormatter.h
generated
Normal file
63
Example/Pods/CocoaLumberjack/Lumberjack/Extensions/DDContextFilterLogFormatter.h
generated
Normal file
@ -0,0 +1,63 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "DDLog.h"
|
||||
|
||||
/**
|
||||
* Welcome to Cocoa Lumberjack!
|
||||
*
|
||||
* The project page has a wealth of documentation if you have any questions.
|
||||
* https://github.com/CocoaLumberjack/CocoaLumberjack
|
||||
*
|
||||
* If you're new to the project you may wish to read the "Getting Started" page.
|
||||
* https://github.com/CocoaLumberjack/CocoaLumberjack/wiki/GettingStarted
|
||||
*
|
||||
*
|
||||
* This class provides a log formatter that filters log statements from a logging context not on the whitelist.
|
||||
*
|
||||
* A log formatter can be added to any logger to format and/or filter its output.
|
||||
* You can learn more about log formatters here:
|
||||
* https://github.com/CocoaLumberjack/CocoaLumberjack/wiki/CustomFormatters
|
||||
*
|
||||
* You can learn more about logging context's here:
|
||||
* https://github.com/CocoaLumberjack/CocoaLumberjack/wiki/CustomContext
|
||||
*
|
||||
* But here's a quick overview / refresher:
|
||||
*
|
||||
* Every log statement has a logging context.
|
||||
* These come from the underlying logging macros defined in DDLog.h.
|
||||
* The default logging context is zero.
|
||||
* You can define multiple logging context's for use in your application.
|
||||
* For example, logically separate parts of your app each have a different logging context.
|
||||
* Also 3rd party frameworks that make use of Lumberjack generally use their own dedicated logging context.
|
||||
**/
|
||||
@interface DDContextWhitelistFilterLogFormatter : NSObject <DDLogFormatter>
|
||||
|
||||
- (id)init;
|
||||
|
||||
- (void)addToWhitelist:(int)loggingContext;
|
||||
- (void)removeFromWhitelist:(int)loggingContext;
|
||||
|
||||
- (NSArray *)whitelist;
|
||||
|
||||
- (BOOL)isOnWhitelist:(int)loggingContext;
|
||||
|
||||
@end
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark -
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* This class provides a log formatter that filters log statements from a logging context on the blacklist.
|
||||
**/
|
||||
@interface DDContextBlacklistFilterLogFormatter : NSObject <DDLogFormatter>
|
||||
|
||||
- (id)init;
|
||||
|
||||
- (void)addToBlacklist:(int)loggingContext;
|
||||
- (void)removeFromBlacklist:(int)loggingContext;
|
||||
|
||||
- (NSArray *)blacklist;
|
||||
|
||||
- (BOOL)isOnBlacklist:(int)loggingContext;
|
||||
|
||||
@end
|
||||
191
Example/Pods/CocoaLumberjack/Lumberjack/Extensions/DDContextFilterLogFormatter.m
generated
Normal file
191
Example/Pods/CocoaLumberjack/Lumberjack/Extensions/DDContextFilterLogFormatter.m
generated
Normal file
@ -0,0 +1,191 @@
|
||||
#import "DDContextFilterLogFormatter.h"
|
||||
#import <libkern/OSAtomic.h>
|
||||
|
||||
/**
|
||||
* Welcome to Cocoa Lumberjack!
|
||||
*
|
||||
* The project page has a wealth of documentation if you have any questions.
|
||||
* https://github.com/CocoaLumberjack/CocoaLumberjack
|
||||
*
|
||||
* If you're new to the project you may wish to read the "Getting Started" wiki.
|
||||
* https://github.com/CocoaLumberjack/CocoaLumberjack/wiki/GettingStarted
|
||||
**/
|
||||
|
||||
#if ! __has_feature(objc_arc)
|
||||
#error This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC).
|
||||
#endif
|
||||
|
||||
@interface DDLoggingContextSet : NSObject
|
||||
|
||||
- (void)addToSet:(int)loggingContext;
|
||||
- (void)removeFromSet:(int)loggingContext;
|
||||
|
||||
- (NSArray *)currentSet;
|
||||
|
||||
- (BOOL)isInSet:(int)loggingContext;
|
||||
|
||||
@end
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark -
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@implementation DDContextWhitelistFilterLogFormatter
|
||||
{
|
||||
DDLoggingContextSet *contextSet;
|
||||
}
|
||||
|
||||
- (id)init
|
||||
{
|
||||
if ((self = [super init]))
|
||||
{
|
||||
contextSet = [[DDLoggingContextSet alloc] init];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
- (void)addToWhitelist:(int)loggingContext
|
||||
{
|
||||
[contextSet addToSet:loggingContext];
|
||||
}
|
||||
|
||||
- (void)removeFromWhitelist:(int)loggingContext
|
||||
{
|
||||
[contextSet removeFromSet:loggingContext];
|
||||
}
|
||||
|
||||
- (NSArray *)whitelist
|
||||
{
|
||||
return [contextSet currentSet];
|
||||
}
|
||||
|
||||
- (BOOL)isOnWhitelist:(int)loggingContext
|
||||
{
|
||||
return [contextSet isInSet:loggingContext];
|
||||
}
|
||||
|
||||
- (NSString *)formatLogMessage:(DDLogMessage *)logMessage
|
||||
{
|
||||
if ([self isOnWhitelist:logMessage->logContext])
|
||||
return logMessage->logMsg;
|
||||
else
|
||||
return nil;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark -
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@implementation DDContextBlacklistFilterLogFormatter
|
||||
{
|
||||
DDLoggingContextSet *contextSet;
|
||||
}
|
||||
|
||||
- (id)init
|
||||
{
|
||||
if ((self = [super init]))
|
||||
{
|
||||
contextSet = [[DDLoggingContextSet alloc] init];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
- (void)addToBlacklist:(int)loggingContext
|
||||
{
|
||||
[contextSet addToSet:loggingContext];
|
||||
}
|
||||
|
||||
- (void)removeFromBlacklist:(int)loggingContext
|
||||
{
|
||||
[contextSet removeFromSet:loggingContext];
|
||||
}
|
||||
|
||||
- (NSArray *)blacklist
|
||||
{
|
||||
return [contextSet currentSet];
|
||||
}
|
||||
|
||||
- (BOOL)isOnBlacklist:(int)loggingContext
|
||||
{
|
||||
return [contextSet isInSet:loggingContext];
|
||||
}
|
||||
|
||||
- (NSString *)formatLogMessage:(DDLogMessage *)logMessage
|
||||
{
|
||||
if ([self isOnBlacklist:logMessage->logContext])
|
||||
return nil;
|
||||
else
|
||||
return logMessage->logMsg;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark -
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@implementation DDLoggingContextSet
|
||||
{
|
||||
OSSpinLock lock;
|
||||
NSMutableSet *set;
|
||||
}
|
||||
|
||||
- (id)init
|
||||
{
|
||||
if ((self = [super init]))
|
||||
{
|
||||
set = [[NSMutableSet alloc] init];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
- (void)addToSet:(int)loggingContext
|
||||
{
|
||||
OSSpinLockLock(&lock);
|
||||
{
|
||||
[set addObject:@(loggingContext)];
|
||||
}
|
||||
OSSpinLockUnlock(&lock);
|
||||
}
|
||||
|
||||
- (void)removeFromSet:(int)loggingContext
|
||||
{
|
||||
OSSpinLockLock(&lock);
|
||||
{
|
||||
[set removeObject:@(loggingContext)];
|
||||
}
|
||||
OSSpinLockUnlock(&lock);
|
||||
}
|
||||
|
||||
- (NSArray *)currentSet
|
||||
{
|
||||
NSArray *result = nil;
|
||||
|
||||
OSSpinLockLock(&lock);
|
||||
{
|
||||
result = [set allObjects];
|
||||
}
|
||||
OSSpinLockUnlock(&lock);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
- (BOOL)isInSet:(int)loggingContext
|
||||
{
|
||||
BOOL result = NO;
|
||||
|
||||
OSSpinLockLock(&lock);
|
||||
{
|
||||
result = [set containsObject:@(loggingContext)];
|
||||
}
|
||||
OSSpinLockUnlock(&lock);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@end
|
||||
128
Example/Pods/CocoaLumberjack/Lumberjack/Extensions/DDDispatchQueueLogFormatter.h
generated
Normal file
128
Example/Pods/CocoaLumberjack/Lumberjack/Extensions/DDDispatchQueueLogFormatter.h
generated
Normal file
@ -0,0 +1,128 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <libkern/OSAtomic.h>
|
||||
#import "DDLog.h"
|
||||
|
||||
|
||||
/**
|
||||
* Welcome to Cocoa Lumberjack!
|
||||
*
|
||||
* The project page has a wealth of documentation if you have any questions.
|
||||
* https://github.com/CocoaLumberjack/CocoaLumberjack
|
||||
*
|
||||
* If you're new to the project you may wish to read the "Getting Started" page.
|
||||
* https://github.com/CocoaLumberjack/CocoaLumberjack/wiki/GettingStarted
|
||||
*
|
||||
*
|
||||
* This class provides a log formatter that prints the dispatch_queue label instead of the mach_thread_id.
|
||||
*
|
||||
* A log formatter can be added to any logger to format and/or filter its output.
|
||||
* You can learn more about log formatters here:
|
||||
* https://github.com/CocoaLumberjack/CocoaLumberjack/wiki/CustomFormatters
|
||||
*
|
||||
* A typical NSLog (or DDTTYLogger) prints detailed info as [<process_id>:<thread_id>].
|
||||
* For example:
|
||||
*
|
||||
* 2011-10-17 20:21:45.435 AppName[19928:5207] Your log message here
|
||||
*
|
||||
* Where:
|
||||
* - 19928 = process id
|
||||
* - 5207 = thread id (mach_thread_id printed in hex)
|
||||
*
|
||||
* When using grand central dispatch (GCD), this information is less useful.
|
||||
* This is because a single serial dispatch queue may be run on any thread from an internally managed thread pool.
|
||||
* For example:
|
||||
*
|
||||
* 2011-10-17 20:32:31.111 AppName[19954:4d07] Message from my_serial_dispatch_queue
|
||||
* 2011-10-17 20:32:31.112 AppName[19954:5207] Message from my_serial_dispatch_queue
|
||||
* 2011-10-17 20:32:31.113 AppName[19954:2c55] Message from my_serial_dispatch_queue
|
||||
*
|
||||
* This formatter allows you to replace the standard [box:info] with the dispatch_queue name.
|
||||
* For example:
|
||||
*
|
||||
* 2011-10-17 20:32:31.111 AppName[img-scaling] Message from my_serial_dispatch_queue
|
||||
* 2011-10-17 20:32:31.112 AppName[img-scaling] Message from my_serial_dispatch_queue
|
||||
* 2011-10-17 20:32:31.113 AppName[img-scaling] Message from my_serial_dispatch_queue
|
||||
*
|
||||
* If the dispatch_queue doesn't have a set name, then it falls back to the thread name.
|
||||
* If the current thread doesn't have a set name, then it falls back to the mach_thread_id in hex (like normal).
|
||||
*
|
||||
* Note: If manually creating your own background threads (via NSThread/alloc/init or NSThread/detachNeThread),
|
||||
* you can use [[NSThread currentThread] setName:(NSString *)].
|
||||
**/
|
||||
@interface DDDispatchQueueLogFormatter : NSObject <DDLogFormatter> {
|
||||
@protected
|
||||
|
||||
NSString *dateFormatString;
|
||||
}
|
||||
|
||||
/**
|
||||
* Standard init method.
|
||||
* Configure using properties as desired.
|
||||
**/
|
||||
- (id)init;
|
||||
|
||||
/**
|
||||
* The minQueueLength restricts the minimum size of the [detail box].
|
||||
* If the minQueueLength is set to 0, there is no restriction.
|
||||
*
|
||||
* For example, say a dispatch_queue has a label of "diskIO":
|
||||
*
|
||||
* If the minQueueLength is 0: [diskIO]
|
||||
* If the minQueueLength is 4: [diskIO]
|
||||
* If the minQueueLength is 5: [diskIO]
|
||||
* If the minQueueLength is 6: [diskIO]
|
||||
* If the minQueueLength is 7: [diskIO ]
|
||||
* If the minQueueLength is 8: [diskIO ]
|
||||
*
|
||||
* The default minQueueLength is 0 (no minimum, so [detail box] won't be padded).
|
||||
*
|
||||
* If you want every [detail box] to have the exact same width,
|
||||
* set both minQueueLength and maxQueueLength to the same value.
|
||||
**/
|
||||
@property (assign, atomic) NSUInteger minQueueLength;
|
||||
|
||||
/**
|
||||
* The maxQueueLength restricts the number of characters that will be inside the [detail box].
|
||||
* If the maxQueueLength is 0, there is no restriction.
|
||||
*
|
||||
* For example, say a dispatch_queue has a label of "diskIO":
|
||||
*
|
||||
* If the maxQueueLength is 0: [diskIO]
|
||||
* If the maxQueueLength is 4: [disk]
|
||||
* If the maxQueueLength is 5: [diskI]
|
||||
* If the maxQueueLength is 6: [diskIO]
|
||||
* If the maxQueueLength is 7: [diskIO]
|
||||
* If the maxQueueLength is 8: [diskIO]
|
||||
*
|
||||
* The default maxQueueLength is 0 (no maximum, so [detail box] won't be truncated).
|
||||
*
|
||||
* If you want every [detail box] to have the exact same width,
|
||||
* set both minQueueLength and maxQueueLength to the same value.
|
||||
**/
|
||||
@property (assign, atomic) NSUInteger maxQueueLength;
|
||||
|
||||
/**
|
||||
* Sometimes queue labels have long names like "com.apple.main-queue",
|
||||
* but you'd prefer something shorter like simply "main".
|
||||
*
|
||||
* This method allows you to set such preferred replacements.
|
||||
* The above example is set by default.
|
||||
*
|
||||
* To remove/undo a previous replacement, invoke this method with nil for the 'shortLabel' parameter.
|
||||
**/
|
||||
- (NSString *)replacementStringForQueueLabel:(NSString *)longLabel;
|
||||
- (void)setReplacementString:(NSString *)shortLabel forQueueLabel:(NSString *)longLabel;
|
||||
|
||||
@end
|
||||
|
||||
/**
|
||||
* Method declarations that make it easier to extend/modify DDDispatchQueueLogFormatter
|
||||
**/
|
||||
@interface DDDispatchQueueLogFormatter (OverridableMethods)
|
||||
|
||||
- (NSString *)stringFromDate:(NSDate *)date;
|
||||
- (NSString *)queueThreadLabelForLogMessage:(DDLogMessage *)logMessage;
|
||||
- (NSString *)formatLogMessage:(DDLogMessage *)logMessage;
|
||||
|
||||
@end
|
||||
|
||||
260
Example/Pods/CocoaLumberjack/Lumberjack/Extensions/DDDispatchQueueLogFormatter.m
generated
Normal file
260
Example/Pods/CocoaLumberjack/Lumberjack/Extensions/DDDispatchQueueLogFormatter.m
generated
Normal file
@ -0,0 +1,260 @@
|
||||
#import "DDDispatchQueueLogFormatter.h"
|
||||
#import <libkern/OSAtomic.h>
|
||||
|
||||
/**
|
||||
* Welcome to Cocoa Lumberjack!
|
||||
*
|
||||
* The project page has a wealth of documentation if you have any questions.
|
||||
* https://github.com/CocoaLumberjack/CocoaLumberjack
|
||||
*
|
||||
* If you're new to the project you may wish to read the "Getting Started" wiki.
|
||||
* https://github.com/CocoaLumberjack/CocoaLumberjack/wiki/GettingStarted
|
||||
**/
|
||||
|
||||
#if ! __has_feature(objc_arc)
|
||||
#error This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC).
|
||||
#endif
|
||||
|
||||
|
||||
@implementation DDDispatchQueueLogFormatter
|
||||
{
|
||||
int32_t atomicLoggerCount;
|
||||
NSDateFormatter *threadUnsafeDateFormatter; // Use [self stringFromDate]
|
||||
|
||||
OSSpinLock lock;
|
||||
|
||||
NSUInteger _minQueueLength; // _prefix == Only access via atomic property
|
||||
NSUInteger _maxQueueLength; // _prefix == Only access via atomic property
|
||||
NSMutableDictionary *_replacements; // _prefix == Only access from within spinlock
|
||||
}
|
||||
|
||||
- (id)init
|
||||
{
|
||||
if ((self = [super init]))
|
||||
{
|
||||
dateFormatString = @"yyyy-MM-dd HH:mm:ss:SSS";
|
||||
|
||||
atomicLoggerCount = 0;
|
||||
threadUnsafeDateFormatter = nil;
|
||||
|
||||
_minQueueLength = 0;
|
||||
_maxQueueLength = 0;
|
||||
_replacements = [[NSMutableDictionary alloc] init];
|
||||
|
||||
// Set default replacements:
|
||||
|
||||
_replacements[@"com.apple.main-thread"] = @"main";
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark Configuration
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@synthesize minQueueLength = _minQueueLength;
|
||||
@synthesize maxQueueLength = _maxQueueLength;
|
||||
|
||||
- (NSString *)replacementStringForQueueLabel:(NSString *)longLabel
|
||||
{
|
||||
NSString *result = nil;
|
||||
|
||||
OSSpinLockLock(&lock);
|
||||
{
|
||||
result = _replacements[longLabel];
|
||||
}
|
||||
OSSpinLockUnlock(&lock);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
- (void)setReplacementString:(NSString *)shortLabel forQueueLabel:(NSString *)longLabel
|
||||
{
|
||||
OSSpinLockLock(&lock);
|
||||
{
|
||||
if (shortLabel)
|
||||
_replacements[longLabel] = shortLabel;
|
||||
else
|
||||
[_replacements removeObjectForKey:longLabel];
|
||||
}
|
||||
OSSpinLockUnlock(&lock);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark DDLogFormatter
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
- (NSString *)stringFromDate:(NSDate *)date
|
||||
{
|
||||
int32_t loggerCount = OSAtomicAdd32(0, &atomicLoggerCount);
|
||||
|
||||
NSString *calendarIdentifier = nil;
|
||||
#if defined(__IPHONE_8_0) || defined(__MAC_10_10)
|
||||
calendarIdentifier = NSCalendarIdentifierGregorian;
|
||||
#else
|
||||
calendarIdentifier = NSGregorianCalendar;
|
||||
#endif
|
||||
|
||||
if (loggerCount <= 1)
|
||||
{
|
||||
// Single-threaded mode.
|
||||
|
||||
if (threadUnsafeDateFormatter == nil)
|
||||
{
|
||||
threadUnsafeDateFormatter = [[NSDateFormatter alloc] init];
|
||||
[threadUnsafeDateFormatter setFormatterBehavior:NSDateFormatterBehavior10_4];
|
||||
[threadUnsafeDateFormatter setDateFormat:dateFormatString];
|
||||
}
|
||||
|
||||
[threadUnsafeDateFormatter setCalendar:[[NSCalendar alloc] initWithCalendarIdentifier:calendarIdentifier]];
|
||||
return [threadUnsafeDateFormatter stringFromDate:date];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Multi-threaded mode.
|
||||
// NSDateFormatter is NOT thread-safe.
|
||||
|
||||
NSString *key = @"DispatchQueueLogFormatter_NSDateFormatter";
|
||||
|
||||
NSMutableDictionary *threadDictionary = [[NSThread currentThread] threadDictionary];
|
||||
NSDateFormatter *dateFormatter = threadDictionary[key];
|
||||
|
||||
if (dateFormatter == nil)
|
||||
{
|
||||
dateFormatter = [[NSDateFormatter alloc] init];
|
||||
[dateFormatter setFormatterBehavior:NSDateFormatterBehavior10_4];
|
||||
[dateFormatter setDateFormat:dateFormatString];
|
||||
|
||||
threadDictionary[key] = dateFormatter;
|
||||
}
|
||||
|
||||
[dateFormatter setCalendar:[[NSCalendar alloc] initWithCalendarIdentifier:calendarIdentifier]];
|
||||
return [dateFormatter stringFromDate:date];
|
||||
}
|
||||
}
|
||||
|
||||
- (NSString *)queueThreadLabelForLogMessage:(DDLogMessage *)logMessage
|
||||
{
|
||||
// As per the DDLogFormatter contract, this method is always invoked on the same thread/dispatch_queue
|
||||
|
||||
NSUInteger minQueueLength = self.minQueueLength;
|
||||
NSUInteger maxQueueLength = self.maxQueueLength;
|
||||
|
||||
// Get the name of the queue, thread, or machID (whichever we are to use).
|
||||
|
||||
NSString *queueThreadLabel = nil;
|
||||
|
||||
BOOL useQueueLabel = YES;
|
||||
BOOL useThreadName = NO;
|
||||
|
||||
if (logMessage->queueLabel)
|
||||
{
|
||||
// If you manually create a thread, it's dispatch_queue will have one of the thread names below.
|
||||
// Since all such threads have the same name, we'd prefer to use the threadName or the machThreadID.
|
||||
|
||||
char *names[] = { "com.apple.root.low-priority",
|
||||
"com.apple.root.default-priority",
|
||||
"com.apple.root.high-priority",
|
||||
"com.apple.root.low-overcommit-priority",
|
||||
"com.apple.root.default-overcommit-priority",
|
||||
"com.apple.root.high-overcommit-priority" };
|
||||
|
||||
int length = sizeof(names) / sizeof(char *);
|
||||
|
||||
int i;
|
||||
for (i = 0; i < length; i++)
|
||||
{
|
||||
if (strcmp(logMessage->queueLabel, names[i]) == 0)
|
||||
{
|
||||
useQueueLabel = NO;
|
||||
useThreadName = [logMessage->threadName length] > 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
useQueueLabel = NO;
|
||||
useThreadName = [logMessage->threadName length] > 0;
|
||||
}
|
||||
|
||||
if (useQueueLabel || useThreadName)
|
||||
{
|
||||
NSString *fullLabel;
|
||||
NSString *abrvLabel;
|
||||
|
||||
if (useQueueLabel)
|
||||
fullLabel = @(logMessage->queueLabel);
|
||||
else
|
||||
fullLabel = logMessage->threadName;
|
||||
|
||||
OSSpinLockLock(&lock);
|
||||
{
|
||||
abrvLabel = _replacements[fullLabel];
|
||||
}
|
||||
OSSpinLockUnlock(&lock);
|
||||
|
||||
if (abrvLabel)
|
||||
queueThreadLabel = abrvLabel;
|
||||
else
|
||||
queueThreadLabel = fullLabel;
|
||||
}
|
||||
else
|
||||
{
|
||||
queueThreadLabel = [NSString stringWithFormat:@"%x", logMessage->machThreadID];
|
||||
}
|
||||
|
||||
// Now use the thread label in the output
|
||||
|
||||
NSUInteger labelLength = [queueThreadLabel length];
|
||||
|
||||
// labelLength > maxQueueLength : truncate
|
||||
// labelLength < minQueueLength : padding
|
||||
// : exact
|
||||
|
||||
if ((maxQueueLength > 0) && (labelLength > maxQueueLength))
|
||||
{
|
||||
// Truncate
|
||||
|
||||
return [queueThreadLabel substringToIndex:maxQueueLength];
|
||||
}
|
||||
else if (labelLength < minQueueLength)
|
||||
{
|
||||
// Padding
|
||||
|
||||
NSUInteger numSpaces = minQueueLength - labelLength;
|
||||
|
||||
char spaces[numSpaces + 1];
|
||||
memset(spaces, ' ', numSpaces);
|
||||
spaces[numSpaces] = '\0';
|
||||
|
||||
return [NSString stringWithFormat:@"%@%s", queueThreadLabel, spaces];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Exact
|
||||
|
||||
return queueThreadLabel;
|
||||
}
|
||||
}
|
||||
|
||||
- (NSString *)formatLogMessage:(DDLogMessage *)logMessage
|
||||
{
|
||||
NSString *timestamp = [self stringFromDate:(logMessage->timestamp)];
|
||||
NSString *queueThreadLabel = [self queueThreadLabelForLogMessage:logMessage];
|
||||
|
||||
return [NSString stringWithFormat:@"%@ [%@] %@", timestamp, queueThreadLabel, logMessage->logMsg];
|
||||
}
|
||||
|
||||
- (void)didAddToLogger:(id <DDLogger>)logger
|
||||
{
|
||||
OSAtomicIncrement32(&atomicLoggerCount);
|
||||
}
|
||||
|
||||
- (void)willRemoveFromLogger:(id <DDLogger>)logger
|
||||
{
|
||||
OSAtomicDecrement32(&atomicLoggerCount);
|
||||
}
|
||||
|
||||
@end
|
||||
30
Example/Pods/CocoaLumberjack/Lumberjack/Extensions/DDMultiFormatter.h
generated
Normal file
30
Example/Pods/CocoaLumberjack/Lumberjack/Extensions/DDMultiFormatter.h
generated
Normal file
@ -0,0 +1,30 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "DDLog.h"
|
||||
|
||||
/**
|
||||
* Welcome to Cocoa Lumberjack!
|
||||
*
|
||||
* The project page has a wealth of documentation if you have any questions.
|
||||
* https://github.com/CocoaLumberjack/CocoaLumberjack
|
||||
*
|
||||
* If you're new to the project you may wish to read the "Getting Started" page.
|
||||
* https://github.com/CocoaLumberjack/CocoaLumberjack/wiki/GettingStarted
|
||||
*
|
||||
*
|
||||
* This formatter can be used to chain different formatters together.
|
||||
* The log message will processed in the order of the formatters added.
|
||||
**/
|
||||
|
||||
@interface DDMultiFormatter : NSObject <DDLogFormatter>
|
||||
|
||||
/**
|
||||
* Array of chained formatters
|
||||
*/
|
||||
@property (readonly) NSArray *formatters;
|
||||
|
||||
- (void)addFormatter:(id<DDLogFormatter>)formatter;
|
||||
- (void)removeFormatter:(id<DDLogFormatter>)formatter;
|
||||
- (void)removeAllFormatters;
|
||||
- (BOOL)isFormattingWithFormatter:(id<DDLogFormatter>)formatter;
|
||||
|
||||
@end
|
||||
@ -1,31 +1,22 @@
|
||||
// Software License Agreement (BSD License)
|
||||
//
|
||||
// Copyright (c) 2010-2015, Deusty, LLC
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use of this software in source and binary forms,
|
||||
// with or without modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Neither the name of Deusty nor the names of its contributors may be used
|
||||
// to endorse or promote products derived from this software without specific
|
||||
// prior written permission of Deusty, LLC.
|
||||
|
||||
#import "DDMultiFormatter.h"
|
||||
|
||||
/**
|
||||
* Welcome to Cocoa Lumberjack!
|
||||
*
|
||||
* The project page has a wealth of documentation if you have any questions.
|
||||
* https://github.com/CocoaLumberjack/CocoaLumberjack
|
||||
*
|
||||
* If you're new to the project you may wish to read the "Getting Started" page.
|
||||
* https://github.com/CocoaLumberjack/CocoaLumberjack/wiki/GettingStarted
|
||||
**/
|
||||
|
||||
#if TARGET_OS_IOS
|
||||
#if TARGET_OS_IPHONE
|
||||
// Compiling for iOS
|
||||
#if __IPHONE_OS_VERSION_MIN_REQUIRED >= 60000 // iOS 6.0 or later
|
||||
#define NEEDS_DISPATCH_RETAIN_RELEASE 0
|
||||
#else // iOS 5.X or earlier
|
||||
#define NEEDS_DISPATCH_RETAIN_RELEASE 1
|
||||
#endif
|
||||
#elif TARGET_OS_WATCH || TARGET_OS_TV
|
||||
// Compiling for watchOS, tvOS
|
||||
#define NEEDS_DISPATCH_RETAIN_RELEASE 0
|
||||
#else
|
||||
// Compiling for Mac OS X
|
||||
#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1080 // Mac OS X 10.8 or later
|
||||
@ -36,26 +27,25 @@
|
||||
#endif
|
||||
|
||||
|
||||
#if !__has_feature(objc_arc)
|
||||
#if ! __has_feature(objc_arc)
|
||||
#error This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC).
|
||||
#endif
|
||||
|
||||
|
||||
@interface DDMultiFormatter () {
|
||||
dispatch_queue_t _queue;
|
||||
NSMutableArray *_formatters;
|
||||
}
|
||||
@interface DDMultiFormatter ()
|
||||
|
||||
- (DDLogMessage *)logMessageForLine:(NSString *)line originalMessage:(DDLogMessage *)message;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation DDMultiFormatter
|
||||
@implementation DDMultiFormatter {
|
||||
dispatch_queue_t _queue;
|
||||
NSMutableArray *_formatters;
|
||||
}
|
||||
|
||||
- (instancetype)init {
|
||||
- (id)init {
|
||||
self = [super init];
|
||||
|
||||
if (self) {
|
||||
#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1070
|
||||
_queue = dispatch_queue_create("cocoa.lumberjack.multiformatter", DISPATCH_QUEUE_CONCURRENT);
|
||||
@ -64,7 +54,7 @@
|
||||
#endif
|
||||
_formatters = [NSMutableArray new];
|
||||
}
|
||||
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@ -72,32 +62,30 @@
|
||||
- (void)dealloc {
|
||||
dispatch_release(_queue);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#pragma mark Processing
|
||||
|
||||
- (NSString *)formatLogMessage:(DDLogMessage *)logMessage {
|
||||
__block NSString *line = logMessage->_message;
|
||||
|
||||
__block NSString *line = logMessage->logMsg;
|
||||
|
||||
dispatch_sync(_queue, ^{
|
||||
for (id<DDLogFormatter> formatter in _formatters) {
|
||||
DDLogMessage *message = [self logMessageForLine:line originalMessage:logMessage];
|
||||
line = [formatter formatLogMessage:message];
|
||||
|
||||
|
||||
if (!line) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
return line;
|
||||
}
|
||||
|
||||
- (DDLogMessage *)logMessageForLine:(NSString *)line originalMessage:(DDLogMessage *)message {
|
||||
DDLogMessage *newMessage = [message copy];
|
||||
|
||||
newMessage->_message = line;
|
||||
newMessage->logMsg = line;
|
||||
return newMessage;
|
||||
}
|
||||
|
||||
@ -105,11 +93,11 @@
|
||||
|
||||
- (NSArray *)formatters {
|
||||
__block NSArray *formatters;
|
||||
|
||||
|
||||
dispatch_sync(_queue, ^{
|
||||
formatters = [_formatters copy];
|
||||
});
|
||||
|
||||
|
||||
return formatters;
|
||||
}
|
||||
|
||||
@ -133,11 +121,11 @@
|
||||
|
||||
- (BOOL)isFormattingWithFormatter:(id<DDLogFormatter>)formatter {
|
||||
__block BOOL hasFormatter;
|
||||
|
||||
|
||||
dispatch_sync(_queue, ^{
|
||||
hasFormatter = [_formatters containsObject:formatter];
|
||||
});
|
||||
|
||||
|
||||
return hasFormatter;
|
||||
}
|
||||
|
||||
7
Example/Pods/CocoaLumberjack/Lumberjack/Extensions/README.txt
generated
Normal file
7
Example/Pods/CocoaLumberjack/Lumberjack/Extensions/README.txt
generated
Normal file
@ -0,0 +1,7 @@
|
||||
This folder contains some sample formatters that may be helpful.
|
||||
|
||||
Feel free to change them, extend them, or use them as the basis for your own custom formatter(s).
|
||||
|
||||
More information about creating your own custom formatters can be found on the wiki:
|
||||
https://github.com/CocoaLumberjack/CocoaLumberjack/wiki/CustomFormatters
|
||||
|
||||
162
Example/Pods/CocoaLumberjack/README.md
generated
162
Example/Pods/CocoaLumberjack/README.md
generated
@ -4,133 +4,14 @@
|
||||
|
||||
CocoaLumberjack
|
||||
===============
|
||||
[](https://travis-ci.org/CocoaLumberjack/CocoaLumberjack)
|
||||
[](https://travis-ci.org/CocoaLumberjack/CocoaLumberjack)
|
||||
[](http://cocoadocs.org/docsets/CocoaLumberjack/)
|
||||
[](https://github.com/Carthage/Carthage)
|
||||
[](http://cocoadocs.org/docsets/CocoaLumberjack/)
|
||||
[](http://opensource.org/licenses/BSD-3-Clause)
|
||||
[](https://www.versioneye.com/objective-c/cocoalumberjack/references)
|
||||
|
||||
**CocoaLumberjack** is a fast & simple, yet powerful & flexible logging framework for Mac and iOS.
|
||||
|
||||
### How to get started
|
||||
- install via [CocoaPods](http://cocoapods.org)
|
||||
|
||||
##### Swift version via CocoaPods
|
||||
```ruby
|
||||
platform :ios, '8.0'
|
||||
pod 'CocoaLumberjack/Swift'
|
||||
use_frameworks!
|
||||
```
|
||||
Note: `Swift` is a subspec which will include all the Obj-C code plus the Swift one, so this is sufficient.
|
||||
For more details about how to use Swift with Lumberjack, see [this converation](https://github.com/CocoaLumberjack/CocoaLumberjack/issues/405).
|
||||
|
||||
##### Swift Usage
|
||||
|
||||
If you installed using CocoaPods or manually:
|
||||
```swift
|
||||
import CocoaLumberjack
|
||||
```
|
||||
|
||||
```swift
|
||||
DDLog.addLogger(DDTTYLogger.sharedInstance()) // TTY = Xcode console
|
||||
DDLog.addLogger(DDASLLogger.sharedInstance()) // ASL = Apple System Logs
|
||||
|
||||
let fileLogger: DDFileLogger = DDFileLogger() // File Logger
|
||||
fileLogger.rollingFrequency = 60*60*24 // 24 hours
|
||||
fileLogger.logFileManager.maximumNumberOfLogFiles = 7
|
||||
DDLog.addLogger(fileLogger)
|
||||
|
||||
...
|
||||
|
||||
DDLogVerbose("Verbose");
|
||||
DDLogDebug("Debug");
|
||||
DDLogInfo("Info");
|
||||
DDLogWarn("Warn");
|
||||
DDLogError("Error");
|
||||
```
|
||||
|
||||
##### Obj-C version via CocoaPods
|
||||
|
||||
```ruby
|
||||
platform :ios, '7.0'
|
||||
pod 'CocoaLumberjack'
|
||||
```
|
||||
|
||||
##### Objc-C usage
|
||||
If you're using Lumberjack as a framework, you can `@import CocoaLumberjack`.
|
||||
|
||||
Otherwise, `#import <CocoaLumberjack/CocoaLumberjack.h>`
|
||||
|
||||
```objc
|
||||
[DDLog addLogger:[DDTTYLogger sharedInstance]]; // TTY = Xcode console
|
||||
[DDLog addLogger:[DDASLLogger sharedInstance]]; // ASL = Apple System Logs
|
||||
|
||||
DDFileLogger *fileLogger = [[DDFileLogger alloc] init]; // File Logger
|
||||
fileLogger.rollingFrequency = 60 * 60 * 24; // 24 hour rolling
|
||||
fileLogger.logFileManager.maximumNumberOfLogFiles = 7;
|
||||
[DDLog addLogger:fileLogger];
|
||||
|
||||
...
|
||||
|
||||
DDLogVerbose(@"Verbose");
|
||||
DDLogDebug(@"Debug");
|
||||
DDLogInfo(@"Info");
|
||||
DDLogWarn(@"Warn");
|
||||
DDLogError(@"Error");
|
||||
```
|
||||
|
||||
##### Installation with Carthage (iOS 8+)
|
||||
|
||||
[Carthage](https://github.com/Carthage/Carthage) is a lightweight dependency manager for Swift and Objective-C. It leverages CocoaTouch modules and is less invasive than CocoaPods.
|
||||
|
||||
To install with Carthage, follow the instruction on [Carthage](https://github.com/Carthage/Carthage)
|
||||
|
||||
Cartfile
|
||||
```
|
||||
github "CocoaLumberjack/CocoaLumberjack"
|
||||
```
|
||||
|
||||
- or [install manually](Documentation/GettingStarted.md#manual-installation)
|
||||
- read the [Getting started](Documentation/GettingStarted.md) guide, check out the [FAQ](Documentation/FAQ.md) section or the other [docs](Documentation/)
|
||||
- if you find issues or want to suggest improvements, create an issue or a pull request
|
||||
- for all kinds of questions involving CocoaLumberjack, use the [Google group](http://groups.google.com/group/cocoalumberjack) or StackOverflow (use [#lumberjack](http://stackoverflow.com/questions/tagged/lumberjack)).
|
||||
|
||||
### CocoaLumberjack 2
|
||||
|
||||
#### Migrating to 2.x
|
||||
|
||||
* Replace `DDLog.h` imports by `#import <CocoaLumberjack/CocoaLumberjack.h>`.
|
||||
|
||||
Advanced users, third party libraries:
|
||||
|
||||
* Replace all `DDLogC` macros for regular `DDLog` macros.
|
||||
* Replace log level (`LOG_LEVEL_*`) macros with `DDLogLevel` enum values
|
||||
* Replace log flag (`LOG_FLAG_*`) macros with `DDLogFlag` enum values
|
||||
* Replace `DDLogMessage` ivars and method calls to the new ivars and methods
|
||||
* `logMsg` with `_message`
|
||||
* `logLevel` with `_level`
|
||||
* `logFlag` with `_flag`
|
||||
* `logContext` with `_context`
|
||||
* `lineNumber` with `_line` (type changed from `int` to `NSUInteger`)
|
||||
* `file` with `_file` (`filename` contains just the file name, without the extension and the full path)
|
||||
* `timestamp` with `_timestamp`
|
||||
* `methodName` with `function`
|
||||
* Replace `DDAbstractLogger` `formatter` to `logFormatter`
|
||||
* `YSSingleFileLogger` ivars are no longer accesible, use the methods instead
|
||||
* Replace `[DDLog addLogger:withLogLevel:]` with `[DDLog addLogger:withLevel:]`
|
||||
|
||||
#### Forcing 1.x
|
||||
|
||||
If an included library requires it, you can force CocoaLumberjack 1.x by setting the version before the conflicting library:
|
||||
|
||||
```ruby
|
||||
pod 'CocoaLumberjack', '~> 1.9'
|
||||
pod 'ConflictingLibrary'
|
||||
```
|
||||
|
||||
### Features
|
||||
|
||||
#### Lumberjack is Fast & Simple, yet Powerful & Flexible.
|
||||
|
||||
It is similar in concept to other popular logging frameworks such as log4j, yet is designed specifically for Objective-C, and takes advantage of features such as multi-threading, grand central dispatch (if available), lockless atomic operations, and the dynamic nature of the Objective-C runtime.
|
||||
@ -158,30 +39,33 @@ Configure your logging however you want. Change log levels per file (perfect for
|
||||
- You're looking to take your application to the next level in terms of support and stability.
|
||||
- You're looking for an enterprise level logging solution for your application (Mac or iPhone).
|
||||
|
||||
### Documentation
|
||||
### How to get started
|
||||
- install via [CocoaPods](http://cocoapods.org)
|
||||
|
||||
- **[Get started using Lumberjack](Documentation/GettingStarted.md)**<br/>
|
||||
- [Different log levels for Debug and Release builds](Documentation/XcodeTricks.md)<br/>
|
||||
- [Different log levels for each logger](Documentation/PerLoggerLogLevels.md)<br/>
|
||||
- [Use colors in the Xcode debugging console](Documentation/XcodeColors.md)<br/>
|
||||
- [Write your own custom formatters](Documentation/CustomFormatters.md)<br/>
|
||||
- [FAQ](Documentation/FAQ.md)<br/>
|
||||
- [Analysis of performance with benchmarks](Documentation/Performance.md)<br/>
|
||||
- [Common issues you may encounter and their solutions](Documentation/ProblemSolution.md)<br/>
|
||||
- [AppCode support](Documentation/AppCode-support.md)
|
||||
- **[Full Lumberjack documentation](Documentation/)**<br/>
|
||||
```ruby
|
||||
platform :ios, '6.1'
|
||||
pod 'CocoaLumberjack'
|
||||
```
|
||||
- read the [Getting started](https://github.com/CocoaLumberjack/CocoaLumberjack/wiki/GettingStarted) guide, check out the [FAQ](https://github.com/CocoaLumberjack/CocoaLumberjack/wiki/FAQ) section or the other [docs](https://github.com/CocoaLumberjack/CocoaLumberjack/wiki)
|
||||
- if you find issues or want to suggest improvements, create an issue or a pull request
|
||||
- for all kinds of questions involving CocoaLumberjack, use the [Google group](http://groups.google.com/group/cocoalumberjack) or StackOverflow (use [#lumberjack](http://stackoverflow.com/questions/tagged/lumberjack)).
|
||||
|
||||
### Documentation
|
||||
- **[Get started using Lumberjack](https://github.com/CocoaLumberjack/CocoaLumberjack/wiki/GettingStarted)**<br/>
|
||||
- [Different log levels for Debug and Release builds](https://github.com/CocoaLumberjack/CocoaLumberjack/wiki/XcodeTricks)<br/>
|
||||
- [Different log levels for each logger](https://github.com/CocoaLumberjack/CocoaLumberjack/wiki/PerLoggerLogLevels)<br/>
|
||||
- [Use colors in the Xcode debugging console](https://github.com/CocoaLumberjack/CocoaLumberjack/wiki/XcodeColors)<br/>
|
||||
- [Write your own custom formatters](https://github.com/CocoaLumberjack/CocoaLumberjack/wiki/CustomFormatters)<br/>
|
||||
- [FAQ](https://github.com/CocoaLumberjack/CocoaLumberjack/wiki/FAQ)<br/>
|
||||
- [Analysis of performance with benchmarks](https://github.com/CocoaLumberjack/CocoaLumberjack/wiki/Performance)<br/>
|
||||
- [Common issues you may encounter and their solutions](https://github.com/CocoaLumberjack/CocoaLumberjack/wiki/ProblemSolution)<br/>
|
||||
- [AppCode support](https://github.com/CocoaLumberjack/CocoaLumberjack/wiki/AppCode-support)
|
||||
- **[full Lumberjack wiki](https://github.com/CocoaLumberjack/CocoaLumberjack/wiki)**<br/>
|
||||
|
||||
### Requirements
|
||||
The current version of Lumberjack requires:
|
||||
- Xcode 7.1 or later
|
||||
- Xcode 4.4 or later is required
|
||||
- iOS 5 or later
|
||||
- OS X 10.7 or later
|
||||
- WatchOS 2 or later
|
||||
- TVOS 9 or later
|
||||
|
||||
#### Backwards compability
|
||||
- for Xcode 7.0 or earlier, use the 2.1.0 version
|
||||
- for Xcode 6 or earlier, use the 2.0.x version
|
||||
- for OS X < 10.7 support, use the 1.6.0 version
|
||||
|
||||
### Author
|
||||
|
||||
511
Example/Pods/Local Podspecs/PNXMPPFramework.podspec.json
generated
511
Example/Pods/Local Podspecs/PNXMPPFramework.podspec.json
generated
@ -2,13 +2,13 @@
|
||||
"name": "PNXMPPFramework",
|
||||
"version": "0.1.0",
|
||||
"summary": "A short of PNXMPPFramework.",
|
||||
"homepage": "https://github.com/<GITHUB_USERNAME>/PNXMPPFramework",
|
||||
"homepage": "https://github.com/giuseppenucifora/PNXMPPFramework",
|
||||
"license": "MIT",
|
||||
"authors": {
|
||||
"Giuseppe Nucifora": "me@giuseppenucifora.com"
|
||||
},
|
||||
"source": {
|
||||
"git": "https://github.com/<GITHUB_USERNAME>/PNXMPPFramework.git",
|
||||
"git": "https://github.com/giuseppenucifora/PNXMPPFramework.git",
|
||||
"tag": "0.1.0"
|
||||
},
|
||||
"platforms": {
|
||||
@ -16,24 +16,509 @@
|
||||
},
|
||||
"requires_arc": true,
|
||||
"source_files": "Pod/Classes/**/*",
|
||||
"resources": [
|
||||
"**/*.{xcdatamodel,xcdatamodeld}"
|
||||
],
|
||||
"resource_bundles": {
|
||||
"PNXMPPFramework": [
|
||||
"Pod/Assets/*.png"
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"CocoaLumberjack": [
|
||||
"preserve_paths": "PNXMPPFramework/module/module.modulemap",
|
||||
"subspecs": [
|
||||
{
|
||||
"name": "Core",
|
||||
"source_files": [
|
||||
"XMPPFramework.h",
|
||||
"Core/**/*.{h,m}",
|
||||
"Vendor/libidn/*.h",
|
||||
"Authentication/**/*.{h,m}",
|
||||
"Categories/**/*.{h,m}",
|
||||
"Utilities/**/*.{h,m}"
|
||||
],
|
||||
"vendored_libraries": "Vendor/libidn/libidn.a",
|
||||
"libraries": [
|
||||
"xml2",
|
||||
"resolv"
|
||||
],
|
||||
"xcconfig": {
|
||||
"HEADER_SEARCH_PATHS": "$(inherited) $(SDKROOT)/usr/include/libxml2 $(PODS_ROOT)/XMPPFramework/module $(SDKROOT)/usr/include/libresolv",
|
||||
"LIBRARY_SEARCH_PATHS": "\"$(PODS_ROOT)/Vendor/libidn\"",
|
||||
"CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES": "YES",
|
||||
"OTHER_LDFLAGS": "\"-lxml2\"",
|
||||
"ENABLE_BITCODE": "NO"
|
||||
},
|
||||
"dependencies": {
|
||||
"CocoaLumberjack": [
|
||||
"~>1.9"
|
||||
],
|
||||
"CocoaAsyncSocket": [
|
||||
|
||||
],
|
||||
"CocoaAsyncSocket": [
|
||||
],
|
||||
"KissXML": [
|
||||
|
||||
]
|
||||
},
|
||||
"ios": {
|
||||
"dependencies": {
|
||||
"KissXML": [
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Authentication",
|
||||
"dependencies": {
|
||||
"PNXMPPFramework/Core": [
|
||||
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Categories",
|
||||
"dependencies": {
|
||||
"PNXMPPFramework/Core": [
|
||||
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Utilities",
|
||||
"dependencies": {
|
||||
"PNXMPPFramework/Core": [
|
||||
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "BandwidthMonitor",
|
||||
"source_files": "Extensions/BandwidthMonitor/**/*.{h,m}",
|
||||
"dependencies": {
|
||||
"PNXMPPFramework/Core": [
|
||||
|
||||
]
|
||||
},
|
||||
"prefix_header_contents": "#define HAVE_XMPP_SUBSPEC_POD"
|
||||
},
|
||||
{
|
||||
"name": "CoreDataStorage",
|
||||
"source_files": [
|
||||
"Extensions/CoreDataStorage/**/*.{h,m}",
|
||||
"Extensions/XEP-0203/NSXMLElement+XEP_0203.h"
|
||||
],
|
||||
"dependencies": {
|
||||
"PNXMPPFramework/Core": [
|
||||
|
||||
]
|
||||
},
|
||||
"prefix_header_contents": "#define HAVE_XMPP_SUBSPEC_POD",
|
||||
"frameworks": "CoreData"
|
||||
},
|
||||
{
|
||||
"name": "GoogleSharedStatus",
|
||||
"source_files": "Extensions/GoogleSharedStatus/**/*.{h,m}",
|
||||
"dependencies": {
|
||||
"PNXMPPFramework/Core": [
|
||||
|
||||
]
|
||||
},
|
||||
"prefix_header_contents": "#define HAVE_XMPP_SUBSPEC_POD"
|
||||
},
|
||||
{
|
||||
"name": "ProcessOne",
|
||||
"source_files": "Extensions/ProcessOne/**/*.{h,m}",
|
||||
"dependencies": {
|
||||
"PNXMPPFramework/Core": [
|
||||
|
||||
]
|
||||
},
|
||||
"prefix_header_contents": "#define HAVE_XMPP_SUBSPEC_POD"
|
||||
},
|
||||
{
|
||||
"name": "Reconnect",
|
||||
"source_files": "Extensions/Reconnect/**/*.{h,m}",
|
||||
"dependencies": {
|
||||
"PNXMPPFramework/Core": [
|
||||
|
||||
]
|
||||
},
|
||||
"prefix_header_contents": "#define HAVE_XMPP_SUBSPEC_POD",
|
||||
"frameworks": "SystemConfiguration"
|
||||
},
|
||||
{
|
||||
"name": "Roster",
|
||||
"source_files": "Extensions/Roster/**/*.{h,m}",
|
||||
"dependencies": {
|
||||
"PNXMPPFramework/Core": [
|
||||
|
||||
],
|
||||
"PNXMPPFramework/CoreDataStorage": [
|
||||
|
||||
]
|
||||
},
|
||||
"prefix_header_contents": "#define HAVE_XMPP_SUBSPEC_POD"
|
||||
},
|
||||
{
|
||||
"name": "SystemInputActivityMonitor",
|
||||
"source_files": [
|
||||
"Extensions/SystemInputActivityMonitor/**/*.{h,m}",
|
||||
"Utilities/GCDMulticastDelegate.h"
|
||||
],
|
||||
"dependencies": {
|
||||
"PNXMPPFramework/Core": [
|
||||
|
||||
]
|
||||
},
|
||||
"prefix_header_contents": "#define HAVE_XMPP_SUBSPEC_POD"
|
||||
},
|
||||
{
|
||||
"name": "XEP-0009",
|
||||
"source_files": "Extensions/XEP-0009/**/*.{h,m}",
|
||||
"dependencies": {
|
||||
"PNXMPPFramework/Core": [
|
||||
|
||||
]
|
||||
},
|
||||
"prefix_header_contents": "#define HAVE_XMPP_SUBSPEC_POD"
|
||||
},
|
||||
{
|
||||
"name": "XEP-0012",
|
||||
"source_files": "Extensions/XEP-0012/**/*.{h,m}",
|
||||
"dependencies": {
|
||||
"PNXMPPFramework/Core": [
|
||||
|
||||
]
|
||||
},
|
||||
"prefix_header_contents": "#define HAVE_XMPP_SUBSPEC_POD"
|
||||
},
|
||||
{
|
||||
"name": "XEP-0016",
|
||||
"source_files": "Extensions/XEP-0016/**/*.{h,m}",
|
||||
"dependencies": {
|
||||
"PNXMPPFramework/Core": [
|
||||
|
||||
]
|
||||
},
|
||||
"prefix_header_contents": "#define HAVE_XMPP_SUBSPEC_POD"
|
||||
},
|
||||
{
|
||||
"name": "XEP-0045",
|
||||
"source_files": "Extensions/XEP-0045/**/*.{h,m}",
|
||||
"dependencies": {
|
||||
"PNXMPPFramework/Core": [
|
||||
|
||||
],
|
||||
"PNXMPPFramework/CoreDataStorage": [
|
||||
|
||||
],
|
||||
"PNXMPPFramework/XEP-0203": [
|
||||
|
||||
]
|
||||
},
|
||||
"prefix_header_contents": "#define HAVE_XMPP_SUBSPEC_POD"
|
||||
},
|
||||
{
|
||||
"name": "XEP-0054",
|
||||
"source_files": [
|
||||
"Extensions/XEP-0054/**/*.{h,m}",
|
||||
"Extensions/XEP-0153/XMPPvCardAvatarModule.h",
|
||||
"Extensions/XEP-0082/XMPPDateTimeProfiles.h",
|
||||
"Extensions/XEP-0082/NSDate+XMPPDateTimeProfiles.h"
|
||||
],
|
||||
"dependencies": {
|
||||
"PNXMPPFramework/Core": [
|
||||
|
||||
],
|
||||
"PNXMPPFramework/Roster": [
|
||||
|
||||
]
|
||||
},
|
||||
"prefix_header_contents": "#define HAVE_XMPP_SUBSPEC_POD",
|
||||
"frameworks": "CoreLocation"
|
||||
},
|
||||
{
|
||||
"name": "XEP-0059",
|
||||
"source_files": "Extensions/XEP-0059/**/*.{h,m}",
|
||||
"dependencies": {
|
||||
"PNXMPPFramework/Core": [
|
||||
|
||||
]
|
||||
},
|
||||
"prefix_header_contents": "#define HAVE_XMPP_SUBSPEC_POD"
|
||||
},
|
||||
{
|
||||
"name": "XEP-0060",
|
||||
"source_files": "Extensions/XEP-0060/**/*.{h,m}",
|
||||
"dependencies": {
|
||||
"PNXMPPFramework/Core": [
|
||||
|
||||
]
|
||||
},
|
||||
"prefix_header_contents": "#define HAVE_XMPP_SUBSPEC_POD"
|
||||
},
|
||||
{
|
||||
"name": "XEP-0065",
|
||||
"source_files": "Extensions/XEP-0065/**/*.{h,m}",
|
||||
"dependencies": {
|
||||
"PNXMPPFramework/Core": [
|
||||
|
||||
]
|
||||
},
|
||||
"prefix_header_contents": "#define HAVE_XMPP_SUBSPEC_POD"
|
||||
},
|
||||
{
|
||||
"name": "XEP-0066",
|
||||
"source_files": "Extensions/XEP-0066/**/*.{h,m}",
|
||||
"dependencies": {
|
||||
"PNXMPPFramework/Core": [
|
||||
|
||||
]
|
||||
},
|
||||
"prefix_header_contents": "#define HAVE_XMPP_SUBSPEC_POD"
|
||||
},
|
||||
{
|
||||
"name": "XEP-0082",
|
||||
"source_files": "Extensions/XEP-0082/**/*.{h,m}",
|
||||
"dependencies": {
|
||||
"PNXMPPFramework/Core": [
|
||||
|
||||
]
|
||||
},
|
||||
"prefix_header_contents": "#define HAVE_XMPP_SUBSPEC_POD"
|
||||
},
|
||||
{
|
||||
"name": "XEP-0085",
|
||||
"source_files": "Extensions/XEP-0085/**/*.{h,m}",
|
||||
"dependencies": {
|
||||
"PNXMPPFramework/Core": [
|
||||
|
||||
]
|
||||
},
|
||||
"prefix_header_contents": "#define HAVE_XMPP_SUBSPEC_POD"
|
||||
},
|
||||
{
|
||||
"name": "XEP-0092",
|
||||
"source_files": "Extensions/XEP-0092/**/*.{h,m}",
|
||||
"dependencies": {
|
||||
"PNXMPPFramework/Core": [
|
||||
|
||||
]
|
||||
},
|
||||
"prefix_header_contents": "#define HAVE_XMPP_SUBSPEC_POD"
|
||||
},
|
||||
{
|
||||
"name": "XEP-0100",
|
||||
"source_files": "Extensions/XEP-0100/**/*.{h,m}",
|
||||
"dependencies": {
|
||||
"PNXMPPFramework/Core": [
|
||||
|
||||
]
|
||||
},
|
||||
"prefix_header_contents": "#define HAVE_XMPP_SUBSPEC_POD"
|
||||
},
|
||||
{
|
||||
"name": "XEP-0106",
|
||||
"source_files": "Extensions/XEP-0106/**/*.{h,m}",
|
||||
"dependencies": {
|
||||
"PNXMPPFramework/Core": [
|
||||
|
||||
]
|
||||
},
|
||||
"prefix_header_contents": "#define HAVE_XMPP_SUBSPEC_POD"
|
||||
},
|
||||
{
|
||||
"name": "XEP-0115",
|
||||
"source_files": "Extensions/XEP-0115/**/*.{h,m}",
|
||||
"dependencies": {
|
||||
"PNXMPPFramework/Core": [
|
||||
|
||||
],
|
||||
"PNXMPPFramework/CoreDataStorage": [
|
||||
|
||||
]
|
||||
},
|
||||
"prefix_header_contents": "#define HAVE_XMPP_SUBSPEC_POD"
|
||||
},
|
||||
{
|
||||
"name": "XEP-0136",
|
||||
"source_files": "Extensions/XEP-0136/**/*.{h,m}",
|
||||
"dependencies": {
|
||||
"PNXMPPFramework/CoreDataStorage": [
|
||||
|
||||
],
|
||||
"PNXMPPFramework/XEP-0203": [
|
||||
|
||||
],
|
||||
"PNXMPPFramework/XEP-0085": [
|
||||
|
||||
]
|
||||
},
|
||||
"prefix_header_contents": "#define HAVE_XMPP_SUBSPEC_POD"
|
||||
},
|
||||
{
|
||||
"name": "XEP-0153",
|
||||
"source_files": [
|
||||
"Extensions/XEP-0153/**/*.{h,m}",
|
||||
"Extensions/XEP-0082/NSDate+XMPPDateTimeProfiles.h"
|
||||
],
|
||||
"dependencies": {
|
||||
"PNXMPPFramework/Core": [
|
||||
|
||||
],
|
||||
"PNXMPPFramework/XEP-0054": [
|
||||
|
||||
]
|
||||
},
|
||||
"prefix_header_contents": "#define HAVE_XMPP_SUBSPEC_POD"
|
||||
},
|
||||
{
|
||||
"name": "XEP-0172",
|
||||
"source_files": "Extensions/XEP-0172/**/*.{h,m}",
|
||||
"dependencies": {
|
||||
"PNXMPPFramework/Core": [
|
||||
|
||||
]
|
||||
},
|
||||
"prefix_header_contents": "#define HAVE_XMPP_SUBSPEC_POD"
|
||||
},
|
||||
{
|
||||
"name": "XEP-0184",
|
||||
"source_files": "Extensions/XEP-0184/**/*.{h,m}",
|
||||
"dependencies": {
|
||||
"PNXMPPFramework/Core": [
|
||||
|
||||
]
|
||||
},
|
||||
"prefix_header_contents": "#define HAVE_XMPP_SUBSPEC_POD"
|
||||
},
|
||||
{
|
||||
"name": "XEP-0191",
|
||||
"source_files": "Extensions/XEP-0191/**/*.{h,m}",
|
||||
"dependencies": {
|
||||
"PNXMPPFramework/Core": [
|
||||
|
||||
]
|
||||
},
|
||||
"prefix_header_contents": "#define HAVE_XMPP_SUBSPEC_POD"
|
||||
},
|
||||
{
|
||||
"name": "XEP-0198",
|
||||
"source_files": "Extensions/XEP-0198/**/*.{h,m}",
|
||||
"dependencies": {
|
||||
"PNXMPPFramework/Core": [
|
||||
|
||||
]
|
||||
},
|
||||
"prefix_header_contents": "#define HAVE_XMPP_SUBSPEC_POD"
|
||||
},
|
||||
{
|
||||
"name": "XEP-0199",
|
||||
"source_files": "Extensions/XEP-0199/**/*.{h,m}",
|
||||
"dependencies": {
|
||||
"PNXMPPFramework/Core": [
|
||||
|
||||
]
|
||||
},
|
||||
"prefix_header_contents": "#define HAVE_XMPP_SUBSPEC_POD"
|
||||
},
|
||||
{
|
||||
"name": "XEP-0202",
|
||||
"source_files": "Extensions/XEP-0202/**/*.{h,m}",
|
||||
"dependencies": {
|
||||
"PNXMPPFramework/Core": [
|
||||
|
||||
],
|
||||
"PNXMPPFramework/XEP-0082": [
|
||||
|
||||
]
|
||||
},
|
||||
"prefix_header_contents": "#define HAVE_XMPP_SUBSPEC_POD"
|
||||
},
|
||||
{
|
||||
"name": "XEP-0203",
|
||||
"source_files": "Extensions/XEP-0203/**/*.{h,m}",
|
||||
"dependencies": {
|
||||
"PNXMPPFramework/Core": [
|
||||
|
||||
],
|
||||
"PNXMPPFramework/XEP-0082": [
|
||||
|
||||
]
|
||||
},
|
||||
"prefix_header_contents": "#define HAVE_XMPP_SUBSPEC_POD"
|
||||
},
|
||||
{
|
||||
"name": "XEP-0223",
|
||||
"source_files": "Extensions/XEP-0223/**/*.{h,m}",
|
||||
"dependencies": {
|
||||
"PNXMPPFramework/Core": [
|
||||
|
||||
]
|
||||
},
|
||||
"prefix_header_contents": "#define HAVE_XMPP_SUBSPEC_POD"
|
||||
},
|
||||
{
|
||||
"name": "XEP-0224",
|
||||
"source_files": "Extensions/XEP-0224/**/*.{h,m}",
|
||||
"dependencies": {
|
||||
"PNXMPPFramework/Core": [
|
||||
|
||||
]
|
||||
},
|
||||
"prefix_header_contents": "#define HAVE_XMPP_SUBSPEC_POD"
|
||||
},
|
||||
{
|
||||
"name": "XEP-0280",
|
||||
"source_files": [
|
||||
"Extensions/XEP-0280/**/*.{h,m}",
|
||||
"Extensions/XEP-0297/NSXMLElement+XEP_0297.h"
|
||||
],
|
||||
"dependencies": {
|
||||
"PNXMPPFramework/Core": [
|
||||
|
||||
]
|
||||
},
|
||||
"prefix_header_contents": "#define HAVE_XMPP_SUBSPEC_POD"
|
||||
},
|
||||
{
|
||||
"name": "XEP-0297",
|
||||
"source_files": [
|
||||
"Extensions/XEP-0297/**/*.{h,m}",
|
||||
"Extensions/XEP-0203/**/*.h"
|
||||
],
|
||||
"dependencies": {
|
||||
"PNXMPPFramework/Core": [
|
||||
|
||||
],
|
||||
"PNXMPPFramework/XEP-0203": [
|
||||
|
||||
]
|
||||
},
|
||||
"prefix_header_contents": "#define HAVE_XMPP_SUBSPEC_POD"
|
||||
},
|
||||
{
|
||||
"name": "XEP-0308",
|
||||
"source_files": "Extensions/XEP-0308/**/*.{h,m}",
|
||||
"dependencies": {
|
||||
"PNXMPPFramework/Core": [
|
||||
|
||||
]
|
||||
},
|
||||
"prefix_header_contents": "#define HAVE_XMPP_SUBSPEC_POD"
|
||||
},
|
||||
{
|
||||
"name": "XEP-0333",
|
||||
"source_files": "Extensions/XEP-0333/**/*.{h,m}",
|
||||
"dependencies": {
|
||||
"PNXMPPFramework/Core": [
|
||||
|
||||
]
|
||||
},
|
||||
"prefix_header_contents": "#define HAVE_XMPP_SUBSPEC_POD"
|
||||
},
|
||||
{
|
||||
"name": "XEP-0335",
|
||||
"source_files": "Extensions/XEP-0335/**/*.{h,m}",
|
||||
"dependencies": {
|
||||
"PNXMPPFramework/Core": [
|
||||
|
||||
]
|
||||
},
|
||||
"prefix_header_contents": "#define HAVE_XMPP_SUBSPEC_POD"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
153
Example/Pods/Manifest.lock
generated
153
Example/Pods/Manifest.lock
generated
@ -6,14 +6,11 @@ PODS:
|
||||
- CocoaAsyncSocket/RunLoop
|
||||
- CocoaAsyncSocket/GCD (7.4.3)
|
||||
- CocoaAsyncSocket/RunLoop (7.4.3)
|
||||
- CocoaLumberjack (2.2.0):
|
||||
- CocoaLumberjack/Default (= 2.2.0)
|
||||
- CocoaLumberjack/Extensions (= 2.2.0)
|
||||
- CocoaLumberjack/Core (2.2.0)
|
||||
- CocoaLumberjack/Default (2.2.0):
|
||||
- CocoaLumberjack (1.9.2):
|
||||
- CocoaLumberjack/Extensions (= 1.9.2)
|
||||
- CocoaLumberjack/Core (1.9.2)
|
||||
- CocoaLumberjack/Extensions (1.9.2):
|
||||
- CocoaLumberjack/Core
|
||||
- CocoaLumberjack/Extensions (2.2.0):
|
||||
- CocoaLumberjack/Default
|
||||
- FBSnapshotTestCase (2.0.7):
|
||||
- FBSnapshotTestCase/SwiftSupport (= 2.0.7)
|
||||
- FBSnapshotTestCase/Core (2.0.7)
|
||||
@ -25,9 +22,145 @@ PODS:
|
||||
- KissXML/Standard (5.0.3):
|
||||
- KissXML/Core
|
||||
- PNXMPPFramework (0.1.0):
|
||||
- PNXMPPFramework/Authentication (= 0.1.0)
|
||||
- PNXMPPFramework/BandwidthMonitor (= 0.1.0)
|
||||
- PNXMPPFramework/Categories (= 0.1.0)
|
||||
- PNXMPPFramework/Core (= 0.1.0)
|
||||
- PNXMPPFramework/CoreDataStorage (= 0.1.0)
|
||||
- PNXMPPFramework/GoogleSharedStatus (= 0.1.0)
|
||||
- PNXMPPFramework/ProcessOne (= 0.1.0)
|
||||
- PNXMPPFramework/Reconnect (= 0.1.0)
|
||||
- PNXMPPFramework/Roster (= 0.1.0)
|
||||
- PNXMPPFramework/SystemInputActivityMonitor (= 0.1.0)
|
||||
- PNXMPPFramework/Utilities (= 0.1.0)
|
||||
- PNXMPPFramework/XEP-0009 (= 0.1.0)
|
||||
- PNXMPPFramework/XEP-0012 (= 0.1.0)
|
||||
- PNXMPPFramework/XEP-0016 (= 0.1.0)
|
||||
- PNXMPPFramework/XEP-0045 (= 0.1.0)
|
||||
- PNXMPPFramework/XEP-0054 (= 0.1.0)
|
||||
- PNXMPPFramework/XEP-0059 (= 0.1.0)
|
||||
- PNXMPPFramework/XEP-0060 (= 0.1.0)
|
||||
- PNXMPPFramework/XEP-0065 (= 0.1.0)
|
||||
- PNXMPPFramework/XEP-0066 (= 0.1.0)
|
||||
- PNXMPPFramework/XEP-0082 (= 0.1.0)
|
||||
- PNXMPPFramework/XEP-0085 (= 0.1.0)
|
||||
- PNXMPPFramework/XEP-0092 (= 0.1.0)
|
||||
- PNXMPPFramework/XEP-0100 (= 0.1.0)
|
||||
- PNXMPPFramework/XEP-0106 (= 0.1.0)
|
||||
- PNXMPPFramework/XEP-0115 (= 0.1.0)
|
||||
- PNXMPPFramework/XEP-0136 (= 0.1.0)
|
||||
- PNXMPPFramework/XEP-0153 (= 0.1.0)
|
||||
- PNXMPPFramework/XEP-0172 (= 0.1.0)
|
||||
- PNXMPPFramework/XEP-0184 (= 0.1.0)
|
||||
- PNXMPPFramework/XEP-0191 (= 0.1.0)
|
||||
- PNXMPPFramework/XEP-0198 (= 0.1.0)
|
||||
- PNXMPPFramework/XEP-0199 (= 0.1.0)
|
||||
- PNXMPPFramework/XEP-0202 (= 0.1.0)
|
||||
- PNXMPPFramework/XEP-0203 (= 0.1.0)
|
||||
- PNXMPPFramework/XEP-0223 (= 0.1.0)
|
||||
- PNXMPPFramework/XEP-0224 (= 0.1.0)
|
||||
- PNXMPPFramework/XEP-0280 (= 0.1.0)
|
||||
- PNXMPPFramework/XEP-0297 (= 0.1.0)
|
||||
- PNXMPPFramework/XEP-0308 (= 0.1.0)
|
||||
- PNXMPPFramework/XEP-0333 (= 0.1.0)
|
||||
- PNXMPPFramework/XEP-0335 (= 0.1.0)
|
||||
- PNXMPPFramework/Authentication (0.1.0):
|
||||
- PNXMPPFramework/Core
|
||||
- PNXMPPFramework/BandwidthMonitor (0.1.0):
|
||||
- PNXMPPFramework/Core
|
||||
- PNXMPPFramework/Categories (0.1.0):
|
||||
- PNXMPPFramework/Core
|
||||
- PNXMPPFramework/Core (0.1.0):
|
||||
- CocoaAsyncSocket
|
||||
- CocoaLumberjack
|
||||
- CocoaLumberjack (~> 1.9)
|
||||
- KissXML
|
||||
- PNXMPPFramework/CoreDataStorage (0.1.0):
|
||||
- PNXMPPFramework/Core
|
||||
- PNXMPPFramework/GoogleSharedStatus (0.1.0):
|
||||
- PNXMPPFramework/Core
|
||||
- PNXMPPFramework/ProcessOne (0.1.0):
|
||||
- PNXMPPFramework/Core
|
||||
- PNXMPPFramework/Reconnect (0.1.0):
|
||||
- PNXMPPFramework/Core
|
||||
- PNXMPPFramework/Roster (0.1.0):
|
||||
- PNXMPPFramework/Core
|
||||
- PNXMPPFramework/CoreDataStorage
|
||||
- PNXMPPFramework/SystemInputActivityMonitor (0.1.0):
|
||||
- PNXMPPFramework/Core
|
||||
- PNXMPPFramework/Utilities (0.1.0):
|
||||
- PNXMPPFramework/Core
|
||||
- PNXMPPFramework/XEP-0009 (0.1.0):
|
||||
- PNXMPPFramework/Core
|
||||
- PNXMPPFramework/XEP-0012 (0.1.0):
|
||||
- PNXMPPFramework/Core
|
||||
- PNXMPPFramework/XEP-0016 (0.1.0):
|
||||
- PNXMPPFramework/Core
|
||||
- PNXMPPFramework/XEP-0045 (0.1.0):
|
||||
- PNXMPPFramework/Core
|
||||
- PNXMPPFramework/CoreDataStorage
|
||||
- PNXMPPFramework/XEP-0203
|
||||
- PNXMPPFramework/XEP-0054 (0.1.0):
|
||||
- PNXMPPFramework/Core
|
||||
- PNXMPPFramework/Roster
|
||||
- PNXMPPFramework/XEP-0059 (0.1.0):
|
||||
- PNXMPPFramework/Core
|
||||
- PNXMPPFramework/XEP-0060 (0.1.0):
|
||||
- PNXMPPFramework/Core
|
||||
- PNXMPPFramework/XEP-0065 (0.1.0):
|
||||
- PNXMPPFramework/Core
|
||||
- PNXMPPFramework/XEP-0066 (0.1.0):
|
||||
- PNXMPPFramework/Core
|
||||
- PNXMPPFramework/XEP-0082 (0.1.0):
|
||||
- PNXMPPFramework/Core
|
||||
- PNXMPPFramework/XEP-0085 (0.1.0):
|
||||
- PNXMPPFramework/Core
|
||||
- PNXMPPFramework/XEP-0092 (0.1.0):
|
||||
- PNXMPPFramework/Core
|
||||
- PNXMPPFramework/XEP-0100 (0.1.0):
|
||||
- PNXMPPFramework/Core
|
||||
- PNXMPPFramework/XEP-0106 (0.1.0):
|
||||
- PNXMPPFramework/Core
|
||||
- PNXMPPFramework/XEP-0115 (0.1.0):
|
||||
- PNXMPPFramework/Core
|
||||
- PNXMPPFramework/CoreDataStorage
|
||||
- PNXMPPFramework/XEP-0136 (0.1.0):
|
||||
- PNXMPPFramework/CoreDataStorage
|
||||
- PNXMPPFramework/XEP-0085
|
||||
- PNXMPPFramework/XEP-0203
|
||||
- PNXMPPFramework/XEP-0153 (0.1.0):
|
||||
- PNXMPPFramework/Core
|
||||
- PNXMPPFramework/XEP-0054
|
||||
- PNXMPPFramework/XEP-0172 (0.1.0):
|
||||
- PNXMPPFramework/Core
|
||||
- PNXMPPFramework/XEP-0184 (0.1.0):
|
||||
- PNXMPPFramework/Core
|
||||
- PNXMPPFramework/XEP-0191 (0.1.0):
|
||||
- PNXMPPFramework/Core
|
||||
- PNXMPPFramework/XEP-0198 (0.1.0):
|
||||
- PNXMPPFramework/Core
|
||||
- PNXMPPFramework/XEP-0199 (0.1.0):
|
||||
- PNXMPPFramework/Core
|
||||
- PNXMPPFramework/XEP-0202 (0.1.0):
|
||||
- PNXMPPFramework/Core
|
||||
- PNXMPPFramework/XEP-0082
|
||||
- PNXMPPFramework/XEP-0203 (0.1.0):
|
||||
- PNXMPPFramework/Core
|
||||
- PNXMPPFramework/XEP-0082
|
||||
- PNXMPPFramework/XEP-0223 (0.1.0):
|
||||
- PNXMPPFramework/Core
|
||||
- PNXMPPFramework/XEP-0224 (0.1.0):
|
||||
- PNXMPPFramework/Core
|
||||
- PNXMPPFramework/XEP-0280 (0.1.0):
|
||||
- PNXMPPFramework/Core
|
||||
- PNXMPPFramework/XEP-0297 (0.1.0):
|
||||
- PNXMPPFramework/Core
|
||||
- PNXMPPFramework/XEP-0203
|
||||
- PNXMPPFramework/XEP-0308 (0.1.0):
|
||||
- PNXMPPFramework/Core
|
||||
- PNXMPPFramework/XEP-0333 (0.1.0):
|
||||
- PNXMPPFramework/Core
|
||||
- PNXMPPFramework/XEP-0335 (0.1.0):
|
||||
- PNXMPPFramework/Core
|
||||
|
||||
DEPENDENCIES:
|
||||
- FBSnapshotTestCase
|
||||
@ -39,10 +172,10 @@ EXTERNAL SOURCES:
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
CocoaAsyncSocket: a18c75dca4b08723628a0bacca6e94803d90be91
|
||||
CocoaLumberjack: 17fe8581f84914d5d7e6360f7c70022b173c3ae0
|
||||
CocoaLumberjack: 628fca2e88ef06f7cf6817309aa405f325d9a6fa
|
||||
FBSnapshotTestCase: 7e85180d0d141a0cf472352edda7e80d7eaeb547
|
||||
KissXML: d19dd6dc65e0dc721ba92b3077b8ebdd240f1c1e
|
||||
PNXMPPFramework: 85a177de196fd742392f6ed0053c9cd2dd160f06
|
||||
PNXMPPFramework: 69989a4b1f7470763acd3e9a3d982ddfa5c161df
|
||||
|
||||
PODFILE CHECKSUM: c24dacdc80a49fe0e0fea049a6d762eb76667498
|
||||
|
||||
|
||||
3899
Example/Pods/Pods.xcodeproj/project.pbxproj
generated
3899
Example/Pods/Pods.xcodeproj/project.pbxproj
generated
File diff suppressed because it is too large
Load Diff
@ -14,7 +14,7 @@
|
||||
buildForArchiving = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = 'primary'
|
||||
BlueprintIdentifier = '5A35D899250B8C7553F3AC9DE053FA3F'
|
||||
BlueprintIdentifier = '188DC26053F39001ADD409E9BD3B63A8'
|
||||
BlueprintName = 'PNXMPPFramework'
|
||||
ReferencedContainer = 'container:Pods.xcodeproj'
|
||||
BuildableName = 'PNXMPPFramework.framework'>
|
||||
|
||||
28
Example/Pods/Target Support Files/CocoaLumberjack/CocoaLumberjack-umbrella.h
generated
Normal file
28
Example/Pods/Target Support Files/CocoaLumberjack/CocoaLumberjack-umbrella.h
generated
Normal file
@ -0,0 +1,28 @@
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#import "DDAbstractDatabaseLogger.h"
|
||||
#import "DDASLLogCapture.h"
|
||||
#import "DDASLLogger.h"
|
||||
#import "DDAssert.h"
|
||||
#import "DDFileLogger.h"
|
||||
#import "DDLog+LOGV.h"
|
||||
#import "DDLog.h"
|
||||
#import "DDTTYLogger.h"
|
||||
#import "DDContextFilterLogFormatter.h"
|
||||
#import "DDDispatchQueueLogFormatter.h"
|
||||
#import "DDMultiFormatter.h"
|
||||
#import "DDAbstractDatabaseLogger.h"
|
||||
#import "DDASLLogCapture.h"
|
||||
#import "DDASLLogger.h"
|
||||
#import "DDAssert.h"
|
||||
#import "DDFileLogger.h"
|
||||
#import "DDLog+LOGV.h"
|
||||
#import "DDLog.h"
|
||||
#import "DDTTYLogger.h"
|
||||
#import "DDContextFilterLogFormatter.h"
|
||||
#import "DDDispatchQueueLogFormatter.h"
|
||||
#import "DDMultiFormatter.h"
|
||||
|
||||
FOUNDATION_EXPORT double CocoaLumberjackVersionNumber;
|
||||
FOUNDATION_EXPORT const unsigned char CocoaLumberjackVersionString[];
|
||||
|
||||
@ -1,36 +1,6 @@
|
||||
framework module CocoaLumberjack {
|
||||
umbrella header "CocoaLumberjack.h"
|
||||
|
||||
export *
|
||||
module * { export * }
|
||||
|
||||
textual header "DDLogMacros.h"
|
||||
|
||||
exclude header "DDLog+LOGV.h"
|
||||
exclude header "DDLegacyMacros.h"
|
||||
|
||||
explicit module DDContextFilterLogFormatter {
|
||||
header "DDContextFilterLogFormatter.h"
|
||||
export *
|
||||
}
|
||||
|
||||
explicit module DDDispatchQueueLogFormatter {
|
||||
header "DDDispatchQueueLogFormatter.h"
|
||||
export *
|
||||
}
|
||||
|
||||
explicit module DDMultiFormatter {
|
||||
header "DDMultiFormatter.h"
|
||||
export *
|
||||
}
|
||||
|
||||
explicit module DDASLLogCapture {
|
||||
header "DDASLLogCapture.h"
|
||||
export *
|
||||
}
|
||||
|
||||
explicit module DDAbstractDatabaseLogger {
|
||||
header "DDAbstractDatabaseLogger.h"
|
||||
export *
|
||||
}
|
||||
umbrella header "CocoaLumberjack-umbrella.h"
|
||||
|
||||
export *
|
||||
module * { export * }
|
||||
}
|
||||
|
||||
@ -15,7 +15,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>2.2.0</string>
|
||||
<string>1.9.2</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
|
||||
@ -2,3 +2,4 @@
|
||||
#import <UIKit/UIKit.h>
|
||||
#endif
|
||||
|
||||
#define HAVE_XMPP_SUBSPEC_POD
|
||||
|
||||
@ -1,5 +1,145 @@
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#import "XMPPBandwidthMonitor.h"
|
||||
#import "XMPPFramework.h"
|
||||
#import "XMPP.h"
|
||||
#import "XMPPConstants.h"
|
||||
#import "XMPPElement.h"
|
||||
#import "XMPPInternal.h"
|
||||
#import "XMPPIQ.h"
|
||||
#import "XMPPJID.h"
|
||||
#import "XMPPLogging.h"
|
||||
#import "XMPPMessage.h"
|
||||
#import "XMPPModule.h"
|
||||
#import "XMPPParser.h"
|
||||
#import "XMPPPresence.h"
|
||||
#import "XMPPStream.h"
|
||||
#import "idn-int.h"
|
||||
#import "stringprep.h"
|
||||
#import "XMPPAnonymousAuthentication.h"
|
||||
#import "XMPPDeprecatedDigestAuthentication.h"
|
||||
#import "XMPPDeprecatedPlainAuthentication.h"
|
||||
#import "XMPPDigestMD5Authentication.h"
|
||||
#import "XMPPPlainAuthentication.h"
|
||||
#import "XMPPSCRAMSHA1Authentication.h"
|
||||
#import "XMPPXFacebookPlatformAuthentication.h"
|
||||
#import "XMPPXOAuth2Google.h"
|
||||
#import "XMPPCustomBinding.h"
|
||||
#import "XMPPSASLAuthentication.h"
|
||||
#import "NSData+XMPP.h"
|
||||
#import "NSNumber+XMPP.h"
|
||||
#import "NSXMLElement+XMPP.h"
|
||||
#import "DDList.h"
|
||||
#import "GCDMulticastDelegate.h"
|
||||
#import "RFImageToDataTransformer.h"
|
||||
#import "XMPPIDTracker.h"
|
||||
#import "XMPPSRVResolver.h"
|
||||
#import "XMPPStringPrep.h"
|
||||
#import "XMPPTimer.h"
|
||||
#import "XMPPCoreDataStorage.h"
|
||||
#import "XMPPCoreDataStorageProtected.h"
|
||||
#import "NSXMLElement+XEP_0203.h"
|
||||
#import "XMPPGoogleSharedStatus.h"
|
||||
#import "XMPPProcessOne.h"
|
||||
#import "XMPPReconnect.h"
|
||||
#import "XMPPGroupCoreDataStorageObject.h"
|
||||
#import "XMPPResourceCoreDataStorageObject.h"
|
||||
#import "XMPPRosterCoreDataStorage.h"
|
||||
#import "XMPPUserCoreDataStorageObject.h"
|
||||
#import "XMPPResourceMemoryStorageObject.h"
|
||||
#import "XMPPRosterMemoryStorage.h"
|
||||
#import "XMPPRosterMemoryStoragePrivate.h"
|
||||
#import "XMPPUserMemoryStorageObject.h"
|
||||
#import "XMPPResource.h"
|
||||
#import "XMPPRoster.h"
|
||||
#import "XMPPRosterPrivate.h"
|
||||
#import "XMPPUser.h"
|
||||
#import "XMPPSystemInputActivityMonitor.h"
|
||||
#import "GCDMulticastDelegate.h"
|
||||
#import "XMPPIQ+JabberRPC.h"
|
||||
#import "XMPPIQ+JabberRPCResonse.h"
|
||||
#import "XMPPJabberRPCModule.h"
|
||||
#import "XMPPIQ+LastActivity.h"
|
||||
#import "XMPPLastActivity.h"
|
||||
#import "XMPPPrivacy.h"
|
||||
#import "XMPPRoomCoreDataStorage.h"
|
||||
#import "XMPPRoomMessageCoreDataStorageObject.h"
|
||||
#import "XMPPRoomOccupantCoreDataStorageObject.h"
|
||||
#import "XMPPRoomHybridStorage.h"
|
||||
#import "XMPPRoomHybridStorageProtected.h"
|
||||
#import "XMPPRoomMessageHybridCoreDataStorageObject.h"
|
||||
#import "XMPPRoomOccupantHybridMemoryStorageObject.h"
|
||||
#import "XMPPRoomMemoryStorage.h"
|
||||
#import "XMPPRoomMessageMemoryStorageObject.h"
|
||||
#import "XMPPRoomOccupantMemoryStorageObject.h"
|
||||
#import "XMPPMessage+XEP0045.h"
|
||||
#import "XMPPMUC.h"
|
||||
#import "XMPPRoom.h"
|
||||
#import "XMPPRoomMessage.h"
|
||||
#import "XMPPRoomOccupant.h"
|
||||
#import "XMPPRoomPrivate.h"
|
||||
#import "XMPPvCardAvatarCoreDataStorageObject.h"
|
||||
#import "XMPPvCardCoreDataStorage.h"
|
||||
#import "XMPPvCardCoreDataStorageObject.h"
|
||||
#import "XMPPvCardTempCoreDataStorageObject.h"
|
||||
#import "XMPPvCardTemp.h"
|
||||
#import "XMPPvCardTempAdr.h"
|
||||
#import "XMPPvCardTempAdrTypes.h"
|
||||
#import "XMPPvCardTempBase.h"
|
||||
#import "XMPPvCardTempEmail.h"
|
||||
#import "XMPPvCardTempLabel.h"
|
||||
#import "XMPPvCardTempModule.h"
|
||||
#import "XMPPvCardTempTel.h"
|
||||
#import "XMPPvCardAvatarModule.h"
|
||||
#import "XMPPDateTimeProfiles.h"
|
||||
#import "NSDate+XMPPDateTimeProfiles.h"
|
||||
#import "NSXMLElement+XEP_0059.h"
|
||||
#import "XMPPResultSet.h"
|
||||
#import "XMPPIQ+XEP_0060.h"
|
||||
#import "XMPPPubSub.h"
|
||||
#import "TURNSocket.h"
|
||||
#import "XMPPIQ+XEP_0066.h"
|
||||
#import "XMPPMessage+XEP_0066.h"
|
||||
#import "NSDate+XMPPDateTimeProfiles.h"
|
||||
#import "XMPPDateTimeProfiles.h"
|
||||
#import "XMPPMessage+XEP_0085.h"
|
||||
#import "XMPPSoftwareVersion.h"
|
||||
#import "XMPPTransports.h"
|
||||
#import "NSString+XEP_0106.h"
|
||||
#import "XMPPCapabilitiesCoreDataStorage.h"
|
||||
#import "XMPPCapsCoreDataStorageObject.h"
|
||||
#import "XMPPCapsResourceCoreDataStorageObject.h"
|
||||
#import "XMPPCapabilities.h"
|
||||
#import "XMPPMessageArchiving_Contact_CoreDataObject.h"
|
||||
#import "XMPPMessageArchiving_Message_CoreDataObject.h"
|
||||
#import "XMPPMessageArchivingCoreDataStorage.h"
|
||||
#import "XMPPMessageArchiving.h"
|
||||
#import "XMPPvCardAvatarModule.h"
|
||||
#import "NSDate+XMPPDateTimeProfiles.h"
|
||||
#import "XMPPMessage+XEP_0172.h"
|
||||
#import "XMPPPresence+XEP_0172.h"
|
||||
#import "XMPPMessage+XEP_0184.h"
|
||||
#import "XMPPMessageDeliveryReceipts.h"
|
||||
#import "XMPPBlocking.h"
|
||||
#import "XMPPStreamManagementMemoryStorage.h"
|
||||
#import "XMPPStreamManagementStanzas.h"
|
||||
#import "XMPPStreamManagement.h"
|
||||
#import "XMPPAutoPing.h"
|
||||
#import "XMPPPing.h"
|
||||
#import "XMPPAutoTime.h"
|
||||
#import "XMPPTime.h"
|
||||
#import "NSXMLElement+XEP_0203.h"
|
||||
#import "XEP_0223.h"
|
||||
#import "XMPPAttentionModule.h"
|
||||
#import "XMPPMessage+XEP_0224.h"
|
||||
#import "XMPPMessage+XEP_0280.h"
|
||||
#import "XMPPMessageCarbons.h"
|
||||
#import "NSXMLElement+XEP_0297.h"
|
||||
#import "NSXMLElement+XEP_0297.h"
|
||||
#import "NSXMLElement+XEP_0203.h"
|
||||
#import "XMPPMessage+XEP_0308.h"
|
||||
#import "XMPPMessage+XEP_0333.h"
|
||||
#import "NSXMLElement+XEP_0335.h"
|
||||
|
||||
FOUNDATION_EXPORT double PNXMPPFrameworkVersionNumber;
|
||||
FOUNDATION_EXPORT const unsigned char PNXMPPFrameworkVersionString[];
|
||||
|
||||
@ -1,5 +1,9 @@
|
||||
CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES = YES
|
||||
ENABLE_BITCODE = NO
|
||||
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
|
||||
HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Public"
|
||||
HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Public" $(inherited) $(SDKROOT)/usr/include/libxml2 $(PODS_ROOT)/XMPPFramework/module $(SDKROOT)/usr/include/libresolv
|
||||
LIBRARY_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/../../Vendor/libidn" "$(PODS_ROOT)/Vendor/libidn"
|
||||
OTHER_LDFLAGS = -l"idn" -l"resolv" -l"xml2" -framework "CoreData" -framework "CoreLocation" -framework "SystemConfiguration"
|
||||
PODS_ROOT = ${SRCROOT}
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
|
||||
SKIP_INSTALL = YES
|
||||
|
||||
@ -15,7 +15,7 @@ Updated and maintained by Deusty LLC and the Apple development community.
|
||||
|
||||
Software License Agreement (BSD License)
|
||||
|
||||
Copyright (c) 2010-2015, Deusty, LLC
|
||||
Copyright (c) 2010, Deusty, LLC
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use of this software in source and binary forms,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user