/ src / Cookies / CFHTTPCookie.c
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  }