ObjFW  Check-in [a15b403a11]

Overview
Comment:OFURL: Store the URL-encoded version internally

This allows retrieving it how it was originally stored, which is useful
for example for query strings, as these often URL-encode allowed
characters in order to allow pairs of the form foo=bar&bar=qux.

Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: a15b403a1148499b88fb8366809afed266d048db282211065c7b94a6e20e83b0
User & Date: js on 2017-11-06 23:01:57
Other Links: manifest | tags
Context
2017-11-11
22:28
Make the default OFFileManager a singleton check-in: 9a60a5adfc user: js tags: trunk
2017-11-06
23:01
OFURL: Store the URL-encoded version internally check-in: a15b403a11 user: js tags: trunk
00:10
of_string_utf8_encode(): Remove a variable check-in: 455caa7063 user: js tags: trunk
Changes

Modified src/OFHTTPServer.m from [380d4e5179] to [3bc806eac7].

661
662
663
664
665
666
667
668
669


670
671

672
673
674
675
676
677
678
661
662
663
664
665
666
667


668
669
670

671
672
673
674
675
676
677
678







-
-
+
+

-
+







	if ((pos = [_path rangeOfString: @"?"].location) != OF_NOT_FOUND) {
		OFString *path, *query;

		path = [_path substringWithRange: of_range(0, pos)];
		query = [_path substringWithRange:
		    of_range(pos + 1, [_path length] - pos - 1)];

		[URL setPath: path];
		[URL setQuery: query];
		[URL setURLEncodedPath: path];
		[URL setURLEncodedQuery: query];
	} else
		[URL setPath: _path];
		[URL setURLEncodedPath: _path];

	[URL makeImmutable];

	request = [OFHTTPRequest requestWithURL: URL];
	[request setMethod: _method];
	[request setProtocolVersion:
	    (of_http_request_protocol_version_t){ 1, _HTTPMinorVersion }];

Modified src/OFMutableURL.h from [716b3b017c] to [cd0e75d21f].

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
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
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144







+
+
+
+
+
+
+
+
+





+
+
+
+
+
+
+
+
+










+
+
+
+
+
+
+
+
+





+
+
+
+
+
+
+
+
+





+
+
+
+
+
+
+
+
+













+
+
+
+
+
+
+
+
+





+
+
+
+
+
+
+
+
+







 */
@interface OFMutableURL: OFURL
/*!
 * The scheme part of the URL.
 */
@property OF_NULLABLE_PROPERTY (readwrite, copy, nonatomic) OFString *scheme;

/*!
 * The scheme part of the URL in URL-encoded form.
 *
 * Setting this retains the original URL-encoding used - if more characters
 * than necessary are URL-encoded, it is kept this way.
 */
@property OF_NULLABLE_PROPERTY (readwrite, copy, nonatomic)
    OFString *URLEncodedScheme;

/*!
 * The host part of the URL.
 */
@property OF_NULLABLE_PROPERTY (readwrite, copy, nonatomic) OFString *host;

/*!
 * The host part of the URL in URL-encoded form.
 *
 * Setting this retains the original URL-encoding used - if more characters
 * than necessary are URL-encoded, it is kept this way.
 */
@property OF_NULLABLE_PROPERTY (readwrite, copy, nonatomic)
    OFString *URLEncodedHost;

/*!
 * The port part of the URL.
 */
@property OF_NULLABLE_PROPERTY (readwrite, copy, nonatomic) OFNumber *port;

/*!
 * The user part of the URL.
 */
@property OF_NULLABLE_PROPERTY (readwrite, copy, nonatomic) OFString *user;

/*!
 * The user part of the URL in URL-encoded form.
 *
 * Setting this retains the original URL-encoding used - if more characters
 * than necessary are URL-encoded, it is kept this way.
 */
@property OF_NULLABLE_PROPERTY (readwrite, copy, nonatomic)
    OFString *URLEncodedUser;

/*!
 * The password part of the URL.
 */
@property OF_NULLABLE_PROPERTY (readwrite, copy, nonatomic) OFString *password;

/*!
 * The password part of the URL in URL-encoded form.
 *
 * Setting this retains the original URL-encoding used - if more characters
 * than necessary are URL-encoded, it is kept this way.
 */
@property OF_NULLABLE_PROPERTY (readwrite, copy, nonatomic)
    OFString *URLEncodedPassword;

/*!
 * The path part of the URL.
 */
@property OF_NULLABLE_PROPERTY (readwrite, copy, nonatomic) OFString *path;

/*!
 * The path part of the URL in URL-encoded form.
 *
 * Setting this retains the original URL-encoding used - if more characters
 * than necessary are URL-encoded, it is kept this way.
 */
@property OF_NULLABLE_PROPERTY (readwrite, copy, nonatomic)
    OFString *URLEncodedPath;

/*!
 * The path of the URL split into components.
 *
 * The first component must always be empty to designate the root.
 */
@property OF_NULLABLE_PROPERTY (readwrite, copy, nonatomic)
    OFArray OF_GENERIC(OFString *) *pathComponents;

/*!
 * The query part of the URL.
 */
@property OF_NULLABLE_PROPERTY (readwrite, copy, nonatomic) OFString *query;

/*!
 * The query part of the URL in URL-encoded form.
 *
 * Setting this retains the original URL-encoding used - if more characters
 * than necessary are URL-encoded, it is kept this way.
 */
@property OF_NULLABLE_PROPERTY (readwrite, copy, nonatomic)
    OFString *URLEncodedQuery;

/*!
 * The fragment part of the URL.
 */
@property OF_NULLABLE_PROPERTY (readwrite, copy, nonatomic) OFString *fragment;

/*!
 * The fragment part of the URL in URL-encoded form.
 *
 * Setting this retains the original URL-encoding used - if more characters
 * than necessary are URL-encoded, it is kept this way.
 */
@property OF_NULLABLE_PROPERTY (readwrite, copy, nonatomic)
    OFString *URLEncodedFragment;

/*!
 * @brief Creates a new mutable URL.
 *
 * @return A new, autoreleased OFMutableURL
 */
+ (instancetype)URL;

Modified src/OFMutableURL.m from [f6b587c9bc] to [b9f96c6e08].

21
22
23
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
82
83
84
85
86
21
22
23
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
82
83
84
85
86
87
88
89
90
91
92
93
94
95


96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113


114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130


131
132
133
134
135
136
137
138
139







-
-
+
+
+













+
+
+
+
+
+
+
+
+
+
+
-
-
+
+





+
+
+
+
+
+
+
+
+
+
-
-
+
+












+
+
+
+
+
+
+
+
+
+
-
-
+
+





+
+
+
+
+
+
+
+
+
+
+
-
-
+
+





+
+
+
+
+
+
+
+
+
+
-
-
+
+







#import "OFNumber.h"
#import "OFString.h"
#import "OFURL+Private.h"

#import "OFInvalidFormatException.h"

@implementation OFMutableURL
@dynamic scheme, host, port, user, password, path, pathComponents, query;
@dynamic fragment;
@dynamic scheme, URLEncodedScheme, host, URLEncodedHost, port, user;
@dynamic URLEncodedUser, password, URLEncodedPassword, path, URLEncodedPath;
@dynamic pathComponents, query, URLEncodedQuery, fragment, URLEncodedFragment;

+ (instancetype)URL
{
	return [[[self alloc] init] autorelease];
}

- (instancetype)init
{
	return [super of_init];
}

- (void)setScheme: (OFString *)scheme
{
	void *pool = objc_autoreleasePoolPush();

	[self setURLEncodedScheme:
	    [scheme stringByURLEncodingWithAllowedCharacters:
	    [OFCharacterSet URLSchemeAllowedCharacterSet]]];

	objc_autoreleasePoolPop(pool);
}

- (void)setURLEncodedScheme: (OFString *)URLEncodedScheme
{
	OFString *old = _scheme;
	_scheme = [scheme copy];
	OFString *old = _URLEncodedScheme;
	_URLEncodedScheme = [URLEncodedScheme copy];
	[old release];
}

- (void)setHost: (OFString *)host
{
	void *pool = objc_autoreleasePoolPush();

	[self setURLEncodedHost: [host stringByURLEncodingWithAllowedCharacters:
	    [OFCharacterSet URLHostAllowedCharacterSet]]];

	objc_autoreleasePoolPop(pool);
}

- (void)setURLEncodedHost: (OFString *)URLEncodedHost
{
	OFString *old = _host;
	_host = [host copy];
	OFString *old = _URLEncodedHost;
	_URLEncodedHost = [URLEncodedHost copy];
	[old release];
}

- (void)setPort: (OFNumber *)port
{
	OFNumber *old = _port;
	_port = [port copy];
	[old release];
}

- (void)setUser: (OFString *)user
{
	void *pool = objc_autoreleasePoolPush();

	[self setURLEncodedUser: [user stringByURLEncodingWithAllowedCharacters:
	    [OFCharacterSet URLUserAllowedCharacterSet]]];

	objc_autoreleasePoolPop(pool);
}

- (void)setURLEncodedUser: (OFString *)URLEncodedUser
{
	OFString *old = _user;
	_user = [user copy];
	OFString *old = _URLEncodedUser;
	_URLEncodedUser = [URLEncodedUser copy];
	[old release];
}

- (void)setPassword: (OFString *)password
{
	void *pool = objc_autoreleasePoolPush();

	[self setURLEncodedPassword:
	    [password stringByURLEncodingWithAllowedCharacters:
	    [OFCharacterSet URLPasswordAllowedCharacterSet]]];

	objc_autoreleasePoolPop(pool);
}

- (void)setURLEncodedPassword: (OFString *)URLEncodedPassword
{
	OFString *old = _password;
	_password = [password copy];
	OFString *old = _URLEncodedPassword;
	_URLEncodedPassword = [URLEncodedPassword copy];
	[old release];
}

- (void)setPath: (OFString *)path
{
	void *pool = objc_autoreleasePoolPush();

	[self setURLEncodedPath: [path stringByURLEncodingWithAllowedCharacters:
	    [OFCharacterSet URLPathAllowedCharacterSet]]];

	objc_autoreleasePoolPop(pool);
}

- (void)setURLEncodedPath: (OFString *)URLEncodedPath
{
	OFString *old = _path;
	_path = [path copy];
	OFString *old = _URLEncodedPath;
	_URLEncodedPath = [URLEncodedPath copy];
	[old release];
}

- (void)setPathComponents: (OFArray *)components
{
	void *pool = objc_autoreleasePoolPush();

98
99
100
101
102
103
104











105
106


107
108
109
110
111











112
113


114
115
116
117
118
119
120
151
152
153
154
155
156
157
158
159
160
161
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







+
+
+
+
+
+
+
+
+
+
+
-
-
+
+





+
+
+
+
+
+
+
+
+
+
+
-
-
+
+







	[self setPath: [components componentsJoinedByString: @"/"]];

	objc_autoreleasePoolPop(pool);
}

- (void)setQuery: (OFString *)query
{
	void *pool = objc_autoreleasePoolPush();

	[self setURLEncodedQuery:
	    [query stringByURLEncodingWithAllowedCharacters:
	    [OFCharacterSet URLQueryAllowedCharacterSet]]];

	objc_autoreleasePoolPop(pool);
}

- (void)setURLEncodedQuery: (OFString *)URLEncodedQuery
{
	OFString *old = _query;
	_query = [query copy];
	OFString *old = _URLEncodedQuery;
	_URLEncodedQuery = [URLEncodedQuery copy];
	[old release];
}

- (void)setFragment: (OFString *)fragment
{
	void *pool = objc_autoreleasePoolPush();

	[self setURLEncodedFragment:
	    [fragment stringByURLEncodingWithAllowedCharacters:
	    [OFCharacterSet URLFragmentAllowedCharacterSet]]];

	objc_autoreleasePoolPop(pool);
}

- (void)setURLEncodedFragment: (OFString *)URLEncodedFragment
{
	OFString *old = _fragment;
	_fragment = [fragment copy];
	OFString *old = _URLEncodedFragment;
	_URLEncodedFragment = [URLEncodedFragment copy];
	[old release];
}

- (id)copy
{
	OFMutableURL *copy = [self mutableCopy];

Modified src/OFURL.h from [7ad59d1622] to [8793c73a5f].

27
28
29
30
31
32
33
34

35
36
37



38
39
40
41
42
43
44
27
28
29
30
31
32
33

34
35


36
37
38
39
40
41
42
43
44
45







-
+

-
-
+
+
+







/*!
 * @class OFURL OFURL.h ObjFW/OFURL.h
 *
 * @brief A class for parsing URLs and accessing parts of it.
 */
@interface OFURL: OFObject <OFCopying, OFMutableCopying, OFSerialization>
{
	OFString *_Nullable _scheme, *_Nullable _host;
	OFString *_Nullable _URLEncodedScheme, *_Nullable _URLEncodedHost;
	OFNumber *_Nullable _port;
	OFString *_Nullable _user, *_Nullable _password, *_path;
	OFString *_Nullable _query, *_Nullable _fragment;
	OFString *_Nullable _URLEncodedUser, *_Nullable _URLEncodedPassword;
	OFString *_Nullable _URLEncodedPath;
	OFString *_Nullable _URLEncodedQuery, *_Nullable _URLEncodedFragment;
}

/*!
 * The scheme part of the URL.
 */
@property OF_NULLABLE_PROPERTY (readonly, copy, nonatomic) OFString *scheme;

Modified src/OFURL.m from [09db0decaa] to [674edf5266].

344
345
346
347
348
349
350

351
352


353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375




376
377
378


379
380
381
382
383
384
385
386
387
388
389

390

391
392
393
394
395
396
397
398
399
400
401
402
403

404

405
406
407
408
409
410
411
412


413
414
415
416
417
418
419


420
421
422
423
424

425

426
427
428
429
430
431
432
433
344
345
346
347
348
349
350
351


352
353

354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371




372
373
374
375
376


377
378
379
380
381
382
383

384
385
386
387
388
389

390

391

392
393
394
395
396
397
398


399
400

401

402
403
404
405
406


407
408
409
410
411
412
413


414
415
416
417
418
419
420
421

422

423
424
425
426
427
428
429







+
-
-
+
+
-


















-
-
-
-
+
+
+
+

-
-
+
+





-





+
-
+
-

-







-
-

+
-
+
-





-
-
+
+





-
-
+
+





+
-
+
-








		if (strncmp(tmp, "://", 3) != 0)
			@throw [OFInvalidFormatException exception];

		for (tmp2 = UTF8String; tmp2 < tmp; tmp2++)
			*tmp2 = of_ascii_tolower(*tmp2);

		_URLEncodedScheme = [[OFString alloc]
		_scheme = [[[OFString stringWithUTF8String: UTF8String
						    length: tmp - UTF8String]
		    initWithUTF8String: UTF8String
				length: tmp - UTF8String];
		    stringByURLDecoding] copy];

		UTF8String = tmp + 3;

		if ((tmp = strchr(UTF8String, '/')) != NULL) {
			*tmp = '\0';
			tmp++;
		}

		if ((tmp2 = strchr(UTF8String, '@')) != NULL) {
			char *tmp3;

			*tmp2 = '\0';
			tmp2++;

			if ((tmp3 = strchr(UTF8String, ':')) != NULL) {
				*tmp3 = '\0';
				tmp3++;

				_user = [[[OFString stringWithUTF8String:
				    UTF8String] stringByURLDecoding] copy];
				_password = [[[OFString stringWithUTF8String:
				    tmp3] stringByURLDecoding] copy];
				_URLEncodedUser = [[OFString alloc]
				    initWithUTF8String: UTF8String];
				_URLEncodedPassword = [[OFString alloc]
				    initWithUTF8String: tmp3];
			} else
				_user = [[[OFString stringWithUTF8String:
				    UTF8String] stringByURLDecoding] copy];
				_URLEncodedUser = [[OFString alloc]
				    initWithUTF8String: UTF8String];

			UTF8String = tmp2;
		}

		if ((tmp2 = strchr(UTF8String, ':')) != NULL) {
			void *pool2;
			OFString *portString;

			*tmp2 = '\0';
			tmp2++;

			_URLEncodedHost = [[OFString alloc]
			_host = [[[OFString stringWithUTF8String: UTF8String]
			    initWithUTF8String: UTF8String];
			    stringByURLDecoding] copy];

			pool2 = objc_autoreleasePoolPush();
			portString = [OFString stringWithUTF8String: tmp2];

			if ([portString decimalValue] > 65535)
				@throw [OFInvalidFormatException exception];

			_port = [[OFNumber alloc] initWithUInt16:
			    (uint16_t)[portString decimalValue]];

			objc_autoreleasePoolPop(pool2);
		} else
			_URLEncodedHost = [[OFString alloc]
			_host = [[[OFString stringWithUTF8String: UTF8String]
			    initWithUTF8String: UTF8String];
			    stringByURLDecoding] copy];

		if ((UTF8String = tmp) != NULL) {
			if ((tmp = strchr(UTF8String, '#')) != NULL) {
				*tmp = '\0';

				_fragment = [[[OFString stringWithUTF8String:
				    tmp + 1] stringByURLDecoding] copy];
				_URLEncodedFragment = [[OFString alloc]
				    initWithUTF8String: tmp + 1];
			}

			if ((tmp = strchr(UTF8String, '?')) != NULL) {
				*tmp = '\0';

				_query = [[[OFString stringWithUTF8String:
				    tmp + 1] stringByURLDecoding] copy];
				_URLEncodedQuery = [[OFString alloc]
				    initWithUTF8String: tmp + 1];
			}

			UTF8String--;
			*UTF8String = '/';

			_URLEncodedPath = [[OFString alloc]
			_path = [[[OFString stringWithUTF8String: UTF8String]
			    initWithUTF8String: UTF8String];
			    stringByURLDecoding] copy];
		}

		objc_autoreleasePoolPop(pool);
	} @catch (id e) {
		[self release];
		@throw e;
	} @finally {
447
448
449
450
451
452
453
454
455


456
457
458


459
460
461
462
463
464
465
466
467
468

469

470
471
472
473
474

475

476
477
478
479

480

481
482
483
484
485

486
487
488
489



490
491

492
493

494

495

496
497
498
499
500
501
502
443
444
445
446
447
448
449


450
451
452


453
454
455
456
457
458
459
460
461
462
463
464
465

466

467
468
469
470
471

472

473
474
475
476

477

478
479
480

481

482


483
484
485
486

487


488
489
490

491
492
493
494
495
496
497
498







-
-
+
+

-
-
+
+










+
-
+
-




+
-
+
-



+
-
+
-



-
+
-

-
-
+
+
+

-
+
-
-
+

+
-
+








	self = [super init];

	@try {
		void *pool = objc_autoreleasePoolPush();
		char *tmp;

		_scheme = [URL->_scheme copy];
		_host = [URL->_host copy];
		_URLEncodedScheme = [URL->_URLEncodedScheme copy];
		_URLEncodedHost = [URL->_URLEncodedHost copy];
		_port = [URL->_port copy];
		_user = [URL->_user copy];
		_password = [URL->_password copy];
		_URLEncodedUser = [URL->_URLEncodedUser copy];
		_URLEncodedPassword = [URL->_URLEncodedPassword copy];

		if ((UTF8String2 = of_strdup([string UTF8String])) == NULL)
			@throw [OFOutOfMemoryException
			     exceptionWithRequestedSize:
			     [string UTF8StringLength]];

		UTF8String = UTF8String2;

		if ((tmp = strchr(UTF8String, '#')) != NULL) {
			*tmp = '\0';
			_URLEncodedFragment = [[OFString alloc]
			_fragment = [[[OFString stringWithUTF8String: tmp + 1]
			    initWithUTF8String: tmp + 1];
			    stringByURLDecoding] copy];
		}

		if ((tmp = strchr(UTF8String, '?')) != NULL) {
			*tmp = '\0';
			_URLEncodedQuery = [[OFString alloc]
			_query = [[[OFString stringWithUTF8String: tmp + 1]
			    initWithUTF8String: tmp + 1];
			    stringByURLDecoding] copy];
		}

		if (*UTF8String == '/')
			_URLEncodedPath = [[OFString alloc]
			_path = [[[OFString stringWithUTF8String: UTF8String]
			    initWithUTF8String: UTF8String];
			    stringByURLDecoding] copy];
		else {
			OFString *path, *s;

			path = [[OFString stringWithUTF8String: UTF8String]
			path = [OFString stringWithUTF8String: UTF8String];
			    stringByURLDecoding];

			if ([URL->_path hasSuffix: @"/"])
				s = [URL->_path stringByAppendingString: path];
			if ([URL->_URLEncodedPath hasSuffix: @"/"])
				s = [URL->_URLEncodedPath
				    stringByAppendingString: path];
			else
				s = [OFString stringWithFormat: @"%@/../%@",
				s = [OFString stringWithFormat:
								URL->_path,
								path];
				    @"%@/../%@", URL->_URLEncodedPath, path];

			_URLEncodedPath =
			_path = [[s stringByStandardizingURLPath] copy];
			    [[s stringByStandardizingURLPath] copy];
		}

		objc_autoreleasePoolPop(pool);
	} @catch (id e) {
		[self release];
		@throw e;
	} @finally {
576
577
578
579
580
581
582
583
584


585
586
587
588
589
590





591
592
593
594
595
596
597
598
599
600
601
602
603

604

605

606

607
608
609

610

611

612

613

614

615

616

617

618

619
620
621
622
623
624
625
626
627
628
629
630
631


632
633
634
635
636
637





638
639
640
641
642
643
644
645
646

647
648
649
650
651

652
653
654
655
656
657

658
659
660
661
662

663
664
665
666
667
668
669
670
671
672
673

674
675
676
677
678

679
680
681
682
683
684

685
686
687
688
689
690

691
692
693
694
695

696
697
698
699
700

701
702
703
704
705
706

707
708
709
710
711
712


713
714
715
716
717



718
719
720





721
722

723
724
725
726
727
728
729
730
731
572
573
574
575
576
577
578


579
580
581





582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600

601
602
603

604
605
606
607
608

609
610
611

612
613
614

615
616
617

618
619
620

621
622
623
624
625
626
627
628
629
630
631
632


633
634
635





636
637
638
639
640
641
642
643
644
645
646
647
648

649
650
651
652
653

654

655
656
657
658

659
660
661
662
663

664

665
666
667
668
669
670
671
672
673

674
675
676
677
678

679

680
681
682
683

684
685
686
687
688


689
690
691
692
693

694
695
696
697
698

699

700
701
702
703

704
705
706
707
708


709
710
711
712
713
714

715
716
717
718


719
720
721
722
723
724

725


726
727
728
729
730
731
732







-
-
+
+

-
-
-
-
-
+
+
+
+
+













+
-
+

+
-
+



+
-
+

+
-
+

+
-
+

+
-
+

+
-
+











-
-
+
+

-
-
-
-
-
+
+
+
+
+








-
+




-
+
-




-
+




-
+
-









-
+




-
+
-




-
+




-
-
+




-
+




-
+
-




-
+




-
-
+
+




-
+
+
+

-
-
+
+
+
+
+

-
+
-
-







	}

	return self;
}

- (void)dealloc
{
	[_scheme release];
	[_host release];
	[_URLEncodedScheme release];
	[_URLEncodedHost release];
	[_port release];
	[_user release];
	[_password release];
	[_path release];
	[_query release];
	[_fragment release];
	[_URLEncodedUser release];
	[_URLEncodedPassword release];
	[_URLEncodedPath release];
	[_URLEncodedQuery release];
	[_URLEncodedFragment release];

	[super dealloc];
}

- (bool)isEqual: (id)object
{
	OFURL *URL;

	if (![object isKindOfClass: [OFURL class]])
		return false;

	URL = object;

	if (URL->_URLEncodedScheme != _URLEncodedScheme &&
	if (URL->_scheme != _scheme && ![URL->_scheme isEqual: _scheme])
	    ![URL->_URLEncodedScheme isEqual: _URLEncodedScheme])
		return false;
	if (URL->_URLEncodedHost != _URLEncodedHost &&
	if (URL->_host != _host && ![URL->_host isEqual: _host])
	    ![URL->_URLEncodedHost isEqual: _URLEncodedHost])
		return false;
	if (URL->_port != _port && ![URL->_port isEqual: _port])
		return false;
	if (URL->_URLEncodedUser != _URLEncodedUser &&
	if (URL->_user != _user && ![URL->_user isEqual: _user])
	    ![URL->_URLEncodedUser isEqual: _URLEncodedUser])
		return false;
	if (URL->_URLEncodedPassword != _URLEncodedPassword &&
	if (URL->_password != _password && ![URL->_password isEqual: _password])
	    ![URL->_URLEncodedPassword isEqual: _URLEncodedPassword])
		return false;
	if (URL->_URLEncodedPath != _URLEncodedPath &&
	if (URL->_path != _path && ![URL->_path isEqual: _path])
	    ![URL->_URLEncodedPath isEqual: _URLEncodedPath])
		return false;
	if (URL->_URLEncodedQuery != _URLEncodedQuery &&
	if (URL->_query != _query && ![URL->_query isEqual: _query])
	    ![URL->_URLEncodedQuery isEqual: _URLEncodedQuery])
		return false;
	if (URL->_URLEncodedFragment != _URLEncodedFragment &&
	if (URL->_fragment != _fragment && ![URL->_fragment isEqual: _fragment])
	    ![URL->_URLEncodedFragment isEqual: _URLEncodedFragment])
		return false;

	return true;
}

- (uint32_t)hash
{
	uint32_t hash;

	OF_HASH_INIT(hash);

	OF_HASH_ADD_HASH(hash, [_scheme hash]);
	OF_HASH_ADD_HASH(hash, [_host hash]);
	OF_HASH_ADD_HASH(hash, [_URLEncodedScheme hash]);
	OF_HASH_ADD_HASH(hash, [_URLEncodedHost hash]);
	OF_HASH_ADD_HASH(hash, [_port hash]);
	OF_HASH_ADD_HASH(hash, [_user hash]);
	OF_HASH_ADD_HASH(hash, [_password hash]);
	OF_HASH_ADD_HASH(hash, [_path hash]);
	OF_HASH_ADD_HASH(hash, [_query hash]);
	OF_HASH_ADD_HASH(hash, [_fragment hash]);
	OF_HASH_ADD_HASH(hash, [_URLEncodedUser hash]);
	OF_HASH_ADD_HASH(hash, [_URLEncodedPassword hash]);
	OF_HASH_ADD_HASH(hash, [_URLEncodedPath hash]);
	OF_HASH_ADD_HASH(hash, [_URLEncodedQuery hash]);
	OF_HASH_ADD_HASH(hash, [_URLEncodedFragment hash]);

	OF_HASH_FINALIZE(hash);

	return hash;
}

- (OFString *)scheme
{
	return _scheme;
	return [_URLEncodedScheme stringByURLDecoding];
}

- (OFString *)URLEncodedScheme
{
	return [_scheme stringByURLEncodingWithAllowedCharacters:
	return _URLEncodedScheme;
	    [OFCharacterSet URLSchemeAllowedCharacterSet]];
}

- (OFString *)host
{
	return _host;
	return [_URLEncodedHost stringByURLDecoding];
}

- (OFString *)URLEncodedHost
{
	return [_host stringByURLEncodingWithAllowedCharacters:
	return _URLEncodedHost;
	    [OFCharacterSet URLHostAllowedCharacterSet]];
}

- (OFNumber *)port
{
	return _port;
}

- (OFString *)user
{
	return _user;
	return [_URLEncodedUser stringByURLDecoding];
}

- (OFString *)URLEncodedUser
{
	return [_user stringByURLEncodingWithAllowedCharacters:
	return _URLEncodedUser;
	    [OFCharacterSet URLUserAllowedCharacterSet]];
}

- (OFString *)password
{
	return _password;
	return [_URLEncodedPassword stringByURLDecoding];
}

- (OFString *)URLEncodedPassword
{
	return [_password stringByURLEncodingWithAllowedCharacters:
	    [OFCharacterSet URLPasswordAllowedCharacterSet]];
	return _URLEncodedPassword;
}

- (OFString *)path
{
	return _path;
	return [_URLEncodedPath stringByURLDecoding];
}

- (OFString *)URLEncodedPath
{
	return [_path stringByURLEncodingWithAllowedCharacters:
	return _URLEncodedPath;
	    [OFCharacterSet URLPathAllowedCharacterSet]];
}

- (OFArray *)pathComponents
{
	return [_path componentsSeparatedByString: @"/"];
	return [[self path] componentsSeparatedByString: @"/"];
}

- (OFString *)lastPathComponent
{
	void *pool;
	OFString *path;
	void *pool = objc_autoreleasePoolPush();
	OFString *path = [self path];
	const char *UTF8String, *lastComponent;
	size_t length;
	OFString *ret;

	if (_path == nil)
	if (path == nil) {
		objc_autoreleasePoolPop(pool);

		return nil;

	if ([_path isEqual: @"/"])
	}

	if ([path isEqual: @"/"]) {
		objc_autoreleasePoolPop(pool);

		return @"";

	}
	pool = objc_autoreleasePoolPush();
	path = _path;

	if ([path hasSuffix: @"/"])
		path = [path substringWithRange:
		    of_range(0, [path length] - 1)];

	UTF8String = lastComponent = [path UTF8String];
	length = [path UTF8StringLength];
744
745
746
747
748
749
750
751

752
753
754
755
756

757
758
759
760
761
762

763
764
765
766
767
768

769
770
771
772
773
774
775
776
777
778
779
780
781
782


783
784
785
786
787
788





789
790
791
792
793
794
795
796
797
798
799
800
801
802

803
804

805
806

807
808
809


810
811
812


813
814
815
816
817


818
819
820

821
822
823
824


825
826
827


828
829
830
831
832
833
834
835
836
837
838
839
840
841

842
843
844

845
846
847

848
849
850
851
852
853
854
745
746
747
748
749
750
751

752
753
754
755
756

757

758
759
760
761

762
763
764
765
766


767
768
769
770
771
772
773
774
775
776
777
778
779


780
781
782





783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798

799

800
801

802
803

804



805
806
807


808
809
810
811
812


813
814
815
816

817
818
819


820
821
822


823
824


825
826
827
828
829
830
831
832
833
834
835

836
837
838

839
840
841

842
843
844
845
846
847
848
849







-
+




-
+
-




-
+




-
-
+












-
-
+
+

-
-
-
-
-
+
+
+
+
+











-

-
+

-
+

-
+
-
-
-
+
+

-
-
+
+



-
-
+
+


-
+


-
-
+
+

-
-
+
+
-
-











-
+


-
+


-
+







	objc_autoreleasePoolPop(pool);

	return [ret autorelease];
}

- (OFString *)query
{
	return _query;
	return [_URLEncodedQuery stringByURLDecoding];
}

- (OFString *)URLEncodedQuery
{
	return [_query stringByURLEncodingWithAllowedCharacters:
	return _URLEncodedQuery;
	    [OFCharacterSet URLQueryAllowedCharacterSet]];
}

- (OFString *)fragment
{
	return _fragment;
	return [_URLEncodedFragment stringByURLDecoding];
}

- (OFString *)URLEncodedFragment
{
	return [_fragment stringByURLEncodingWithAllowedCharacters:
	    [OFCharacterSet URLFragmentAllowedCharacterSet]];
	return _URLEncodedFragment;
}

- (id)copy
{
	return [self retain];
}

- (id)mutableCopy
{
	OFMutableURL *copy = [[OFMutableURL alloc] init];

	@try {
		[copy setScheme: _scheme];
		[copy setHost: _host];
		[copy setURLEncodedScheme: _URLEncodedScheme];
		[copy setURLEncodedHost: _URLEncodedHost];
		[copy setPort: _port];
		[copy setUser: _user];
		[copy setPassword: _password];
		[copy setPath: _path];
		[copy setQuery: _query];
		[copy setFragment: _fragment];
		[copy setURLEncodedUser: _URLEncodedUser];
		[copy setURLEncodedPassword: _URLEncodedPassword];
		[copy setURLEncodedPath: _URLEncodedPath];
		[copy setURLEncodedQuery: _URLEncodedQuery];
		[copy setURLEncodedFragment: _URLEncodedFragment];
	} @catch (id e) {
		[copy release];
		@throw e;
	}

	return copy;
}

- (OFString *)string
{
	OFMutableString *ret = [OFMutableString string];
	void *pool = objc_autoreleasePoolPush();

	[ret appendFormat: @"%@://", [self URLEncodedScheme]];
	[ret appendFormat: @"%@://", _URLEncodedScheme];

	if (_user != nil && _password != nil)
	if (_URLEncodedUser != nil && _URLEncodedPassword != nil)
		[ret appendFormat: @"%@:%@@",
				   [self URLEncodedUser],
				   _URLEncodedUser, _URLEncodedPassword];
				   [self URLEncodedPassword]];
	else if (_user != nil)
		[ret appendFormat: @"%@@", [self URLEncodedUser]];
	else if (_URLEncodedUser != nil)
		[ret appendFormat: @"%@@", _URLEncodedUser];

	if (_host != nil)
		[ret appendString: [self URLEncodedHost]];
	if (_URLEncodedHost != nil)
		[ret appendString: _URLEncodedHost];
	if (_port != nil)
		[ret appendFormat: @":%@", _port];

	if (_path != nil) {
		if (![_path hasPrefix: @"/"])
	if (_URLEncodedPath != nil) {
		if (![_URLEncodedPath hasPrefix: @"/"])
			@throw [OFInvalidFormatException exception];

		[ret appendString: [self URLEncodedPath]];
		[ret appendString: _URLEncodedPath];
	}

	if (_query != nil)
		[ret appendFormat: @"?%@", [self URLEncodedQuery]];
	if (_URLEncodedQuery != nil)
		[ret appendFormat: @"?%@", _URLEncodedQuery];

	if (_fragment != nil)
		[ret appendFormat: @"#%@", [self URLEncodedFragment]];
	if (_URLEncodedFragment != nil)
		[ret appendFormat: @"#%@", _URLEncodedFragment];

	objc_autoreleasePoolPop(pool);

	[ret makeImmutable];

	return ret;
}

- (OFString *)fileSystemRepresentation
{
	void *pool = objc_autoreleasePoolPush();
	OFString *path;

	if (![_scheme isEqual: @"file"])
	if (![_URLEncodedScheme isEqual: @"file"])
		@throw [OFInvalidArgumentException exception];

	if (![_path hasPrefix: @"/"])
	if (![_URLEncodedPath hasPrefix: @"/"])
		@throw [OFInvalidFormatException exception];

	path = _path;
	path = [self path];

	if ([path hasSuffix: @"/"])
		path = [path substringWithRange:
		    of_range(0, [path length] - 1)];

#ifndef OF_PATH_STARTS_WITH_SLASH
	path = [path substringWithRange: of_range(1, [path length] - 1)];

Modified tests/OFURLTests.m from [96f3992920] to [291a443212].

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







-
+







+










-
+







#import "OFAutoreleasePool.h"

#import "OFInvalidFormatException.h"

#import "TestsAppDelegate.h"

static OFString *module = @"OFURL";
static OFString *url_str = @"ht%3Atp://us%3Aer:p%40w@ho%3Ast:1234/"
static OFString *url_str = @"ht%3atp://us%3Aer:p%40w@ho%3Ast:1234/"
    @"pa%3Fth?que%23ry#frag%23ment";

@implementation TestsAppDelegate (OFURLTests)
- (void)URLTests
{
	OFAutoreleasePool *pool = [[OFAutoreleasePool alloc] init];
	OFURL *u1, *u2, *u3, *u4;
	OFMutableURL *mu;

	TEST(@"+[URLWithString:]",
	    R(u1 = [OFURL URLWithString: url_str]) &&
	    R(u2 = [OFURL URLWithString: @"http://foo:80"]) &&
	    R(u3 = [OFURL URLWithString: @"http://bar/"]) &&
	    R(u4 = [OFURL URLWithString: @"file:///etc/passwd"]))

	TEST(@"+[URLWithString:relativeToURL:]",
	    [[[OFURL URLWithString: @"/foo"
		     relativeToURL: u1] string] isEqual:
	    @"ht%3Atp://us%3Aer:p%40w@ho%3Ast:1234/foo"] &&
	    @"ht%3atp://us%3Aer:p%40w@ho%3Ast:1234/foo"] &&
	    [[[OFURL URLWithString: @"foo/bar?q"
		     relativeToURL: [OFURL URLWithString: @"http://h/qux/quux"]]
	    string] isEqual: @"http://h/qux/foo/bar?q"] &&
	    [[[OFURL URLWithString: @"foo/bar"
		     relativeToURL: [OFURL URLWithString: @"http://h/qux/?x"]]
	    string] isEqual: @"http://h/qux/foo/bar"] &&
	    [[[OFURL URLWithString: @"http://foo/?q"
107
108
109
110
111
112
113






























114
115
116
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+



	    [[OFURL URLWithString: @"HTTP://bar/"] isEqual: u3])

	TEST(@"-[hash:]", [u1 hash] == [u4 hash] && [u2 hash] != [u3 hash])

	EXPECT_EXCEPTION(@"Detection of invalid format",
	    OFInvalidFormatException, [OFURL URLWithString: @"http"])

	mu = [OFMutableURL URL];

	TEST(@"-[setScheme:]",
	    R([mu setScheme: @"ht:tp"]) &&
	    [[mu URLEncodedScheme] isEqual: @"ht%3Atp"])

	TEST(@"-[setHost:]",
	    R([mu setHost: @"ho:st"]) &&
	    [[mu URLEncodedHost] isEqual: @"ho%3Ast"])

	TEST(@"-[setUser:]",
	    R([mu setUser: @"us:er"]) &&
	    [[mu URLEncodedUser] isEqual: @"us%3Aer"])

	TEST(@"-[setPassword:]",
	    R([mu setPassword: @"pass:word"]) &&
	    [[mu URLEncodedPassword] isEqual: @"pass%3Aword"])

	TEST(@"-[setPath:]",
	    R([mu setPath: @"pa/th@?"]) &&
	    [[mu URLEncodedPath] isEqual: @"pa/th@%3F"])

	TEST(@"-[setQuery:]",
	    R([mu setQuery: @"que/ry?#"]) &&
	    [[mu URLEncodedQuery] isEqual: @"que/ry?%23"])

	TEST(@"-[setFragment:]",
	    R([mu setFragment: @"frag/ment?#"]) &&
	    [[mu URLEncodedFragment] isEqual: @"frag/ment?%23"])

	[pool drain];
}
@end