HTTPAuthenticationRequest.m 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. #import "HTTPAuthenticationRequest.h"
  2. #import "HTTPMessage.h"
  3. #if ! __has_feature(objc_arc)
  4. #warning This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC).
  5. #endif
  6. @interface HTTPAuthenticationRequest (PrivateAPI)
  7. - (NSString *)quotedSubHeaderFieldValue:(NSString *)param fromHeaderFieldValue:(NSString *)header;
  8. - (NSString *)nonquotedSubHeaderFieldValue:(NSString *)param fromHeaderFieldValue:(NSString *)header;
  9. @end
  10. @implementation HTTPAuthenticationRequest
  11. - (id)initWithRequest:(HTTPMessage *)request
  12. {
  13. if ((self = [super init]))
  14. {
  15. NSString *authInfo = [request headerField:@"Authorization"];
  16. isBasic = NO;
  17. if ([authInfo length] >= 6)
  18. {
  19. isBasic = [[authInfo substringToIndex:6] caseInsensitiveCompare:@"Basic "] == NSOrderedSame;
  20. }
  21. isDigest = NO;
  22. if ([authInfo length] >= 7)
  23. {
  24. isDigest = [[authInfo substringToIndex:7] caseInsensitiveCompare:@"Digest "] == NSOrderedSame;
  25. }
  26. if (isBasic)
  27. {
  28. NSMutableString *temp = [[authInfo substringFromIndex:6] mutableCopy];
  29. CFStringTrimWhitespace((__bridge CFMutableStringRef)temp);
  30. base64Credentials = [temp copy];
  31. }
  32. if (isDigest)
  33. {
  34. username = [self quotedSubHeaderFieldValue:@"username" fromHeaderFieldValue:authInfo];
  35. realm = [self quotedSubHeaderFieldValue:@"realm" fromHeaderFieldValue:authInfo];
  36. nonce = [self quotedSubHeaderFieldValue:@"nonce" fromHeaderFieldValue:authInfo];
  37. uri = [self quotedSubHeaderFieldValue:@"uri" fromHeaderFieldValue:authInfo];
  38. // It appears from RFC 2617 that the qop is to be given unquoted
  39. // Tests show that Firefox performs this way, but Safari does not
  40. // Thus we'll attempt to retrieve the value as nonquoted, but we'll verify it doesn't start with a quote
  41. qop = [self nonquotedSubHeaderFieldValue:@"qop" fromHeaderFieldValue:authInfo];
  42. if(qop && ([qop characterAtIndex:0] == '"'))
  43. {
  44. qop = [self quotedSubHeaderFieldValue:@"qop" fromHeaderFieldValue:authInfo];
  45. }
  46. nc = [self nonquotedSubHeaderFieldValue:@"nc" fromHeaderFieldValue:authInfo];
  47. cnonce = [self quotedSubHeaderFieldValue:@"cnonce" fromHeaderFieldValue:authInfo];
  48. response = [self quotedSubHeaderFieldValue:@"response" fromHeaderFieldValue:authInfo];
  49. }
  50. }
  51. return self;
  52. }
  53. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  54. #pragma mark Accessors:
  55. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  56. - (BOOL)isBasic {
  57. return isBasic;
  58. }
  59. - (BOOL)isDigest {
  60. return isDigest;
  61. }
  62. - (NSString *)base64Credentials {
  63. return base64Credentials;
  64. }
  65. - (NSString *)username {
  66. return username;
  67. }
  68. - (NSString *)realm {
  69. return realm;
  70. }
  71. - (NSString *)nonce {
  72. return nonce;
  73. }
  74. - (NSString *)uri {
  75. return uri;
  76. }
  77. - (NSString *)qop {
  78. return qop;
  79. }
  80. - (NSString *)nc {
  81. return nc;
  82. }
  83. - (NSString *)cnonce {
  84. return cnonce;
  85. }
  86. - (NSString *)response {
  87. return response;
  88. }
  89. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  90. #pragma mark Private API:
  91. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  92. /**
  93. * Retrieves a "Sub Header Field Value" from a given header field value.
  94. * The sub header field is expected to be quoted.
  95. *
  96. * In the following header field:
  97. * Authorization: Digest username="Mufasa", qop=auth, response="6629fae4939"
  98. * The sub header field titled 'username' is quoted, and this method would return the value @"Mufasa".
  99. **/
  100. - (NSString *)quotedSubHeaderFieldValue:(NSString *)param fromHeaderFieldValue:(NSString *)header
  101. {
  102. NSRange startRange = [header rangeOfString:[NSString stringWithFormat:@"%@=\"", param]];
  103. if(startRange.location == NSNotFound)
  104. {
  105. // The param was not found anywhere in the header
  106. return nil;
  107. }
  108. NSUInteger postStartRangeLocation = startRange.location + startRange.length;
  109. NSUInteger postStartRangeLength = [header length] - postStartRangeLocation;
  110. NSRange postStartRange = NSMakeRange(postStartRangeLocation, postStartRangeLength);
  111. NSRange endRange = [header rangeOfString:@"\"" options:0 range:postStartRange];
  112. if(endRange.location == NSNotFound)
  113. {
  114. // The ending double-quote was not found anywhere in the header
  115. return nil;
  116. }
  117. NSRange subHeaderRange = NSMakeRange(postStartRangeLocation, endRange.location - postStartRangeLocation);
  118. return [header substringWithRange:subHeaderRange];
  119. }
  120. /**
  121. * Retrieves a "Sub Header Field Value" from a given header field value.
  122. * The sub header field is expected to not be quoted.
  123. *
  124. * In the following header field:
  125. * Authorization: Digest username="Mufasa", qop=auth, response="6629fae4939"
  126. * The sub header field titled 'qop' is nonquoted, and this method would return the value @"auth".
  127. **/
  128. - (NSString *)nonquotedSubHeaderFieldValue:(NSString *)param fromHeaderFieldValue:(NSString *)header
  129. {
  130. NSRange startRange = [header rangeOfString:[NSString stringWithFormat:@"%@=", param]];
  131. if(startRange.location == NSNotFound)
  132. {
  133. // The param was not found anywhere in the header
  134. return nil;
  135. }
  136. NSUInteger postStartRangeLocation = startRange.location + startRange.length;
  137. NSUInteger postStartRangeLength = [header length] - postStartRangeLocation;
  138. NSRange postStartRange = NSMakeRange(postStartRangeLocation, postStartRangeLength);
  139. NSRange endRange = [header rangeOfString:@"," options:0 range:postStartRange];
  140. if(endRange.location == NSNotFound)
  141. {
  142. // The ending comma was not found anywhere in the header
  143. // However, if the nonquoted param is at the end of the string, there would be no comma
  144. // This is only possible if there are no spaces anywhere
  145. NSRange endRange2 = [header rangeOfString:@" " options:0 range:postStartRange];
  146. if(endRange2.location != NSNotFound)
  147. {
  148. return nil;
  149. }
  150. else
  151. {
  152. return [header substringWithRange:postStartRange];
  153. }
  154. }
  155. else
  156. {
  157. NSRange subHeaderRange = NSMakeRange(postStartRangeLocation, endRange.location - postStartRangeLocation);
  158. return [header substringWithRange:subHeaderRange];
  159. }
  160. }
  161. @end