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
#import "OFURL.h"

#import "OFInvalidFormatException.h"

static void
handleAttribute(OFHTTPCookie *cookie, OFString *name, OFString *value)
{
	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];
		} else if ([lowercaseName isEqual: @"max-age"]) {
			OFDate *date = [OFDate dateWithTimeIntervalSinceNow:
			    [value decimalValue]];
			[cookie setExpires: date];
		} else if ([lowercaseName isEqual: @"domain"])
			[cookie setDomain: value];
		else if ([lowercaseName isEqual: @"path"])
			[cookie setPath: value];
		else
			[[cookie extensions] addObject:
			    [OFString stringWithFormat: @"%@=%@", name, value]];
	} else {
		if ([lowercaseName isEqual: @"secure"])
			[cookie setSecure: true];
		else if ([lowercaseName isEqual: @"httponly"])
			[cookie setHTTPOnly: 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;
	enum {
		STATE_PRE_NAME,
		STATE_NAME,
		STATE_EXPECT_VALUE,
		STATE_VALUE,
		STATE_QUOTED_VALUE,
		STATE_POST_QUOTED_VALUE,







|






|


|
|

|

|

|



|

|
|
|















|
|
|







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;

	if (value != nil) {
		if ([lowercaseName isEqual: @"expires"]) {
			OFDate *date = [OFDate
			    dateWithDateString: value
					format: @"%a, %d %b %Y %H:%M:%S %z"];
			cookie.expires = date;
		} else if ([lowercaseName isEqual: @"max-age"]) {
			OFDate *date = [OFDate dateWithTimeIntervalSinceNow:
			    value.decimalValue];
			cookie.expires = date;
		} else if ([lowercaseName isEqual: @"domain"])
			cookie.domain = value;
		else if ([lowercaseName isEqual: @"path"])
			cookie.path = value;
		else
			[cookie.extensions addObject:
			    [OFString stringWithFormat: @"%@=%@", name, value]];
	} else {
		if ([lowercaseName isEqual: @"secure"])
			cookie.secure = true;
		else if ([lowercaseName isEqual: @"httponly"])
			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;
	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
				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);

				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 &&
				    ([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);

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







|




















|









|







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);

				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 &&
				    ([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);

				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
						      domain: domain]];
		break;
	case STATE_ATTR_NAME:
		if (last != length) {
			name = [string substringWithRange:
			    of_range(last, length - last)];

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

		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)
		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: @"="];
		[cookieString appendString: [cookie value]];
	}

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

	objc_autoreleasePoolPop(pool);








|






|

















|











|

|







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);
		}
		break;
	case STATE_ATTR_VALUE:
		value = [string substringWithRange:
		    of_range(last, length - last)];

		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)
		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: @"="];
		[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
}

- (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, _secure);
	OF_HASH_ADD(hash, _HTTPOnly);
	OF_HASH_ADD_HASH(hash, [_extensions hash]);
	OF_HASH_FINALIZE(hash);

	return hash;
}

- (id)copy
{







|
|
|
|
|


|







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, _secure);
	OF_HASH_ADD(hash, _HTTPOnly);
	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

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

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

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

	objc_autoreleasePoolPop(pool);

	[ret makeImmutable];

	return ret;
}
@end







|










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)
		[ret appendFormat:
		    @"; %@", [_extensions componentsJoinedByString: @"; "]];

	objc_autoreleasePoolPop(pool);

	[ret makeImmutable];

	return ret;
}
@end