This commit is contained in:
Giuseppe Nucifora 2016-02-24 16:56:39 +01:00
parent d281d765ea
commit 5b074a5176
1261 changed files with 199158 additions and 7303 deletions

View 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

View 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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View 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

View 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

View 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

View 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

View 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

View 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

View File

@ -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

View File

@ -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

View 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

View 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

View 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

View 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
View 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
View 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

View 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
View 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

View 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

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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 &#38; 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
View 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
View 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

File diff suppressed because it is too large Load Diff

5105
Core/XMPPStream.m Normal file

File diff suppressed because it is too large Load Diff

View File

@ -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;

View File

@ -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

View File

@ -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"

View File

@ -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
}

View File

@ -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

View File

@ -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, &notifyToken, 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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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__)

File diff suppressed because it is too large Load Diff

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 *
}
}

View File

@ -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,

View 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

View 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, &notifyToken, 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

View 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

View 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

View File

@ -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

View 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

View 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)

View File

@ -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;

View 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
View 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

File diff suppressed because it is too large Load Diff

View 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

File diff suppressed because it is too large Load Diff

View 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

View 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

View 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

View 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

View 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

View File

@ -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;
}

View 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

View File

@ -4,133 +4,14 @@
CocoaLumberjack
===============
[![Build Status](https://travis-ci.org/CocoaLumberjack/CocoaLumberjack.svg?branch=master)](https://travis-ci.org/CocoaLumberjack/CocoaLumberjack)
[![Build Status](http://img.shields.io/travis/CocoaLumberjack/CocoaLumberjack/master.svg?style=flat)](https://travis-ci.org/CocoaLumberjack/CocoaLumberjack)
[![Pod Version](http://img.shields.io/cocoapods/v/CocoaLumberjack.svg?style=flat)](http://cocoadocs.org/docsets/CocoaLumberjack/)
[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage)
[![Pod Platform](http://img.shields.io/cocoapods/p/CocoaLumberjack.svg?style=flat)](http://cocoadocs.org/docsets/CocoaLumberjack/)
[![Pod License](http://img.shields.io/cocoapods/l/CocoaLumberjack.svg?style=flat)](http://opensource.org/licenses/BSD-3-Clause)
[![Reference Status](https://www.versioneye.com/objective-c/cocoalumberjack/reference_badge.svg?style=flat)](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

View File

@ -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"
}
}
]
}

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -14,7 +14,7 @@
buildForArchiving = "YES">
<BuildableReference
BuildableIdentifier = 'primary'
BlueprintIdentifier = '5A35D899250B8C7553F3AC9DE053FA3F'
BlueprintIdentifier = '188DC26053F39001ADD409E9BD3B63A8'
BlueprintName = 'PNXMPPFramework'
ReferencedContainer = 'container:Pods.xcodeproj'
BuildableName = 'PNXMPPFramework.framework'>

View 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[];

View File

@ -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 * }
}

View File

@ -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>

View File

@ -2,3 +2,4 @@
#import <UIKit/UIKit.h>
#endif
#define HAVE_XMPP_SUBSPEC_POD

View File

@ -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[];

View File

@ -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

View File

@ -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