ObjFW  Check-in [3e455c4839]

Overview
Comment:OFURI: Make scheme and path nonnull

This is as per the RFC, which says the scheme and path can never be
missing, while the path can be empty.

Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 3e455c48393cbcad58a1714e910025a6eb40829a22da05423d7098334fcaba06
User & Date: js on 2022-10-11 19:27:09
Other Links: manifest | tags
Context
2022-10-14
16:11
OFOpenItemFailedException: Improve description check-in: 67b1d67088 user: js tags: trunk
2022-10-11
19:27
OFURI: Make scheme and path nonnull check-in: 3e455c4839 user: js tags: trunk
00:28
+[URIForFile{ -> Path}:inArchive{ -> WithURI}:] check-in: a8bc0b31d0 user: js tags: trunk
Changes

Modified src/OFArchiveURIHandler.m from [99f47b2418] to [010e8050b6].

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







-
+












-








@end

OFURI *
OFArchiveURIHandlerURIForFileInArchive(OFString *scheme,
    OFString *pathInArchive, OFURI *archiveURI)
{
	static OFOnceControl onceControl = OFOnceControlInitValue;
	OFMutableURI *ret = [OFMutableURI URI];
	OFMutableURI *ret = [OFMutableURI URIWithScheme: scheme];
	void *pool = objc_autoreleasePoolPush();
	OFString *archiveURIString;

	OFOnce(&onceControl, initPathAllowedCharacters);

	pathInArchive = [pathInArchive
	    stringByAddingPercentEncodingWithAllowedCharacters:
	    pathAllowedCharacters];
	archiveURIString = [archiveURI.string
	    stringByAddingPercentEncodingWithAllowedCharacters:
	    pathAllowedCharacters];

	ret.scheme = scheme;
	ret.percentEncodedPath = [OFString
	    stringWithFormat: @"%@!%@", archiveURIString, pathInArchive];
	[ret makeImmutable];

	objc_autoreleasePoolPop(pool);

	return ret;
}

Modified src/OFHTTPServer.m from [684e137d81] to [e75cf57b42].

530
531
532
533
534
535
536
537

538
539
540
541
542
543
544
545
530
531
532
533
534
535
536

537

538
539
540
541
542
543
544







-
+
-







		}

		[_host release];
		_host = [_server.host copy];
		_port = [_server port];
	}

	URI = [OFMutableURI URI];
	URI = [OFMutableURI URIWithScheme: @"http"];
	URI.scheme = @"http";
	URI.host = _host;
	if (_port != 80)
		URI.port = [OFNumber numberWithUnsignedShort: _port];

	@try {
		if ((pos = [_path rangeOfString: @"?"].location) !=
		    OFNotFound) {

Modified src/OFMutableURI.h from [c01390b76e] to [9400cfdc05].

30
31
32
33
34
35
36
37

38
39
40
41
42
43
44
30
31
32
33
34
35
36

37
38
39
40
41
42
43
44







-
+








/**
 * @brief The scheme part of the URI.
 *
 * @throw OFInvalidFormatException The scheme being set is not in the correct
 *				   format
 */
@property OF_NULLABLE_PROPERTY (readwrite, copy, nonatomic) OFString *scheme;
@property (readwrite, copy, nonatomic) OFString *scheme;

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

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







-
+










-
-
+









-
+







 */
@property OF_NULLABLE_PROPERTY (readwrite, copy, nonatomic)
    OFString *percentEncodedPassword;

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

/**
 * @brief The path part of the URI in percent-encoded form.
 *
 * Setting this retains the original percent-encoding used - if more characters
 * than necessary are percent-encoded, it is kept this way.
 *
 * @throw OFInvalidFormatException The path being set is not in the correct
 *				   format
 */
@property OF_NULLABLE_PROPERTY (readwrite, copy, nonatomic)
    OFString *percentEncodedPath;
@property (readwrite, copy, nonatomic) OFString *percentEncodedPath;

/**
 * @brief The path of the URI split into components.
 *
 * The first component must always be empty to designate the root.
 *
 * @throw OFInvalidFormatException The path components being set are not in the
 *				   correct format
 */
@property OF_NULLABLE_PROPERTY (readwrite, copy, nonatomic)
@property (readwrite, copy, nonatomic)
    OFArray OF_GENERIC(OFString *) *pathComponents;

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

170
171
172
173
174
175
176
177

178

179
180
181










182
183
184
185
186
187
188
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







-
+

+


-
+
+
+
+
+
+
+
+
+
+







 * @throw OFInvalidFormatException The fragment being set is not in the correct
 *				   format
 */
@property OF_NULLABLE_PROPERTY (readwrite, copy, nonatomic)
    OFString *percentEncodedFragment;

/**
 * @brief Creates a new mutable URI.
 * @brief Creates a new mutable URI with the specified schemed.
 *
 * @param scheme The scheme for the URI
 * @return A new, autoreleased OFMutableURI
 */
+ (instancetype)URI;
+ (instancetype)URIWithScheme: (OFString *)scheme;

/**
 * @brief Initializes an already allocated mutable URI with the specified
 *	  schemed.
 *
 * @param scheme The scheme for the URI
 * @return An initialized OFMutableURI
 */
- (instancetype)initWithScheme: (OFString *)scheme;

/**
 * @brief Appends the specified path component.
 *
 * @param component The component to append
 */
- (void)appendPathComponent: (OFString *)component;

Modified src/OFMutableURI.m from [800b8aa913] to [375c07c449].

12
13
14
15
16
17
18

19
20
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
12
13
14
15
16
17
18
19
20
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







+


















-
+

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







 * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
 * file.
 */

#include "config.h"

#import "OFMutableURI.h"
#import "OFURI+Private.h"
#import "OFArray.h"
#import "OFDictionary.h"
#ifdef OF_HAVE_FILES
# import "OFFileManager.h"
#endif
#import "OFNumber.h"
#import "OFPair.h"
#import "OFString.h"

#import "OFInvalidArgumentException.h"
#import "OFInvalidFormatException.h"

@implementation OFMutableURI
@dynamic scheme, host, percentEncodedHost, port, user, percentEncodedUser;
@dynamic password, percentEncodedPassword, path, percentEncodedPath;
@dynamic pathComponents, query, percentEncodedQuery, queryItems, fragment;
@dynamic percentEncodedFragment;

+ (instancetype)URI
+ (instancetype)URIWithScheme: (OFString *)scheme
{
	return [[[self alloc] init] autorelease];
	return [[[self alloc] initWithScheme: scheme] autorelease];
}

- (instancetype)initWithScheme: (OFString *)scheme
{
	self = [super of_init];

	@try {
		self.scheme = scheme;
		_percentEncodedPath = @"";
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

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

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
208
188
189
190
191
192
193
194



195
196
197
198
199
200
201
202
203
204
205
206





207
208
209


210
211
212
213
214
215
216
217
218
219
220
221
222







-
-
-
+
+










-
-
-
-
-



-
-
+
+
+
+
+
+







	objc_autoreleasePoolPop(pool);
}

- (void)setPercentEncodedPath: (OFString *)percentEncodedPath
{
	OFString *old;

	if (percentEncodedPath != nil)
		OFURIVerifyIsEscaped(percentEncodedPath,
		    [OFCharacterSet URIPathAllowedCharacterSet], true);
	OFURIVerifyIsEscaped(percentEncodedPath,
	    [OFCharacterSet URIPathAllowedCharacterSet], true);

	old = _percentEncodedPath;
	_percentEncodedPath = [percentEncodedPath copy];
	[old release];
}

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

	if (components == nil) {
		self.path = nil;
		return;
	}

	if (components.count == 0)
		@throw [OFInvalidFormatException exception];

	if ([components.firstObject length] != 0)
		@throw [OFInvalidFormatException exception];
	if ([components.firstObject isEqual: @"/"]) {
		OFMutableArray *mutComponents =
		    [[components mutableCopy] autorelease];
		[mutComponents replaceObjectAtIndex: 0 withObject: @""];
		components = mutComponents;
	}

	self.path = [components componentsJoinedByString: @"/"];

	objc_autoreleasePoolPop(pool);
}

- (void)setQuery: (OFString *)query
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
375
376
377
378
379
380
381

382
383

384
385
386





387
388
389



390
391
392
393
394
395
396
397
398







-
+

-
+


-
-
-
-
-



-
-
-

+







	_percentEncodedPath = [path retain];

	objc_autoreleasePoolPop(pool);
}

- (void)standardizePath
{
	void *pool;
	void *pool = objc_autoreleasePoolPush();
	OFMutableArray OF_GENERIC(OFString *) *array;
	bool done = false, endsWithEmpty;
	bool done = false, startsWithEmpty, endsWithEmpty;
	OFString *path;

	if (_percentEncodedPath == nil)
		return;

	pool = objc_autoreleasePoolPush();

	array = [[[_percentEncodedPath
	    componentsSeparatedByString: @"/"] mutableCopy] autorelease];

	if ([array.firstObject length] != 0)
		@throw [OFInvalidFormatException exception];

	endsWithEmpty = ([array.lastObject length] == 0);
	startsWithEmpty = ([array.firstObject length] == 0);

	while (!done) {
		size_t length = array.count;

		done = true;

		for (size_t i = 0; i < length; i++) {
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
414
415
416
417
418
419
420
421

422
423
424
425
426

427
428
429

430
431
432
433
434
435
436
437
438
439







+
-
+




-
+


-
+










				done = false;
				break;
			}
		}
	}

	if (startsWithEmpty)
	[array insertObject: @"" atIndex: 0];
		[array insertObject: @"" atIndex: 0];
	if (endsWithEmpty)
		[array addObject: @""];

	path = [array componentsJoinedByString: @"/"];
	if (path.length == 0)
	if (startsWithEmpty && path.length == 0)
		path = @"/";

	[self setPercentEncodedPath: path];
	self.percentEncodedPath = path;

	objc_autoreleasePoolPop(pool);
}

- (void)makeImmutable
{
	object_setClass(self, [OFURI class]);
}
@end

Added src/OFURI+Private.h version [c5dc8bc44b].

























1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/*
 * Copyright (c) 2008-2022 Jonathan Schleifer <js@nil.im>
 *
 * All rights reserved.
 *
 * This file is part of ObjFW. It may be distributed under the terms of the
 * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
 * the packaging of this file.
 *
 * Alternatively, it may be distributed under the terms of the GNU General
 * Public License, either version 2 or 3, which can be found in the file
 * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
 * file.
 */

#import "OFURI.h"

OF_ASSUME_NONNULL_BEGIN

@interface OFURI ()
- (instancetype)of_init OF_METHOD_FAMILY(init);
@end

OF_ASSUME_NONNULL_END

Modified src/OFURI.h from [9ffb55b5cf] to [b3a4d03434].

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







-
+




-
+








-
+







/**
 * @class OFURI OFURI.h ObjFW/OFURI.h
 *
 * @brief A class for parsing URIs as per RFC 3986 and accessing parts of it.
 */
@interface OFURI: OFObject <OFCopying, OFMutableCopying, OFSerialization>
{
	OFString *_Nullable _scheme;
	OFString *_scheme;
	OFString *_Nullable _percentEncodedHost;
	OFNumber *_Nullable _port;
	OFString *_Nullable _percentEncodedUser;
	OFString *_Nullable _percentEncodedPassword;
	OFString *_Nullable _percentEncodedPath;
	OFString *_percentEncodedPath;
	OFString *_Nullable _percentEncodedQuery;
	OFString *_Nullable _percentEncodedFragment;
	OF_RESERVE_IVARS(OFURI, 4)
}

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

/**
 * @brief The host part of the URI.
 */
@property OF_NULLABLE_PROPERTY (readonly, copy, nonatomic) OFString *host;

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







-
+




-
-
+






-
+







-
-
+







 */
@property OF_NULLABLE_PROPERTY (readonly, copy, nonatomic)
    OFString *percentEncodedPassword;

/**
 * @brief The path part of the URI.
 */
@property OF_NULLABLE_PROPERTY (readonly, copy, nonatomic) OFString *path;
@property (readonly, copy, nonatomic) OFString *path;

/**
 * @brief The path part of the URI in percent-encoded form.
 */
@property OF_NULLABLE_PROPERTY (readonly, copy, nonatomic)
    OFString *percentEncodedPath;
@property (readonly, copy, nonatomic) OFString *percentEncodedPath;

/**
 * @brief The path of the URI split into components.
 *
 * The first component must always be `/` to designate the root.
 */
@property OF_NULLABLE_PROPERTY (readonly, copy, nonatomic)
@property (readonly, copy, nonatomic)
    OFArray OF_GENERIC(OFString *) *pathComponents;

/**
 * @brief The last path component of the URI.
 *
 * Returns the empty string if the path is the root.
 */
@property OF_NULLABLE_PROPERTY (readonly, copy, nonatomic)
    OFString *lastPathComponent;
@property (readonly, copy, nonatomic) OFString *lastPathComponent;

/**
 * @brief The query part of the URI.
 */
@property OF_NULLABLE_PROPERTY (readonly, copy, nonatomic) OFString *query;

/**
261
262
263
264
265
266
267


268
269
270
271
272
273
274
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274







+
+







 * @param isDirectory Whether the path is a directory, in which case a slash is
 *		      appened if there is no slash yet
 * @return An initialized OFURI
 */
- (instancetype)initFileURIWithPath: (OFString *)path
			isDirectory: (bool)isDirectory;
#endif

- (instancetype)init OF_UNAVAILABLE;

/**
 * @brief Returns a new URI with the specified path component appended.
 *
 * If the URI is a file URI, the file system is queried whether the appended
 * component is a directory.
 *

Modified src/OFURI.m from [57bba32cb0] to [fd812a6a57].

833
834
835
836
837
838
839










840
841
842
843
844
845
846
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856







+
+
+
+
+
+
+
+
+
+







		[self release];
		@throw e;
	}

	return self;
}
#endif

- (instancetype)init
{
	OF_INVALID_INIT_METHOD
}

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

- (instancetype)initWithSerialization: (OFXMLElement *)element
{
	void *pool = objc_autoreleasePoolPush();
	OFString *stringValue;

	@try {
883
884
885
886
887
888
889
890

891
892
893
894
895
896
897
898
899
900
901
902
903
904

905
906
907
908
909
910
911
893
894
895
896
897
898
899

900
901
902
903
904
905
906
907
908
909
910
911
912


913
914
915
916
917
918
919
920







-
+












-
-
+







		return true;

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

	URI = object;

	if (URI->_scheme != _scheme && ![URI->_scheme isEqual: _scheme])
	if (![URI->_scheme isEqual: _scheme])
		return false;
	if (URI->_percentEncodedHost != _percentEncodedHost &&
	    ![URI->_percentEncodedHost isEqual: _percentEncodedHost])
		return false;
	if (URI->_port != _port && ![URI->_port isEqual: _port])
		return false;
	if (URI->_percentEncodedUser != _percentEncodedUser &&
	    ![URI->_percentEncodedUser isEqual: _percentEncodedUser])
		return false;
	if (URI->_percentEncodedPassword != _percentEncodedPassword &&
	    ![URI->_percentEncodedPassword isEqual: _percentEncodedPassword])
		return false;
	if (URI->_percentEncodedPath != _percentEncodedPath &&
	    ![URI->_percentEncodedPath isEqual: _percentEncodedPath])
	if (![URI->_percentEncodedPath isEqual: _percentEncodedPath])
		return false;
	if (URI->_percentEncodedQuery != _percentEncodedQuery &&
	    ![URI->_percentEncodedQuery isEqual: _percentEncodedQuery])
		return false;
	if (URI->_percentEncodedFragment != _percentEncodedFragment &&
	    ![URI->_percentEncodedFragment isEqual: _percentEncodedFragment])
		return false;
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1055
1056
1057
1058
1059
1060
1061





1062
1063
1064
1065
1066
1067
1068







-
-
-
-
-







{
	void *pool = objc_autoreleasePoolPush();
	OFString *path = _percentEncodedPath;
	const char *UTF8String, *lastComponent;
	size_t length;
	OFString *ret;

	if (path == nil) {
		objc_autoreleasePoolPop(pool);
		return nil;
	}

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

	if ([path hasSuffix: @"/"])
		path = [path substringToIndex: path.length - 1];
1144
1145
1146
1147
1148
1149
1150
1151

1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1148
1149
1150
1151
1152
1153
1154

1155
1156
1157

1158
1159
1160
1161
1162
1163
1164







-
+


-







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

- (id)mutableCopy
{
	OFURI *copy = [[OFMutableURI alloc] init];
	OFURI *copy = [[OFMutableURI alloc] initWithScheme: _scheme];

	@try {
		copy->_scheme = [_scheme copy];
		copy->_percentEncodedHost = [_percentEncodedHost copy];
		copy->_port = [_port copy];
		copy->_percentEncodedUser = [_percentEncodedUser copy];
		copy->_percentEncodedPassword = [_percentEncodedPassword copy];
		copy->_percentEncodedPath = [_percentEncodedPath copy];
		copy->_percentEncodedQuery = [_percentEncodedQuery copy];
		copy->_percentEncodedFragment = [_percentEncodedFragment copy];
1185
1186
1187
1188
1189
1190
1191
1192
1193

1194
1195
1196
1197
1198
1199
1200
1188
1189
1190
1191
1192
1193
1194


1195
1196
1197
1198
1199
1200
1201
1202







-
-
+







		[ret appendFormat: @"%@@", _percentEncodedUser];

	if (_percentEncodedHost != nil)
		[ret appendString: _percentEncodedHost];
	if (_port != nil)
		[ret appendFormat: @":%@", _port];

	if (_percentEncodedPath != nil)
		[ret appendString: _percentEncodedPath];
	[ret appendString: _percentEncodedPath];

	if (_percentEncodedQuery != nil)
		[ret appendFormat: @"?%@", _percentEncodedQuery];

	if (_percentEncodedFragment != nil)
		[ret appendFormat: @"#%@", _percentEncodedFragment];

Modified tests/OFURITests.m from [ba89cb38e8] to [e9cb46977b].

208
209
210
211
212
213
214
215

216
217
218
219
220
221
222
208
209
210
211
212
213
214

215
216
217
218
219
220
221
222







-
+







	    [[OFURI URIWithString: @"HTTP://bar/"] isEqual: URI3])

	TEST(@"-[hash:]", URI1.hash == URI4.hash && URI2.hash != URI3.hash)

	EXPECT_EXCEPTION(@"Detection of invalid format",
	    OFInvalidFormatException, [OFURI URIWithString: @"http"])

	mutableURI = [OFMutableURI URI];
	mutableURI = [OFMutableURI URIWithScheme: @"dummy"];

	EXPECT_EXCEPTION(
	    @"-[setPercentEncodedScheme:] with invalid characters fails",
	    OFInvalidFormatException, mutableURI.scheme = @"%20")

	TEST(@"-[setHost:]",
	    (mutableURI.host = @"ho:st") &&