~ubuntu-branches/ubuntu/trusty/gnustep-base/trusty

« back to all changes in this revision

Viewing changes to Source/NSHTTPCookie.m

Tags: upstream-1.20.0
ImportĀ upstreamĀ versionĀ 1.20.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
/* Implementation for NSHTTPCookie for GNUstep
2
2
   Copyright (C) 2006 Software Foundation, Inc.
3
3
 
4
 
   Written by:  Richard Frith-Macdonald <frm@gnu.org>
 
4
   Written by:  Richard Frith-Macdonald <rfm@gnu.org>
5
5
   Date: 2006
6
6
   
7
7
   This file is part of the GNUstep Base Library.
22
22
   Boston, MA 02111 USA.
23
23
   */ 
24
24
 
25
 
#include "GSURLPrivate.h"
26
 
#include "Foundation/NSSet.h"
27
 
 
28
 
NSString * const NSHTTPCookieComment = @"NSHTTPCookieComment";
29
 
NSString * const NSHTTPCookieCommentURL = @"NSHTTPCookieCommentURL";
30
 
NSString * const NSHTTPCookieDiscard = @"NSHTTPCookieDiscard";
31
 
NSString * const NSHTTPCookieDomain = @"NSHTTPCookieDomain";
32
 
NSString * const NSHTTPCookieExpires = @"NSHTTPCookieExpires";
33
 
NSString * const NSHTTPCookieMaximumAge = @"NSHTTPCookieMaximumAge";
34
 
NSString * const NSHTTPCookieName = @"NSHTTPCookieName";
35
 
NSString * const NSHTTPCookieOriginURL = @"NSHTTPCookieOriginURL";
36
 
NSString * const NSHTTPCookiePath = @"NSHTTPCookiePath";
37
 
NSString * const NSHTTPCookiePort = @"NSHTTPCookiePort";
38
 
NSString * const NSHTTPCookieSecure = @"NSHTTPCookieSecure";
39
 
NSString * const NSHTTPCookieValue = @"NSHTTPCookieValue";
40
 
NSString * const NSHTTPCookieVersion = @"NSHTTPCookieVersion";
 
25
/* 
 
26
  Try to handle cookies via the original Netscape specification
 
27
  (http://web.archive.org/web/20070805052634/http://wp.netscape.com/newsref/std/cookie_spec.html)
 
28
  and the official RFC2965 (http://tools.ietf.org/html/rfc2965).
 
29
 
 
30
  Header fields named "Set-Cookie" are processed using either the original
 
31
  spec or RFC2965. "Set-Cookie2" fields use the RFC spec. There are some 
 
32
  crazy things to be aware of though. Multiple cookies can be specified in the
 
33
  same header and are separated by a comma. However, cookies themselves can
 
34
  also contain commas, most notably in the Expires field (which is not quoted
 
35
  and can contain spaces as well). The last key/value does not have to have a
 
36
  semi-colon, so this can be tricky to parse if another cookie occurs
 
37
  after this (See GSRangeOfCookie).
 
38
*/
 
39
 
 
40
#import "common.h"
 
41
#define EXPOSE_NSHTTPCookie_IVARS       1
 
42
#import "GSURLPrivate.h"
 
43
#import "Foundation/NSSet.h"
 
44
#import "Foundation/NSValue.h"
 
45
#import "Foundation/NSString.h"
 
46
#import "Foundation/NSCalendarDate.h"
 
47
#import "GNUstepBase/Unicode.h"
 
48
#import "GNUstepBase/NSObject+GNUstepBase.h"
 
49
 
 
50
NSString * const NSHTTPCookieComment = @"Comment";
 
51
NSString * const NSHTTPCookieCommentURL = @"CommentURL";
 
52
NSString * const NSHTTPCookieDiscard = @"Discard";
 
53
NSString * const NSHTTPCookieDomain = @"Domain";
 
54
NSString * const NSHTTPCookieExpires = @"Expires";
 
55
NSString * const NSHTTPCookieMaximumAge = @"MaximumAge";
 
56
NSString * const NSHTTPCookieName = @"Name";
 
57
NSString * const NSHTTPCookieOriginURL = @"OriginURL";
 
58
NSString * const NSHTTPCookiePath = @"Path";
 
59
NSString * const NSHTTPCookiePort = @"Port";
 
60
NSString * const NSHTTPCookieSecure = @"Secure";
 
61
NSString * const NSHTTPCookieValue = @"Value";
 
62
NSString * const NSHTTPCookieVersion = @"Version";
41
63
 
42
64
// Internal data storage
43
65
typedef struct {
44
66
  NSDictionary  *_properties;
45
67
} Internal;
46
68
 
47
 
typedef struct {
48
 
  @defs(NSHTTPCookie)
49
 
} priv;
50
 
#define this    ((Internal*)(((priv*)self)->_NSHTTPCookieInternal))
51
 
#define inst    ((Internal*)(((priv*)o)->_NSHTTPCookieInternal))
 
69
#define this    ((Internal*)(self->_NSHTTPCookieInternal))
 
70
#define inst    ((Internal*)(o->_NSHTTPCookieInternal))
 
71
 
 
72
/* Bitmap of characters considered white space if in an old style property
 
73
 * list. This is the same as the set given by the isspace() function in the
 
74
 * POSIX locale, but (for cross-locale portability of property list files)
 
75
 * is fixed, rather than locale dependent.
 
76
 */
 
77
static const unsigned char whitespace[32] = {
 
78
  '\x00',
 
79
  '\x3f',
 
80
  '\x00',
 
81
  '\x00',
 
82
  '\x01',
 
83
  '\x00',
 
84
  '\x00',
 
85
  '\x00',
 
86
  '\x00',
 
87
  '\x00',
 
88
  '\x00',
 
89
  '\x00',
 
90
  '\x00',
 
91
  '\x00',
 
92
  '\x00',
 
93
  '\x00',
 
94
  '\x00',
 
95
  '\x00',
 
96
  '\x00',
 
97
  '\x00',
 
98
  '\x00',
 
99
  '\x00',
 
100
  '\x00',
 
101
  '\x00',
 
102
  '\x00',
 
103
  '\x00',
 
104
  '\x00',
 
105
  '\x00',
 
106
  '\x00',
 
107
  '\x00',
 
108
  '\x00',
 
109
  '\x00',
 
110
};
 
111
 
 
112
#define IS_BIT_SET(a,i) ((((a) & (1<<(i)))) > 0)
 
113
 
 
114
#define GS_IS_WHITESPACE(X) IS_BIT_SET(whitespace[(X)/8], (X) % 8)
 
115
 
 
116
static id GSPropertyListFromCookieFormat(NSString *string);
 
117
static NSRange GSRangeOfCookie(NSString *string);
52
118
 
53
119
@implementation NSHTTPCookie
54
120
 
71
137
  return AUTORELEASE(o);
72
138
}
73
139
 
 
140
+ (NSMutableArray *) _parseField: (NSString *)field 
 
141
                       forHeader: (NSString *)header
 
142
                          andURL: (NSURL *)url
 
143
{
 
144
  int ckcount, pos;
 
145
  int version;
 
146
  NSString *defaultPath, *defaultDomain;
 
147
  NSMutableArray *a = [NSMutableArray array];
 
148
  ckcount = 0;
 
149
  if ([header isEqual: @"Set-Cookie"])
 
150
    version = 0;
 
151
  else if ([header isEqual: @"Set-Cookie2"])
 
152
    version = 1;
 
153
  else
 
154
    return nil;
 
155
 
 
156
  defaultDomain = [url host];
 
157
  defaultPath = [url path];
 
158
  if ([[url absoluteString] hasSuffix: @"/"] == NO)
 
159
    defaultPath = [defaultPath stringByDeletingLastPathComponent];
 
160
 
 
161
  /* We could use an NSScanner here, but this string could contain all
 
162
     sorts of odd stuff. It's not quite a property list either - it has
 
163
     dates and also could have tokens without values. */
 
164
  pos = 0;
 
165
  while (1)
 
166
    {
 
167
      NSHTTPCookie *cookie;
 
168
      NSMutableDictionary *dict;
 
169
      NSString *onecookie;
 
170
      NSRange range = GSRangeOfCookie(field);
 
171
      if (range.location == NSNotFound)
 
172
        break;
 
173
      onecookie = [field substringFromRange: range];
 
174
      NS_DURING
 
175
        dict = GSPropertyListFromCookieFormat(onecookie);
 
176
      NS_HANDLER
 
177
        dict = nil;
 
178
      NS_ENDHANDLER
 
179
        if ([dict count])
 
180
          {
 
181
            if ([dict objectForKey: NSHTTPCookiePath] == nil)
 
182
              [dict setObject: defaultPath forKey: NSHTTPCookiePath];
 
183
            if ([dict objectForKey: NSHTTPCookieDomain] == nil)
 
184
              [dict setObject: defaultDomain forKey: NSHTTPCookieDomain];
 
185
            cookie = [NSHTTPCookie cookieWithProperties: dict];
 
186
            if (cookie)
 
187
              [a addObject: cookie];
 
188
          }
 
189
      if ([field length] <= NSMaxRange(range))
 
190
        break;
 
191
      field = [field substringFromIndex: NSMaxRange(range)+1];
 
192
    }
 
193
  return a;
 
194
}
 
195
 
74
196
+ (NSArray *) cookiesWithResponseHeaderFields: (NSDictionary *)headerFields
75
197
                                       forURL: (NSURL *)URL
76
198
{
77
 
  NSMutableArray        *a = nil;
78
 
 
79
 
  [self notImplemented: _cmd];  // FIXME
 
199
  NSEnumerator   *henum = [headerFields keyEnumerator];
 
200
  NSMutableArray *a = [NSMutableArray array];
 
201
  NSString *header;
 
202
  while ((header = [henum nextObject]))
 
203
    {
 
204
      NSMutableArray *suba 
 
205
        = [self _parseField: [headerFields objectForKey: header] 
 
206
                forHeader: header andURL: URL];
 
207
      if (suba)
 
208
        [a addObjectsFromArray: suba];
 
209
    }
 
210
  
80
211
  return a;
81
212
}
82
213
 
83
214
+ (NSDictionary *) requestHeaderFieldsWithCookies: (NSArray *)cookies
84
215
{
85
 
  NSMutableDictionary   *d = nil;
86
 
 
87
 
  [self notImplemented: _cmd];  // FIXME
88
 
  return d;
 
216
  int version;
 
217
  NSString *field;
 
218
  NSHTTPCookie *ck;
 
219
  NSEnumerator *ckenum = [cookies objectEnumerator];
 
220
 
 
221
  if ([cookies count] == 0)
 
222
    {
 
223
      NSLog(@"NSHTTPCookie requestHeaderFieldWithCookies: empty array");
 
224
      return nil;
 
225
    }
 
226
  /* Assume these cookies all came from the same URL so we format based
 
227
     on the version of the first. */
 
228
  field = nil;
 
229
  version = [(NSHTTPCookie *)[cookies objectAtIndex: 0] version];
 
230
  if (version)
 
231
    field = @"$Version=\"1\"";
 
232
  while ((ck = [ckenum nextObject]))
 
233
    {
 
234
      NSString *str;
 
235
      str = [NSString stringWithFormat: @"%@=%@", [ck name], [ck value]];
 
236
      if (field)
 
237
        field = [field stringByAppendingFormat: @"; %@", str];
 
238
      else
 
239
        field = str;
 
240
      if (version && [ck path])
 
241
        field = [field stringByAppendingFormat: @"; $Path=\"%@\"", [ck path]];
 
242
    }
 
243
 
 
244
  return [NSDictionary dictionaryWithObject: field forKey: @"Cookie"];
89
245
}
90
246
 
91
247
- (NSString *) comment
118
274
  return [this->_properties objectForKey: NSHTTPCookieExpires];
119
275
}
120
276
 
 
277
- (BOOL) _isValidProperty: (NSString *)prop
 
278
{
 
279
  return ([prop length]
 
280
          && [prop rangeOfString: @"\n"].location == NSNotFound);
 
281
}
 
282
 
121
283
- (id) initWithProperties: (NSDictionary *)properties
122
284
{
123
 
  if ((self = [super init]) != nil)
 
285
  NSMutableDictionary *rawProps;
 
286
  if ((self = [super init]) == nil)
 
287
    return nil;
 
288
 
 
289
  /* Check a few values.  Based on Mac OS X tests. */
 
290
  if (![self _isValidProperty: [properties objectForKey: NSHTTPCookiePath]] 
 
291
      || ![self _isValidProperty: [properties objectForKey: NSHTTPCookieDomain]]
 
292
      || ![self _isValidProperty: [properties objectForKey: NSHTTPCookieName]]
 
293
      || ![self _isValidProperty: [properties objectForKey: NSHTTPCookieValue]]
 
294
      )
124
295
    {
125
 
      this->_properties = [properties copy];
126
 
      // FIXME ... parse and validate
 
296
      [self release];
 
297
      return nil;
127
298
    }
 
299
 
 
300
  rawProps = [[properties mutableCopy] autorelease];
 
301
  if ([rawProps objectForKey: @"Created"] == nil)
 
302
    [rawProps setObject: [NSDate date] forKey: @"Created"];
 
303
  if ([rawProps objectForKey: NSHTTPCookieExpires] == nil
 
304
        || [[rawProps objectForKey: NSHTTPCookieExpires] 
 
305
                isKindOfClass: [NSDate class]] == NO)
 
306
    [rawProps setObject: [NSNumber numberWithBool: YES] 
 
307
                 forKey: NSHTTPCookieDiscard];
 
308
 
 
309
  this->_properties = [rawProps copy];
128
310
  return self;
129
311
}
130
312
 
164
346
  return [this->_properties objectForKey: NSHTTPCookieValue];
165
347
}
166
348
 
167
 
- (unsigned) version
168
 
{
169
 
  return [[this->_properties objectForKey: NSHTTPCookieVersion] intValue];
170
 
}
 
349
- (NSUInteger) version
 
350
{
 
351
  return [[this->_properties objectForKey: NSHTTPCookieVersion] integerValue];
 
352
}
 
353
 
 
354
- (NSString *) description
 
355
{
 
356
  return [NSString stringWithFormat: @"<NSHTTPCookie %p: %@=%@>", self,
 
357
                   [self name], [self value]];
 
358
}
 
359
 
171
360
 
172
361
@end
173
362
 
 
363
#define inrange(ch,min,max) ((ch)>=(min) && (ch)<=(max))
 
364
#define char2num(ch) \
 
365
inrange(ch,'0','9') \
 
366
? ((ch)-0x30) \
 
367
: (inrange(ch,'a','f') \
 
368
? ((ch)-0x57) : ((ch)-0x37))
 
369
 
 
370
typedef struct  {
 
371
  const unsigned char   *ptr;
 
372
  unsigned      end;
 
373
  unsigned      pos;
 
374
  unsigned      lin;
 
375
  NSString      *err;
 
376
  int           opt;
 
377
  BOOL          key;
 
378
  BOOL          old;
 
379
} pldata;
 
380
 
 
381
/*
 
382
 *      Returns YES if there is any non-whitespace text remaining.
 
383
 */
 
384
static BOOL skipSpace(pldata *pld)
 
385
{
 
386
  unsigned char c;
 
387
 
 
388
  while (pld->pos < pld->end)
 
389
    {
 
390
      c = pld->ptr[pld->pos];
 
391
 
 
392
      if (GS_IS_WHITESPACE(c) == NO)
 
393
        {
 
394
          return YES;
 
395
        }
 
396
      if (c == '\n')
 
397
        {
 
398
          pld->lin++;
 
399
        }
 
400
      pld->pos++;
 
401
    }
 
402
  pld->err = @"reached end of string";
 
403
  return NO;
 
404
}
 
405
 
 
406
static inline id parseQuotedString(pldata* pld)
 
407
{
 
408
  unsigned      start = ++pld->pos;
 
409
  unsigned      escaped = 0;
 
410
  unsigned      shrink = 0;
 
411
  BOOL          hex = NO;
 
412
  NSString      *obj;
 
413
 
 
414
  while (pld->pos < pld->end)
 
415
    {
 
416
      unsigned char     c = pld->ptr[pld->pos];
 
417
 
 
418
      if (escaped)
 
419
        {
 
420
          if (escaped == 1 && c >= '0' && c <= '7')
 
421
            {
 
422
              escaped = 2;
 
423
              hex = NO;
 
424
            }
 
425
          else if (escaped == 1 && (c == 'u' || c == 'U'))
 
426
            {
 
427
              escaped = 2;
 
428
              hex = YES;
 
429
            }
 
430
          else if (escaped > 1)
 
431
            {
 
432
              if (hex && isxdigit(c))
 
433
                {
 
434
                  shrink++;
 
435
                  escaped++;
 
436
                  if (escaped == 6)
 
437
                    {
 
438
                      escaped = 0;
 
439
                    }
 
440
                }
 
441
              else if (c >= '0' && c <= '7')
 
442
                {
 
443
                  shrink++;
 
444
                  escaped++;
 
445
                  if (escaped == 4)
 
446
                    {
 
447
                      escaped = 0;
 
448
                    }
 
449
                }
 
450
              else
 
451
                {
 
452
                  pld->pos--;
 
453
                  escaped = 0;
 
454
                }
 
455
            }
 
456
          else
 
457
            {
 
458
              escaped = 0;
 
459
            }
 
460
        }
 
461
      else
 
462
        {
 
463
          if (c == '\\')
 
464
            {
 
465
              escaped = 1;
 
466
              shrink++;
 
467
            }
 
468
          else if (c == '"')
 
469
            {
 
470
              break;
 
471
            }
 
472
        }
 
473
      if (c == '\n')
 
474
        pld->lin++;
 
475
      pld->pos++;
 
476
    }
 
477
  if (pld->pos >= pld->end)
 
478
    {
 
479
      pld->err = @"reached end of string while parsing quoted string";
 
480
      return nil;
 
481
    }
 
482
  if (pld->pos - start - shrink == 0)
 
483
    {
 
484
      obj = @"";
 
485
    }
 
486
  else
 
487
    {
 
488
      unsigned  length;
 
489
      unichar   *chars;
 
490
      unichar   *temp = NULL;
 
491
      unsigned  int temp_length = 0;
 
492
      unsigned  j;
 
493
      unsigned  k;
 
494
 
 
495
      if (!GSToUnicode(&temp, &temp_length, &pld->ptr[start],
 
496
                       pld->pos - start, NSUTF8StringEncoding,
 
497
                       NSDefaultMallocZone(), 0))
 
498
        {
 
499
          pld->err = @"invalid utf8 data while parsing quoted string";
 
500
          return nil;
 
501
        }
 
502
      length = temp_length - shrink;
 
503
      chars = NSAllocateCollectable(sizeof(unichar) * length, 0);
 
504
      escaped = 0;
 
505
      hex = NO;
 
506
      for (j = 0, k = 0; j < temp_length; j++)
 
507
        {
 
508
          unichar c = temp[j];
 
509
 
 
510
          if (escaped)
 
511
            {
 
512
              if (escaped == 1 && c >= '0' && c <= '7')
 
513
                {
 
514
                  chars[k] = c - '0';
 
515
                  hex = NO;
 
516
                  escaped++;
 
517
                }
 
518
              else if (escaped == 1 && (c == 'u' || c == 'U'))
 
519
                {
 
520
                  chars[k] = 0;
 
521
                  hex = YES;
 
522
                  escaped++;
 
523
                }
 
524
              else if (escaped > 1)
 
525
                {
 
526
                  if (hex && isxdigit(c))
 
527
                    {
 
528
                      chars[k] <<= 4;
 
529
                      chars[k] |= char2num(c);
 
530
                      escaped++;
 
531
                      if (escaped == 6)
 
532
                        {
 
533
                          escaped = 0;
 
534
                          k++;
 
535
                        }
 
536
                    }
 
537
                  else if (c >= '0' && c <= '7')
 
538
                    {
 
539
                      chars[k] <<= 3;
 
540
                      chars[k] |= (c - '0');
 
541
                      escaped++;
 
542
                      if (escaped == 4)
 
543
                        {
 
544
                          escaped = 0;
 
545
                          k++;
 
546
                        }
 
547
                    }
 
548
                  else
 
549
                    {
 
550
                      escaped = 0;
 
551
                      j--;
 
552
                      k++;
 
553
                    }
 
554
                }
 
555
              else
 
556
                {
 
557
                  escaped = 0;
 
558
                  switch (c)
 
559
                    {
 
560
                      case 'a' : chars[k] = '\a'; break;
 
561
                      case 'b' : chars[k] = '\b'; break;
 
562
                      case 't' : chars[k] = '\t'; break;
 
563
                      case 'r' : chars[k] = '\r'; break;
 
564
                      case 'n' : chars[k] = '\n'; break;
 
565
                      case 'v' : chars[k] = '\v'; break;
 
566
                      case 'f' : chars[k] = '\f'; break;
 
567
                      default  : chars[k] = c; break;
 
568
                    }
 
569
                  k++;
 
570
                }
 
571
            }
 
572
          else
 
573
            {
 
574
              chars[k] = c;
 
575
              if (c == '\\')
 
576
                {
 
577
                  escaped = 1;
 
578
                }
 
579
              else
 
580
                {
 
581
                  k++;
 
582
                }
 
583
            }
 
584
        }
 
585
 
 
586
      NSZoneFree(NSDefaultMallocZone(), temp);
 
587
      length = k;
 
588
 
 
589
      obj = [NSString alloc];
 
590
      obj = [obj initWithCharactersNoCopy: chars
 
591
                 length: length
 
592
                 freeWhenDone: YES];
 
593
    }
 
594
  pld->pos++;
 
595
  return obj;
 
596
}
 
597
 
 
598
/* In cookies, keys are terminated by '=' and values are terminated by ';'
 
599
   or and EOL */
 
600
static inline id parseUnquotedString(pldata *pld, char endChar)
 
601
{
 
602
  unsigned      start = pld->pos;
 
603
  unsigned      i;
 
604
  unsigned      length;
 
605
  id            obj;
 
606
  unichar       *chars;
 
607
 
 
608
  while (pld->pos < pld->end)
 
609
    {
 
610
      if ((pld->ptr[pld->pos]) == endChar)
 
611
        break;
 
612
      pld->pos++;
 
613
    }
 
614
 
 
615
  length = pld->pos - start;
 
616
  chars = NSAllocateCollectable(sizeof(unichar) * length, 0);
 
617
  for (i = 0; i < length; i++)
 
618
    {
 
619
      chars[i] = pld->ptr[start + i];
 
620
    }
 
621
 
 
622
    {
 
623
      obj = [NSString alloc];
 
624
      obj = [obj initWithCharactersNoCopy: chars
 
625
                                   length: length
 
626
                             freeWhenDone: YES];
 
627
    }
 
628
  return obj;
 
629
}
 
630
 
 
631
static BOOL
 
632
_setCookieKey(NSMutableDictionary *dict, NSString *key, NSString *value)
 
633
{
 
634
  if ([dict count] == 0)
 
635
    {
 
636
      /* This must be the name=value pair */
 
637
      if ([value length] == 0)
 
638
        return NO;
 
639
      [dict setObject: key forKey: NSHTTPCookieName];
 
640
      [dict setObject: value forKey: NSHTTPCookieValue];
 
641
      return YES;
 
642
    }
 
643
  if ([[key lowercaseString] isEqual: @"comment"])
 
644
    [dict setObject: value forKey: NSHTTPCookieComment];
 
645
  else if ([[key lowercaseString] isEqual: @"commenturl"])
 
646
    [dict setObject: value forKey: NSHTTPCookieCommentURL];
 
647
  else if ([[key lowercaseString] isEqual: @"discard"])
 
648
    [dict setObject: [NSNumber numberWithBool: YES] 
 
649
             forKey: NSHTTPCookieDiscard];
 
650
  else if ([[key lowercaseString] isEqual: @"domain"])
 
651
    [dict setObject: value forKey: NSHTTPCookieDomain];
 
652
  else if ([[key lowercaseString] isEqual: @"expires"])
 
653
    {
 
654
      NSDate *expireDate;
 
655
      expireDate = [NSCalendarDate dateWithString: value
 
656
                                calendarFormat: @"%a, %d-%b-%Y %I:%M:%S %Z"];
 
657
      if (expireDate)
 
658
        [dict setObject: expireDate forKey: NSHTTPCookieExpires];
 
659
    }
 
660
  else if ([[key lowercaseString] isEqual: @"max-age"])
 
661
    [dict setObject: value forKey: NSHTTPCookieMaximumAge];
 
662
  else if ([[key lowercaseString] isEqual: @"originurl"])
 
663
    [dict setObject: value forKey: NSHTTPCookieOriginURL];
 
664
  else if ([[key lowercaseString] isEqual: @"path"])
 
665
    [dict setObject: value forKey: NSHTTPCookiePath];
 
666
  else if ([[key lowercaseString] isEqual: @"port"])
 
667
    [dict setObject: value forKey: NSHTTPCookiePort];
 
668
  else if ([[key lowercaseString] isEqual: @"secure"])
 
669
    [dict setObject: [NSNumber numberWithBool: YES] 
 
670
             forKey: NSHTTPCookieSecure];
 
671
  else if ([[key lowercaseString] isEqual: @"version"])
 
672
    [dict setObject: value forKey: NSHTTPCookieVersion];
 
673
  return YES;
 
674
}
 
675
 
 
676
static id
 
677
GSPropertyListFromCookieFormat(NSString *string)
 
678
{
 
679
  NSMutableDictionary   *dict;
 
680
  pldata                _pld;
 
681
  pldata                *pld = &_pld;
 
682
  NSData                *d;
 
683
  BOOL                  moreCharacters;
 
684
 
 
685
  /*
 
686
   * An empty string is a nil property list.
 
687
   */
 
688
  if ([string length] == 0)
 
689
    {
 
690
      return nil;
 
691
    }
 
692
 
 
693
  d = [string dataUsingEncoding: NSUTF8StringEncoding];
 
694
  NSCAssert(d, @"Couldn't get utf8 data from string.");
 
695
  _pld.ptr = (unsigned char*)[d bytes];
 
696
  _pld.pos = 0;
 
697
  _pld.end = [d length];
 
698
  _pld.err = nil;
 
699
  _pld.lin = 0;
 
700
  _pld.opt = 0;
 
701
  _pld.key = NO;
 
702
  _pld.old = YES;       // OpenStep style
 
703
 
 
704
  dict = [[NSMutableDictionary allocWithZone: NSDefaultMallocZone()]
 
705
    initWithCapacity: 0];
 
706
  while (skipSpace(pld) == YES)
 
707
    {
 
708
      id        key;
 
709
      id        val;
 
710
 
 
711
      if (pld->ptr[pld->pos] == '"')
 
712
        {
 
713
          key = parseQuotedString(pld);
 
714
        }
 
715
      else
 
716
        {
 
717
          key = parseUnquotedString(pld, '=');
 
718
        }
 
719
      if (key == nil)
 
720
        {
 
721
          DESTROY(dict);
 
722
          break;
 
723
        }
 
724
      moreCharacters = skipSpace(pld);
 
725
      if (moreCharacters == NO || pld->ptr[pld->pos] == ';')
 
726
        {
 
727
          pld->pos++;
 
728
          if (_setCookieKey(dict, key, @"") == NO)
 
729
            {
 
730
              pld->err = @"invalid cookie pair";
 
731
              DESTROY(dict);
 
732
            }
 
733
          RELEASE(key);
 
734
        }
 
735
      else if (pld->ptr[pld->pos] == '=')
 
736
        {
 
737
          pld->pos++;
 
738
          if (skipSpace(pld) == NO)
 
739
            {
 
740
              RELEASE(key);
 
741
              DESTROY(dict);
 
742
              break;
 
743
            }
 
744
          if (pld->ptr[pld->pos] == '"')
 
745
            {
 
746
              val = parseQuotedString(pld);
 
747
            }
 
748
          else
 
749
            {
 
750
              val = parseUnquotedString(pld, ';');
 
751
            }
 
752
          if (val == nil)
 
753
            {
 
754
              RELEASE(key);
 
755
              DESTROY(dict);
 
756
              break;
 
757
            }
 
758
          moreCharacters = skipSpace(pld);
 
759
          if (_setCookieKey(dict, key, val) == NO)
 
760
            {
 
761
              pld->err = @"invalid cookie pair";
 
762
              DESTROY(dict);
 
763
            }
 
764
          RELEASE(key);
 
765
          RELEASE(val);
 
766
          if (pld->ptr[pld->pos] == ';')
 
767
            {
 
768
              pld->pos++;
 
769
            }
 
770
          else
 
771
            {
 
772
              break;
 
773
            }
 
774
        }
 
775
      else
 
776
        {
 
777
          pld->err = @"unexpected character (wanted '=' or ';')";
 
778
          RELEASE(key);
 
779
          DESTROY(dict);
 
780
          break;
 
781
        }
 
782
    }
 
783
  if (dict == nil && _pld.err != nil)
 
784
    {
 
785
      RELEASE(dict);
 
786
      [NSException raise: NSGenericException
 
787
                  format: @"Parse failed at line %d (char %d) - %@",
 
788
        _pld.lin + 1, _pld.pos + 1, _pld.err];
 
789
    }
 
790
  return AUTORELEASE(dict);
 
791
}
 
792
 
 
793
/* Look for the comma that separates cookies. Commas can also occur in
 
794
   date strings, like "expires", but perhaps it can occur other places.
 
795
   For instance, the key/value pair  key=value1,value2 is not really
 
796
   valid, but should we handle it anyway? Definitely we should handle the
 
797
   perfectly normal case of:
 
798
 
 
799
   Set-Cookie: domain=test.com; expires=Thu, 12-Sep-2109 14:58:04 GMT;
 
800
     session=foo
 
801
   Set-Cookie: bar=baz
 
802
 
 
803
  which gets concatenated into something like:
 
804
 
 
805
  Set-Cookie: domain=test.com; expires=Thu, 12-Sep-2109 14:58:04 GMT;
 
806
    session=foo,bar=baz
 
807
 
 
808
*/
 
809
static NSRange 
 
810
GSRangeOfCookie(NSString *string)
 
811
{
 
812
  pldata                _pld;
 
813
  pldata                *pld = &_pld;
 
814
  NSData                *d;
 
815
  NSRange               range;
 
816
 
 
817
  /*
 
818
   * An empty string is a nil property list.
 
819
   */
 
820
  range = NSMakeRange(NSNotFound, NSNotFound);
 
821
  if ([string length] == 0)
 
822
    {
 
823
      return range;
 
824
    }
 
825
 
 
826
  d = [string dataUsingEncoding: NSUTF8StringEncoding];
 
827
  NSCAssert(d, @"Couldn't get utf8 data from string.");
 
828
  _pld.ptr = (unsigned char*)[d bytes];
 
829
  _pld.pos = 0;
 
830
  _pld.end = [d length];
 
831
  _pld.err = nil;
 
832
  _pld.lin = 0;
 
833
  _pld.opt = 0;
 
834
  _pld.key = NO;
 
835
  _pld.old = YES;       // OpenStep style
 
836
 
 
837
  while (skipSpace(pld) == YES)
 
838
    {
 
839
      if (pld->ptr[pld->pos] == ',')
 
840
        {
 
841
          /* Look ahead for something that will tell us if this is a
 
842
             separate cookie or not */
 
843
          unsigned saved_pos = pld->pos;
 
844
          while (pld->ptr[pld->pos] != '=' && pld->ptr[pld->pos] != ';'
 
845
                && pld->ptr[pld->pos] != ',' && pld->pos < pld->end )
 
846
            pld->pos++;
 
847
          if (pld->ptr[pld->pos] == '=')
 
848
            {
 
849
              /* Separate comment */
 
850
              range = NSMakeRange(0, saved_pos-1);
 
851
              break;
 
852
            }
 
853
          pld->pos = saved_pos;
 
854
        }
 
855
      pld->pos++;
 
856
    }
 
857
  if (range.location == NSNotFound)
 
858
    range = NSMakeRange(0, [string length]);
 
859
 
 
860
  return range;
 
861
}