ObjFW  Diff

Differences From Artifact [26fae4bc2d]:

To Artifact [09fd3cd222]:


24
25
26
27
28
29
30
31

32
33
34
35
36
37
38

39
40
41
42


43
44

45
46

47
48

49
50
51
52

53
54
55
56



57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74



75
76
77
78
79
80
81
24
25
26
27
28
29
30

31
32
33
34
35
36
37

38
39
40


41
42
43

44
45

46
47

48
49
50
51

52
53



54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71



72
73
74
75
76
77
78
79
80
81







-
+






-
+


-
-
+
+

-
+

-
+

-
+



-
+

-
-
-
+
+
+















-
-
-
+
+
+







#import "OFURL.h"

#import "OFInvalidFormatException.h"

static void
handleAttribute(OFHTTPCookie *cookie, OFString *name, OFString *value)
{
	OFString *lowercaseName = [name lowercaseString];
	OFString *lowercaseName = name.lowercaseString;

	if (value != nil) {
		if ([lowercaseName isEqual: @"expires"]) {
			OFDate *date = [OFDate
			    dateWithDateString: value
					format: @"%a, %d %b %Y %H:%M:%S %z"];
			[cookie setExpires: date];
			cookie.expires = date;
		} else if ([lowercaseName isEqual: @"max-age"]) {
			OFDate *date = [OFDate dateWithTimeIntervalSinceNow:
			    [value decimalValue]];
			[cookie setExpires: date];
			    value.decimalValue];
			cookie.expires = date;
		} else if ([lowercaseName isEqual: @"domain"])
			[cookie setDomain: value];
			cookie.domain = value;
		else if ([lowercaseName isEqual: @"path"])
			[cookie setPath: value];
			cookie.path = value;
		else
			[[cookie extensions] addObject:
			[cookie.extensions addObject:
			    [OFString stringWithFormat: @"%@=%@", name, value]];
	} else {
		if ([lowercaseName isEqual: @"secure"])
			[cookie setSecure: true];
			cookie.secure = true;
		else if ([lowercaseName isEqual: @"httponly"])
			[cookie setHTTPOnly: true];
		else if ([name length] > 0)
			[[cookie extensions] addObject: name];
			cookie.HTTPOnly = true;
		else if (name.length > 0)
			[cookie.extensions addObject: name];
	}
}

@implementation OFHTTPCookie
@synthesize name = _name, value = _value, domain = _domain, path = _path;
@synthesize expires = _expires, secure = _secure, HTTPOnly = _HTTPOnly;
@synthesize extensions = _extensions;

+ (OFArray OF_GENERIC(OFHTTPCookie *) *)cookiesWithResponseHeaderFields:
    (OFDictionary OF_GENERIC(OFString *, OFString *) *)headerFields
    forURL: (OFURL *)URL
{
	OFMutableArray OF_GENERIC(OFHTTPCookie *) *ret = [OFMutableArray array];
	void *pool = objc_autoreleasePoolPush();
	OFString *string = [headerFields objectForKey: @"Set-Cookie"];
	OFString *domain = [URL host];
	const of_unichar_t *characters = [string characters];
	size_t length = [string length], last = 0;
	OFString *domain = URL.host;
	const of_unichar_t *characters = string.characters;
	size_t length = string.length, last = 0;
	enum {
		STATE_PRE_NAME,
		STATE_NAME,
		STATE_EXPECT_VALUE,
		STATE_VALUE,
		STATE_QUOTED_VALUE,
		STATE_POST_QUOTED_VALUE,
162
163
164
165
166
167
168
169

170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190

191
192
193
194
195
196
197
198
199
200

201
202
203
204
205
206
207
162
163
164
165
166
167
168

169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189

190
191
192
193
194
195
196
197
198
199

200
201
202
203
204
205
206
207







-
+




















-
+









-
+







				state = STATE_ATTR_VALUE;
				last = i + 1;
			} else if (characters[i] == ';' ||
			    characters[i] == ',') {
				name = [string substringWithRange:
				    of_range(last, i - last)];

				handleAttribute([ret lastObject], name, nil);
				handleAttribute(ret.lastObject, name, nil);

				state = (characters[i] == ';'
				    ? STATE_PRE_ATTR_NAME : STATE_PRE_NAME);
			}

			break;
		case STATE_ATTR_VALUE:
			if (characters[i] == ';' || characters[i] == ',') {
				value = [string substringWithRange:
				    of_range(last, i - last)];

				/*
				 * Expires often contains a comma, even though
				 * the comma is used as a separator for
				 * concatenating headers as per RFC 2616,
				 * meaning RFC 6265 contradicts RFC 2616.
				 * Solve this by special casing this.
				 */
				if (characters[i] == ',' &&
				    [name caseInsensitiveCompare: @"expires"] ==
				    OF_ORDERED_SAME && [value length] == 3 &&
				    OF_ORDERED_SAME && value.length == 3 &&
				    ([value isEqual: @"Mon"] ||
				    [value isEqual: @"Tue"] ||
				    [value isEqual: @"Wed"] ||
				    [value isEqual: @"Thu"] ||
				    [value isEqual: @"Fri"] ||
				    [value isEqual: @"Sat"] ||
				    [value isEqual: @"Sun"]))
					break;

				handleAttribute([ret lastObject], name, value);
				handleAttribute(ret.lastObject, name, value);

				state = (characters[i] == ';'
				    ? STATE_PRE_ATTR_NAME : STATE_PRE_NAME);
			}
			break;
		}
	}
229
230
231
232
233
234
235
236

237
238
239
240
241
242
243

244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261

262
263
264
265
266
267
268
269
270
271
272
273

274
275

276
277
278
279
280
281
282
229
230
231
232
233
234
235

236
237
238
239
240
241
242

243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260

261
262
263
264
265
266
267
268
269
270
271
272

273
274

275
276
277
278
279
280
281
282







-
+






-
+

















-
+











-
+

-
+







						      domain: domain]];
		break;
	case STATE_ATTR_NAME:
		if (last != length) {
			name = [string substringWithRange:
			    of_range(last, length - last)];

			handleAttribute([ret lastObject], name, nil);
			handleAttribute(ret.lastObject, name, nil);
		}
		break;
	case STATE_ATTR_VALUE:
		value = [string substringWithRange:
		    of_range(last, length - last)];

		handleAttribute([ret lastObject], name, value);
		handleAttribute(ret.lastObject, name, value);

		break;
	}

	objc_autoreleasePoolPop(pool);

	return ret;
}

+ (OFDictionary *)requestHeaderFieldsWithCookies:
    (OFArray OF_GENERIC(OFHTTPCookie *) *)cookies
{
	OFDictionary OF_GENERIC(OFString *, OFString *) *ret;
	void *pool;
	OFMutableString *cookieString;
	bool first = true;

	if ([cookies count] == 0)
	if (cookies.count == 0)
		return [OFDictionary dictionary];

	pool = objc_autoreleasePoolPush();
	cookieString = [OFMutableString string];

	for (OFHTTPCookie *cookie in cookies) {
		if OF_UNLIKELY (first)
			first = false;
		else
			[cookieString appendString: @"; "];

		[cookieString appendString: [cookie name]];
		[cookieString appendString: cookie.name];
		[cookieString appendString: @"="];
		[cookieString appendString: [cookie value]];
		[cookieString appendString: cookie.value];
	}

	ret = [[OFDictionary alloc] initWithObject: cookieString
					    forKey: @"Cookie"];

	objc_autoreleasePoolPop(pool);

364
365
366
367
368
369
370
371
372
373
374
375





376
377
378

379
380
381
382
383
384
385
364
365
366
367
368
369
370





371
372
373
374
375
376
377

378
379
380
381
382
383
384
385







-
-
-
-
-
+
+
+
+
+


-
+







}

- (uint32_t)hash
{
	uint32_t hash;

	OF_HASH_INIT(hash);
	OF_HASH_ADD_HASH(hash, [_name hash]);
	OF_HASH_ADD_HASH(hash, [_value hash]);
	OF_HASH_ADD_HASH(hash, [_domain hash]);
	OF_HASH_ADD_HASH(hash, [_path hash]);
	OF_HASH_ADD_HASH(hash, [_expires hash]);
	OF_HASH_ADD_HASH(hash, _name.hash);
	OF_HASH_ADD_HASH(hash, _value.hash);
	OF_HASH_ADD_HASH(hash, _domain.hash);
	OF_HASH_ADD_HASH(hash, _path.hash);
	OF_HASH_ADD_HASH(hash, _expires.hash);
	OF_HASH_ADD(hash, _secure);
	OF_HASH_ADD(hash, _HTTPOnly);
	OF_HASH_ADD_HASH(hash, [_extensions hash]);
	OF_HASH_ADD_HASH(hash, _extensions.hash);
	OF_HASH_FINALIZE(hash);

	return hash;
}

- (id)copy
{
416
417
418
419
420
421
422
423

424
425
426
427
428
429
430
431
432
433
416
417
418
419
420
421
422

423
424
425
426
427
428
429
430
431
432
433







-
+











	if (_secure)
		[ret appendString: @"; Secure"];

	if (_HTTPOnly)
		[ret appendString: @"; HTTPOnly"];

	if ([_extensions count] > 0)
	if (_extensions.count > 0)
		[ret appendFormat:
		    @"; %@", [_extensions componentsJoinedByString: @"; "]];

	objc_autoreleasePoolPop(pool);

	[ret makeImmutable];

	return ret;
}
@end