blob: b4aead0bd442904c1113582b069c19ea4117cd03 [file] [log] [blame]
#import "HTTPAuthenticationRequest.h"
#import "HTTPMessage.h"
#if ! __has_feature(objc_arc)
#warning This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC).
#endif
@interface HTTPAuthenticationRequest (PrivateAPI)
- (NSString *)quotedSubHeaderFieldValue:(NSString *)param fromHeaderFieldValue:(NSString *)header;
- (NSString *)nonquotedSubHeaderFieldValue:(NSString *)param fromHeaderFieldValue:(NSString *)header;
@end
@implementation HTTPAuthenticationRequest
- (id)initWithRequest:(HTTPMessage *)request
{
if ((self = [super init]))
{
NSString *authInfo = [request headerField:@"Authorization"];
isBasic = NO;
if ([authInfo length] >= 6)
{
isBasic = [[authInfo substringToIndex:6] caseInsensitiveCompare:@"Basic "] == NSOrderedSame;
}
isDigest = NO;
if ([authInfo length] >= 7)
{
isDigest = [[authInfo substringToIndex:7] caseInsensitiveCompare:@"Digest "] == NSOrderedSame;
}
if (isBasic)
{
NSMutableString *temp = [[authInfo substringFromIndex:6] mutableCopy];
CFStringTrimWhitespace((__bridge CFMutableStringRef)temp);
base64Credentials = [temp copy];
}
if (isDigest)
{
username = [self quotedSubHeaderFieldValue:@"username" fromHeaderFieldValue:authInfo];
realm = [self quotedSubHeaderFieldValue:@"realm" fromHeaderFieldValue:authInfo];
nonce = [self quotedSubHeaderFieldValue:@"nonce" fromHeaderFieldValue:authInfo];
uri = [self quotedSubHeaderFieldValue:@"uri" fromHeaderFieldValue:authInfo];
// It appears from RFC 2617 that the qop is to be given unquoted
// Tests show that Firefox performs this way, but Safari does not
// Thus we'll attempt to retrieve the value as nonquoted, but we'll verify it doesn't start with a quote
qop = [self nonquotedSubHeaderFieldValue:@"qop" fromHeaderFieldValue:authInfo];
if(qop && ([qop characterAtIndex:0] == '"'))
{
qop = [self quotedSubHeaderFieldValue:@"qop" fromHeaderFieldValue:authInfo];
}
nc = [self nonquotedSubHeaderFieldValue:@"nc" fromHeaderFieldValue:authInfo];
cnonce = [self quotedSubHeaderFieldValue:@"cnonce" fromHeaderFieldValue:authInfo];
response = [self quotedSubHeaderFieldValue:@"response" fromHeaderFieldValue:authInfo];
}
}
return self;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark Accessors:
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (BOOL)isBasic {
return isBasic;
}
- (BOOL)isDigest {
return isDigest;
}
- (NSString *)base64Credentials {
return base64Credentials;
}
- (NSString *)username {
return username;
}
- (NSString *)realm {
return realm;
}
- (NSString *)nonce {
return nonce;
}
- (NSString *)uri {
return uri;
}
- (NSString *)qop {
return qop;
}
- (NSString *)nc {
return nc;
}
- (NSString *)cnonce {
return cnonce;
}
- (NSString *)response {
return response;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark Private API:
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Retrieves a "Sub Header Field Value" from a given header field value.
* The sub header field is expected to be quoted.
*
* In the following header field:
* Authorization: Digest username="Mufasa", qop=auth, response="6629fae4939"
* The sub header field titled 'username' is quoted, and this method would return the value @"Mufasa".
**/
- (NSString *)quotedSubHeaderFieldValue:(NSString *)param fromHeaderFieldValue:(NSString *)header
{
NSRange startRange = [header rangeOfString:[NSString stringWithFormat:@"%@=\"", param]];
if(startRange.location == NSNotFound)
{
// The param was not found anywhere in the header
return nil;
}
NSUInteger postStartRangeLocation = startRange.location + startRange.length;
NSUInteger postStartRangeLength = [header length] - postStartRangeLocation;
NSRange postStartRange = NSMakeRange(postStartRangeLocation, postStartRangeLength);
NSRange endRange = [header rangeOfString:@"\"" options:0 range:postStartRange];
if(endRange.location == NSNotFound)
{
// The ending double-quote was not found anywhere in the header
return nil;
}
NSRange subHeaderRange = NSMakeRange(postStartRangeLocation, endRange.location - postStartRangeLocation);
return [header substringWithRange:subHeaderRange];
}
/**
* Retrieves a "Sub Header Field Value" from a given header field value.
* The sub header field is expected to not be quoted.
*
* In the following header field:
* Authorization: Digest username="Mufasa", qop=auth, response="6629fae4939"
* The sub header field titled 'qop' is nonquoted, and this method would return the value @"auth".
**/
- (NSString *)nonquotedSubHeaderFieldValue:(NSString *)param fromHeaderFieldValue:(NSString *)header
{
NSRange startRange = [header rangeOfString:[NSString stringWithFormat:@"%@=", param]];
if(startRange.location == NSNotFound)
{
// The param was not found anywhere in the header
return nil;
}
NSUInteger postStartRangeLocation = startRange.location + startRange.length;
NSUInteger postStartRangeLength = [header length] - postStartRangeLocation;
NSRange postStartRange = NSMakeRange(postStartRangeLocation, postStartRangeLength);
NSRange endRange = [header rangeOfString:@"," options:0 range:postStartRange];
if(endRange.location == NSNotFound)
{
// The ending comma was not found anywhere in the header
// However, if the nonquoted param is at the end of the string, there would be no comma
// This is only possible if there are no spaces anywhere
NSRange endRange2 = [header rangeOfString:@" " options:0 range:postStartRange];
if(endRange2.location != NSNotFound)
{
return nil;
}
else
{
return [header substringWithRange:postStartRange];
}
}
else
{
NSRange subHeaderRange = NSMakeRange(postStartRangeLocation, endRange.location - postStartRangeLocation);
return [header substringWithRange:subHeaderRange];
}
}
@end