CFHTTPCookie.c
1 // 2 // CFHTTPCookie.c 3 // CFNetwork 4 // 5 // Copyright (c) 2014 Apportable. All rights reserved. 6 // 7 8 #include "CFBase.h" 9 #include "CFRuntime.h" 10 #include "CFHTTPCookie.h" 11 #include "CFNumber.h" 12 #include <unicode/uregex.h> 13 #include <assert.h> 14 15 static const CFStringRef kCFHTTPCookieName = CFSTR("Name"); 16 static const CFStringRef kCFHTTPCookieValue = CFSTR("Value"); 17 static const CFStringRef kCFHTTPCookieOriginURL = CFSTR("OriginURL"); 18 static const CFStringRef kCFHTTPCookieVersion = CFSTR("Version"); 19 static const CFStringRef kCFHTTPCookieDomain = CFSTR("Domain"); 20 static const CFStringRef kCFHTTPCookiePath = CFSTR("Path"); 21 static const CFStringRef kCFHTTPCookieSecure = CFSTR("Secure"); 22 static const CFStringRef kCFHTTPCookieExpires = CFSTR("Expires"); 23 static const CFStringRef kCFHTTPCookieComment = CFSTR("Comment"); 24 static const CFStringRef kCFHTTPCookieCommentURL = CFSTR("CommentURL"); 25 static const CFStringRef kCFHTTPCookieDiscard = CFSTR("Discard"); 26 static const CFStringRef kCFHTTPCookieMaximumAge = CFSTR("Max-Age"); 27 static const CFStringRef kCFHTTPCookiePort = CFSTR("Port"); 28 static const CFStringRef kCFHTTPCookieHTTPOnly = CFSTR("HTTPOnly"); 29 30 struct __CFHTTPCookie { 31 CFRuntimeBase _base; 32 CFDictionaryRef _properties; 33 CFStringRef _domain; 34 }; 35 36 static void __CFHTTPCookieDeallocate(CFTypeRef cf) { 37 struct __CFHTTPCookie *item = (struct __CFHTTPCookie *)cf; 38 CFRelease(item->_properties); 39 } 40 41 static CFTypeID __kCFHTTPCookieTypeID = _kCFRuntimeNotATypeID; 42 43 static const CFRuntimeClass __CFHTTPCookieClass = { 44 _kCFRuntimeScannedObject, 45 "CFHTTPCookie", 46 NULL, // init 47 NULL, // copy 48 __CFHTTPCookieDeallocate, 49 NULL, 50 NULL, 51 NULL, 52 NULL 53 }; 54 55 static void __CFHTTPCookieInitialize(void) { 56 __kCFHTTPCookieTypeID = _CFRuntimeRegisterClass(&__CFHTTPCookieClass); 57 } 58 59 CFTypeID CFHTTPCookieGetTypeID(void) { 60 if (__kCFHTTPCookieTypeID == _kCFRuntimeNotATypeID) { 61 __CFHTTPCookieInitialize(); 62 } 63 return __kCFHTTPCookieTypeID; 64 } 65 66 CFHTTPCookieRef _CFHTTPCookieCreate(CFAllocatorRef allocator) { 67 CFIndex size = sizeof(struct __CFHTTPCookie) - sizeof(CFRuntimeBase); 68 return (CFHTTPCookieRef)_CFRuntimeCreateInstance(allocator, CFHTTPCookieGetTypeID(), size, NULL); 69 } 70 71 CFHTTPCookieRef CFHTTPCookieCreateWithProperties(CFDictionaryRef properties) { 72 CFHTTPCookieRef cookie = _CFHTTPCookieCreate(kCFAllocatorDefault); 73 cookie->_properties = CFRetain(CFDictionaryCreateCopy(kCFAllocatorDefault, properties)); 74 if (CFHTTPCookieGetDomain(cookie)==NULL||CFHTTPCookieGetPath(cookie)==NULL) { 75 CFRelease(cookie); 76 cookie = NULL; 77 } 78 79 return cookie; 80 } 81 CFNumberRef CFHTTPCookieGetVersion(CFHTTPCookieRef cookie) { 82 return CFDictionaryGetValue(cookie->_properties, kCFHTTPCookieVersion); 83 } 84 85 CFStringRef CFHTTPCookieGetName(CFHTTPCookieRef cookie) { 86 return CFDictionaryGetValue(cookie->_properties, kCFHTTPCookieName); 87 } 88 89 CFStringRef CFHTTPCookieGetDomain(CFHTTPCookieRef cookie) { 90 if (cookie->_domain == NULL) { 91 cookie->_domain = CFStringCreateCopy(kCFAllocatorDefault, CFDictionaryGetValue(cookie->_properties, kCFHTTPCookieDomain)); 92 93 if (cookie->_domain == NULL) { 94 CFTypeRef urlString = CFDictionaryGetValue(cookie->_properties, kCFHTTPCookieOriginURL); 95 if (urlString != NULL) { 96 CFURLRef url = NULL; 97 98 if (CFGetTypeID(urlString)==CFStringGetTypeID()) { 99 url = CFURLCreateWithString(kCFAllocatorDefault, urlString, NULL); 100 } else { 101 url = CFRetain(urlString); 102 } 103 cookie->_domain = CFURLCopyHostName(url); 104 105 CFRelease(url); 106 } 107 } 108 } 109 110 return cookie->_domain; 111 } 112 113 114 CFDateRef CFHTTPCookieGetExpirationDate(CFHTTPCookieRef cookie) { 115 return CFDictionaryGetValue(cookie->_properties, kCFHTTPCookieExpires); 116 } 117 118 CFStringRef CFHTTPCookieGetPath(CFHTTPCookieRef cookie) { 119 return CFDictionaryGetValue(cookie->_properties, kCFHTTPCookiePath); 120 } 121 122 CFStringRef CFHTTPCookieGetValue(CFHTTPCookieRef cookie) { 123 return CFDictionaryGetValue(cookie->_properties, kCFHTTPCookieValue); 124 } 125 126 127 CFStringRef CFHTTPCookieGetComment(CFHTTPCookieRef cookie) { 128 return CFDictionaryGetValue(cookie->_properties, kCFHTTPCookieComment); 129 } 130 131 CFURLRef CFHTTPCookieGetCommentURL(CFHTTPCookieRef cookie) { 132 return CFDictionaryGetValue(cookie->_properties, kCFHTTPCookieCommentURL); 133 } 134 135 CFArrayRef CFHTTPCookieGetPortArray(CFHTTPCookieRef cookie) { 136 return CFDictionaryGetValue(cookie->_properties, kCFHTTPCookiePort); 137 } 138 139 140 141 Boolean CFHTTPCookieIsSecure(CFHTTPCookieRef cookie) { 142 return CFBooleanGetValue(CFDictionaryGetValue(cookie->_properties, kCFHTTPCookieSecure)); 143 } 144 145 Boolean CFHTTPCookieIsHTTPOnly(CFHTTPCookieRef cookie) { 146 return CFBooleanGetValue(CFDictionaryGetValue(cookie->_properties, kCFHTTPCookieHTTPOnly)); 147 } 148 149 150 Boolean CFHTTPCookieIsSessionOnly(CFHTTPCookieRef cookie) { 151 return 0; //fixme 152 } 153 154 CFDictionaryRef CFHTTPCookieCopyProperties(CFHTTPCookieRef cookie) { 155 return CFDictionaryCreateCopy(kCFAllocatorDefault, cookie->_properties); 156 } 157 158 159 CFDictionaryRef CFHTTPCookieCopyRequestHeaderFields(CFArrayRef cookies) { 160 if (CFArrayGetCount(cookies)>0) { 161 CFMutableStringRef resultCookieString = CFStringCreateMutable(kCFAllocatorDefault, 4*1024*1024); 162 for (int i=0; i<CFArrayGetCount(cookies); i++) { 163 if (i>0) { 164 CFStringAppend(resultCookieString, CFSTR("; ")); 165 } 166 167 CFHTTPCookieRef cookie = (CFHTTPCookieRef)CFArrayGetValueAtIndex(cookies, i); 168 CFStringRef name = CFHTTPCookieGetName(cookie); 169 CFStringRef value = CFHTTPCookieGetValue(cookie); 170 CFStringAppendFormat(resultCookieString, NULL, CFSTR("%@=%@"), name, value); 171 172 } 173 174 CFStringRef cookieString = CFStringCreateCopy(kCFAllocatorDefault, resultCookieString); 175 CFRelease(resultCookieString); 176 const void *keys[] = { CFSTR("Cookie")}; 177 const void *values[] = { cookieString }; 178 179 180 CFDictionaryRef result = CFDictionaryCreate(NULL, keys, values, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 181 CFRelease(cookieString); 182 return result; 183 } else { 184 return NULL; 185 } 186 187 } 188 189 190 CFArrayRef CFHTTPCookieCreateWithResponseHeaderFields(CFDictionaryRef headerFields, CFURLRef url) { 191 CFStringRef cookieString = CFDictionaryGetValue(headerFields, CFSTR("Set-Cookie")); 192 if (cookieString!=NULL) { 193 194 UErrorCode status = U_ZERO_ERROR; 195 UParseError parse_err = { 0 }; 196 //fixme. regex parsing is evil. adopt parsing algorithm from firefox or from somewhere 197 198 CFStringRef pattern = CFSTR("(.*?)(=(.*?))?($|;|,(?! [1-9][0-9])) *"); 199 200 Boolean patNeedsFree = false; 201 const UChar *pat = CFStringGetCharactersPtr(pattern); 202 203 CFIndex patLen = CFStringGetLength(pattern); 204 if (pat == NULL) 205 { 206 pat = malloc(sizeof(UChar) * patLen); 207 CFStringGetCharacters(pattern, CFRangeMake(0, patLen), (UChar*)pat); 208 patNeedsFree = true; 209 } 210 211 URegularExpression *pExpr = uregex_open((const UChar *)pat, patLen, 0, &parse_err, &status); 212 213 assert(U_SUCCESS(status)); 214 215 /* Configure the text that the regular expression operates on. */ 216 217 const UChar *text = CFStringGetCharactersPtr(cookieString); 218 Boolean needsFree = false; 219 CFIndex len = CFStringGetLength(cookieString); 220 if (text == NULL) 221 { 222 text = malloc(sizeof(UChar) * len); 223 CFStringGetCharacters(cookieString, CFRangeMake(0, len), (UChar*)text); 224 needsFree = true; 225 } 226 227 uregex_setText(pExpr, text, len, &status); 228 assert(U_SUCCESS(status)); 229 230 231 /* Attempt the match */ 232 UBool res = uregex_findNext(pExpr, &status); 233 234 CFStringRef path = NULL; 235 CFMutableDictionaryRef cookiesNamesValues = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 236 while (res) { 237 int64_t nameStart = uregex_start64(pExpr, 1, &status); 238 int64_t nameEnd = uregex_end64(pExpr, 1, &status); 239 int64_t valueStart = uregex_start64(pExpr, 3, &status); 240 int64_t valueEnd = uregex_end64(pExpr, 3, &status); 241 CFStringRef name = CFStringCreateWithSubstring(kCFAllocatorDefault, cookieString, CFRangeMake(nameStart, nameEnd-nameStart)); 242 CFStringRef value = CFStringCreateWithSubstring(kCFAllocatorDefault, cookieString, CFRangeMake(valueStart, valueEnd-valueStart)); 243 //fixme: apply to last cookie only 244 if (CFStringCompare(name, CFSTR("path"), kCFCompareCaseInsensitive)==kCFCompareEqualTo) { 245 path = CFStringCreateCopy(kCFAllocatorDefault, value); 246 } else if (CFStringCompare(name, CFSTR("domain"), kCFCompareCaseInsensitive)==kCFCompareEqualTo) { 247 } else if (CFStringCompare(name, CFSTR("expires"), kCFCompareCaseInsensitive)==kCFCompareEqualTo) { 248 } else if (CFStringCompare(name, CFSTR("httponly"), kCFCompareCaseInsensitive)==kCFCompareEqualTo) { 249 } else if (CFStringCompare(name, CFSTR("secure"), kCFCompareCaseInsensitive)==kCFCompareEqualTo) { 250 } else if (value != NULL && CFStringGetLength(name)>0){ 251 CFDictionarySetValue(cookiesNamesValues, name, value); 252 } 253 254 CFRelease(name); 255 CFRelease(value); 256 257 258 res = uregex_findNext(pExpr, &status); 259 } 260 if (path==NULL) { 261 path = CFSTR("/"); 262 } 263 264 CFMutableArrayRef mutableResult = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); 265 266 CFTypeRef names[CFDictionaryGetCount(cookiesNamesValues)]; 267 CFTypeRef values[CFDictionaryGetCount(cookiesNamesValues)]; 268 CFDictionaryGetKeysAndValues(cookiesNamesValues, names, values); 269 for (int i=0; i<CFDictionaryGetCount(cookiesNamesValues); i++) { 270 CFTypeRef propertyKeys[] = {kCFHTTPCookieName, kCFHTTPCookieValue, kCFHTTPCookieOriginURL, kCFHTTPCookiePath}; 271 CFTypeRef propertyValues[] = {names[i], values[i], url, path}; 272 273 CFDictionaryRef properties = CFDictionaryCreate(kCFAllocatorDefault, propertyKeys, propertyValues, 4, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 274 275 CFHTTPCookieRef cookie = CFHTTPCookieCreateWithProperties(properties); 276 CFRelease(properties); 277 CFArrayAppendValue(mutableResult, cookie); 278 CFRelease(cookie); 279 280 } 281 CFRelease(path); 282 283 284 285 CFRelease(cookiesNamesValues); 286 287 assert(U_SUCCESS(status)); 288 289 290 291 292 293 uregex_close(pExpr); 294 if (needsFree) 295 { 296 free((UChar *)text); 297 } 298 if (patNeedsFree) 299 { 300 free((UChar *)pat); 301 } 302 303 /* 304 305 NSMutableDictionary *cookiesKeyValue = [NSMutableDictionary dictionary]; 306 __block NSString *path = @"/"; 307 308 [regex enumerateMatchesInString:cookieString options:0 range:NSMakeRange(0, cookieString.length) usingBlock:^(NSTextCheckingResult *checkingResult, NSMatchingFlags flags, BOOL *stop) { 309 NSString *name = [cookieString substringWithRange:[checkingResult rangeAtIndex:1]]; 310 NSString *value = [cookieString substringWithRange:[checkingResult rangeAtIndex:2]]; 311 if ([name isEqual:@"Path"]) { 312 path = value; 313 } else { 314 [cookiesKeyValue setObject:value forKey:name]; 315 } 316 }]; 317 318 [cookiesKeyValue enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { 319 NSHTTPCookie *cookie = [NSHTTPCookie cookieWithProperties:@{NSHTTPCookieName: key, NSHTTPCookieValue:obj, NSHTTPCookieOriginURL:URL, NSHTTPCookiePath: path}]; 320 [result addObject:cookie]; 321 }];*/ 322 323 CFArrayRef result = CFArrayCreateCopy(kCFAllocatorDefault, mutableResult); 324 CFRelease(mutableResult); 325 return result; 326 } else { 327 return NULL; 328 } 329 330 }