ObjFW  Diff

Differences From Artifact [cc6aaa39b2]:

To Artifact [c3a4b738ae]:


70
71
72
73
74
75
76
77

78
79

80
81
82
83
84




85
86
87
88
89
90
91
70
71
72
73
74
75
76

77
78

79
80
81



82
83
84
85
86
87
88
89
90
91
92







-
+

-
+


-
-
-
+
+
+
+







@end

OF_APPLICATION_DELEGATE(OFHTTP)

static void
help(OFStream *stream, bool full, int status)
{
	[of_stderr writeString:
	[of_stderr writeLine:
	    OF_LOCALIZED(@"usage",
	    @"Usage: %[prog] -[cehHmoOPqv] url1 [url2 ...]\n",
	    @"Usage: %[prog] -[cehHmoOPqv] url1 [url2 ...]",
	    @"prog", [OFApplication programName])];

	if (full)
		[stream writeString: OF_LOCALIZED(@"full_usage",
		    @"\nOptions:\n    "
	if (full) {
		[stream writeString: @"\n"];
		[stream writeLine: OF_LOCALIZED(@"full_usage",
		    @"Options:\n    "
		    @"-b  --body           "
		    @"  Specify the file to send as body\n    "
		    @"-c  --continue       "
		    @"  Continue download of existing file\n    "
		    @"-f  --force          "
		    @"  Force / overwrite existing file\n    "
		    @"-h  --help           "
101
102
103
104
105
106
107
108


109
110
111
112
113
114
115
102
103
104
105
106
107
108

109
110
111
112
113
114
115
116
117







-
+
+







		    @"-P  --proxy          "
		    @"  Specify SOCKS5 proxy\n    "
		    @"-q  --quiet          "
		    @"  Quiet mode (no output, except errors)\n    "
		    @"-v  --verbose        "
		    @"  Verbose mode (print headers)\n    "
		    @"    --insecure       "
		    @"  Ignore TLS errors\n")];
		    @"  Ignore TLS errors")];
	}

	[OFApplication terminateWithStatus: status];
}

@implementation OFHTTP
- init
{
136
137
138
139
140
141
142
143
144


145
146
147
148
149
150
151
138
139
140
141
142
143
144


145
146
147
148
149
150
151
152
153







-
-
+
+








- (void)addHeader: (OFString*)header
{
	size_t pos = [header rangeOfString: @":"].location;
	OFString *name, *value;

	if (pos == OF_NOT_FOUND) {
		[of_stderr writeString: OF_LOCALIZED(@"invalid_input_header",
		    @"%[prog]: Headers must to be in format name:value!\n",
		[of_stderr writeLine: OF_LOCALIZED(@"invalid_input_header",
		    @"%[prog]: Headers must to be in format name:value!",
		    @"prog", [OFApplication programName])];
		[OFApplication terminateWithStatus: 1];
	}

	name = [header substringWithRange: of_range(0, pos)];
	name = [name stringByDeletingEnclosingWhitespaces];

186
187
188
189
190
191
192
193
194


195
196
197
198
199
200
201
188
189
190
191
192
193
194


195
196
197
198
199
200
201
202
203







-
-
+
+







	else if ([method isEqual: @"PUT"])
		_method = OF_HTTP_REQUEST_METHOD_PUT;
	else if ([method isEqual: @"DELETE"])
		_method = OF_HTTP_REQUEST_METHOD_DELETE;
	else if ([method isEqual: @"TRACE"])
		_method = OF_HTTP_REQUEST_METHOD_TRACE;
	else {
		[of_stderr writeString: OF_LOCALIZED(@"invalid_input_method",
		    @"%[prog]: Invalid request method %[method]!\n",
		[of_stderr writeLine: OF_LOCALIZED(@"invalid_input_method",
		    @"%[prog]: Invalid request method %[method]!",
		    @"prog", [OFApplication programName],
		    @"method", method)];
		[OFApplication terminateWithStatus: 1];
	}

	objc_autoreleasePoolPop(pool);
}
218
219
220
221
222
223
224
225
226


227
228
229
230
231
232
233
220
221
222
223
224
225
226


227
228
229
230
231
232
233
234
235







-
-
+
+








		if (port > UINT16_MAX)
			@throw [OFOutOfRangeException exception];

		[OFTCPSocket setSOCKS5Host: host];
		[OFTCPSocket setSOCKS5Port: (uint16_t)port];
	} @catch (OFInvalidFormatException *e) {
		[of_stderr writeString: OF_LOCALIZED(@"invalid_input_proxy",
		    @"%[prog]: Proxy must to be in format host:port!\n",
		[of_stderr writeLine: OF_LOCALIZED(@"invalid_input_proxy",
		    @"%[prog]: Proxy must to be in format host:port!",
		    @"prog", [OFApplication programName])];
		[OFApplication terminateWithStatus: 1];
	}
}

- (void)applicationDidFinishLaunching
{
268
269
270
271
272
273
274
275

276
277
278

279
280
281
282
283
284
285

286
287
288

289
290
291
292
293
294
295
296
297
298



299
300
301
302
303
304
305
306

307
308

309
310
311
312
313
314
315

316
317

318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334

335
336

337
338
339
340
341
342

343
344
345

346
347
348
349
350
351
352
270
271
272
273
274
275
276

277
278
279

280
281
282
283
284
285
286

287
288
289

290
291
292
293
294
295
296
297



298
299
300
301
302
303
304
305
306
307

308
309

310
311
312
313
314
315
316

317
318

319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335

336
337

338
339
340
341
342
343

344
345
346

347
348
349
350
351
352
353
354







-
+


-
+






-
+


-
+







-
-
-
+
+
+







-
+

-
+






-
+

-
+
















-
+

-
+





-
+


-
+







			[self setMethod: [optionsParser argument]];
			break;
		case 'P':
			[self setProxy: [optionsParser argument]];
			break;
		case ':':
			if ([optionsParser lastLongOption] != nil)
				[of_stderr writeString:
				[of_stderr writeLine:
				    OF_LOCALIZED(@"long_argument_missing",
				    @"%[prog]: Argument for option --%[opt] "
				    "missing\n"
				    @"missing"
				    @"prog", [OFApplication programName],
				    @"opt", [optionsParser lastLongOption])];
			else {
				OFString *optStr = [OFString
				    stringWithFormat: @"%c",
				    [optionsParser lastOption]];
				[of_stderr writeString:
				[of_stderr writeLine:
				    OF_LOCALIZED(@"argument_missing",
				    @"%[prog]: Argument for option -%[opt] "
				    "missing\n",
				    @"missing",
				    @"prog", [OFApplication programName],
				    @"opt", optStr)];
			}

			[OFApplication terminateWithStatus: 1];
			break;
		case '=':
			[of_stderr writeString:
			    OF_LOCALIZED(@"takes_no_argument",
			    @"%[prog]: Option --%[opt] takes no argument\n",
			[of_stderr writeLine:
			    OF_LOCALIZED(@"option_takes_no_argument",
			    @"%[prog]: Option --%[opt] takes no argument",
			    @"prog", [OFApplication programName],
			    @"opt", [optionsParser lastLongOption])];

			[OFApplication terminateWithStatus: 1];
			break;
		case '?':
			if ([optionsParser lastLongOption] != nil)
				[of_stderr writeString:
				[of_stderr writeLine:
				    OF_LOCALIZED(@"unknown_long_option",
				    @"%[prog]: Unknown option: --%[opt]\n",
				    @"%[prog]: Unknown option: --%[opt]",
				    @"prog", [OFApplication programName],
				    @"opt", [optionsParser lastLongOption])];
			else {
				OFString *optStr = [OFString
				    stringWithFormat: @"%c",
				    [optionsParser lastOption]];
				[of_stderr writeString:
				[of_stderr writeLine:
				    OF_LOCALIZED(@"unknown_option",
				    @"%[prog]: Unknown option: -%[opt]\n",
				    @"%[prog]: Unknown option: -%[opt]",
				    @"prog", [OFApplication programName],
				    @"opt", optStr)];
			}

			[OFApplication terminateWithStatus: 1];
			break;
		}
	}

	_outputPath = [outputPath copy];
	_URLs = [[optionsParser remainingArguments] retain];

	if ([_URLs count] < 1)
		help(of_stderr, false, 1);

	if (_quiet && _verbose) {
		[of_stderr writeString: OF_LOCALIZED(@"quiet_xor_verbose",
		[of_stderr writeLine: OF_LOCALIZED(@"quiet_xor_verbose",
		    @"%[prog]: -q / --quiet and -v / --verbose are mutually "
		    @"exclusive!\n",
		    @"exclusive!",
		    @"prog", [OFApplication programName])];
		[OFApplication terminateWithStatus: 1];
	}

	if (_outputPath != nil && [_URLs count] > 1) {
		[of_stderr writeString:
		[of_stderr writeLine:
		    OF_LOCALIZED(@"output_only_with_one_url",
		    @"%[prog]: Cannot use -o / --output when more than one URL "
		    @"has been specified!\n",
		    @"has been specified!",
		    @"prog", [OFApplication programName])];
		[OFApplication terminateWithStatus: 1];
	}

	[self performSelector: @selector(downloadNextURL)
		   afterDelay: 0];
}
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
434
435
436
437

438
439
440
441

442
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
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
434
435
436
437
438

439
440
441
442

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







-
+


-
+







-
+


-
+







-
+


-
+






-
+



-
+


















-
+


-
+









-
-
+
+








	@try {
		response = [_HTTPClient performRequest: request];
	} @catch (OFAddressTranslationFailedException *e) {
		if (!_quiet)
			[of_stdout writeString: @"\n"];

		[of_stderr writeString:
		[of_stderr writeLine:
		    OF_LOCALIZED(@"download_failed_address_translation",
		    @"%[prog]: Failed to download <%[url]>!\n"
		    @"  Address translation failed: %[exception]\n",
		    @"  Address translation failed: %[exception]",
		    @"prog", [OFApplication programName],
		    @"url", [[request URL] string],
		    @"exception", e)];
	} @catch (OFConnectionFailedException *e) {
		if (!_quiet)
			[of_stdout writeString: @"\n"];

		[of_stderr writeString:
		[of_stderr writeLine:
		    OF_LOCALIZED(@"download_failed_connection_failed",
		    @"%[prog]: Failed to download <%[url]>!\n"
		    @"  Connection failed: %[exception]\n",
		    @"  Connection failed: %[exception]",
		    @"prog", [OFApplication programName],
		    @"url", [[request URL] string],
		    @"exception", e)];
	} @catch (OFInvalidServerReplyException *e) {
		if (!_quiet)
			[of_stdout writeString: @"\n"];

		[of_stderr writeString:
		[of_stderr writeLine:
		    OF_LOCALIZED(@"download_failed_invalid_server_reply",
		    @"%[prog]: Failed to download <%[url]>!\n"
		    @"  Invalid server reply!\n",
		    @"  Invalid server reply!",
		    @"prog", [OFApplication programName],
		    @"url", [[request URL] string])];
	} @catch (OFUnsupportedProtocolException *e) {
		if (!_quiet)
			[of_stdout writeString: @"\n"];

		[of_stderr writeString: OF_LOCALIZED(@"no_ssl_library",
		[of_stderr writeLine: OF_LOCALIZED(@"no_ssl_library",
		    @"%[prog]: No TLS library loaded!\n"
		    @"  In order to download via https, you need to preload an "
		    @"TLS library for ObjFW\n"
		    "such as ObjOpenSSL!\n",
		    @"  such as ObjOpenSSL!",
		    @"prog", [OFApplication programName])];
	} @catch (OFReadOrWriteFailedException *e) {
		OFString *error = OF_LOCALIZED(
		    @"download_failed_read_or_write_failed_any",
		    @"Read or write failed");

		if (!_quiet)
			[of_stdout writeString: @"\n"];

		if ([e isKindOfClass: [OFReadFailedException class]])
			error = OF_LOCALIZED(
			    @"download_failed_read_or_write_failed_read",
			    @"Read failed");
		else if ([e isKindOfClass: [OFWriteFailedException class]])
			error = OF_LOCALIZED(
			    @"download_failed_read_or_write_failed_write",
			    @"Write failed");

		[of_stderr writeString:
		[of_stderr writeLine:
		    OF_LOCALIZED(@"download_failed_read_or_write_failed",
		    @"%[prog]: Failed to download <%[url]>!\n"
		    @"  %[error]: %[exception]\n",
		    @"  %[error]: %[exception]",
		    @"prog", [OFApplication programName],
		    @"url", [[request URL] string],
		    @"error", error,
		    @"exception", e)];
	} @catch (OFHTTPRequestFailedException *e) {
		if (!_quiet)
			[of_stdout writeFormat: @" ➜ %d\n",
						[[e response] statusCode]];

		[of_stderr writeString: OF_LOCALIZED(@"download_failed",
		    @"%[prog]: Failed to download <%[url]>!\n",
		[of_stderr writeLine: OF_LOCALIZED(@"download_failed",
		    @"%[prog]: Failed to download <%[url]>!",
		    @"prog", [OFApplication programName],
		    @"url", [[request URL] string])];
	}

	if (!_quiet && response != nil)
		[of_stdout writeFormat: @" ➜ %d\n", [response statusCode]];

615
616
617
618
619
620
621
622

623
624

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

624
625

626
627
628
629
630
631
632
633







-
+

-
+







		[_progressBar release];
		_progressBar = nil;

		if (!_quiet)
			[of_stdout writeString: @"\n  Error!\n"];

		URL = [_URLs objectAtIndex: _URLIndex - 1];
		[of_stderr writeString:
		[of_stderr writeLine:
		    OF_LOCALIZED(@"download_failed_exception",
		    @"%[prog]: Failed to download <%[url]>: %[exception]\n",
		    @"%[prog]: Failed to download <%[url]>: %[exception]",
		    @"prog", [OFApplication programName],
		    @"url", URL,
		    @"exception", e)];

		_errorCode = 1;
		goto next;
	}
640
641
642
643
644
645
646
647
648
649





650
651
652
653
654
655
656
642
643
644
645
646
647
648



649
650
651
652
653
654
655
656
657
658
659
660







-
-
-
+
+
+
+
+







	if ([response isAtEndOfStream] ||
	    (_length >= 0 && _received >= _length)) {
		[_progressBar stop];
		[_progressBar draw];
		[_progressBar release];
		_progressBar = nil;

		if (!_quiet)
			[of_stdout writeString:
			    OF_LOCALIZED(@"download_done", @"\n  Done!\n")];
		if (!_quiet) {
			[of_stdout writeString: @"\n  "];
			[of_stdout writeLine:
			    OF_LOCALIZED(@"download_done", @"Done!")];
		}

		goto next;
	}

	return true;

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







-
-
+
+









-
-
+
+







	if (_URLIndex >= [_URLs count])
		[OFApplication terminateWithStatus: _errorCode];

	@try {
		URLString = [_URLs objectAtIndex: _URLIndex++];
		URL = [OFURL URLWithString: URLString];
	} @catch (OFInvalidFormatException *e) {
		[of_stderr writeString: OF_LOCALIZED(@"invalid_url",
		    @"%[prog]: Invalid URL: <%[url]>!\n",
		[of_stderr writeLine: OF_LOCALIZED(@"invalid_url",
		    @"%[prog]: Invalid URL: <%[url]>!",
		    @"prog", [OFApplication programName],
		    @"url", URLString)];

		_errorCode = 1;
		goto next;
	}

	if (![[URL scheme] isEqual: @"http"] &&
	    ![[URL scheme] isEqual: @"https"]) {
		[of_stderr writeString: OF_LOCALIZED(@"invalid_scheme",
		    @"%[prog]: Invalid scheme: <%[scheme]:>!\n",
		[of_stderr writeLine: OF_LOCALIZED(@"invalid_scheme",
		    @"%[prog]: Invalid scheme: <%[scheme]:>!",
		    @"prog", [OFApplication programName],
		    @"scheme", URLString)];

		_errorCode = 1;
		goto next;
	}

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
855
856
857
858
859
860
861

862
863
864

865
866
867
868
869
870
871
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
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869

870
871
872

873
874
875
876
877
878
879
880







-
-
+
+
+
+









-
-
+
+
+

-
-
+
+
+

-
-
+
+
+









-
-
-
+
+
+













-
+


-
+







			OFDictionary OF_GENERIC(OFString*, OFString*) *headers =
			    [response headers];
			OFEnumerator *keyEnumerator = [headers keyEnumerator];
			OFEnumerator *objectEnumerator =
			    [headers objectEnumerator];
			OFString *key, *object;

			[of_stdout writeString: OF_LOCALIZED(@"info_name_nopad",
			    @"  Name: %[name]\n",
			[of_stdout writeString: @"  "];
			[of_stdout writeLine: OF_LOCALIZED(
			    @"info_name_unaligned",
			    @"Name: %[name]",
			    @"name", fileName)];

			while ((key = [keyEnumerator nextObject]) != nil &&
			    (object = [objectEnumerator nextObject]) != nil)
				[of_stdout writeFormat: @"  %@: %@\n",
							key, object];

			objc_autoreleasePoolPop(pool);
		} else {
			[of_stdout writeString: OF_LOCALIZED(@"info_name",
			    @"  Name: %[name]\n",
			[of_stdout writeString: @"  "];
			[of_stdout writeLine: OF_LOCALIZED(@"info_name",
			    @"Name: %[name]",
			    @"name", fileName)];
			[of_stdout writeString: OF_LOCALIZED(@"info_type",
			    @"  Type: %[type]\n",
			[of_stdout writeString: @"  "];
			[of_stdout writeLine: OF_LOCALIZED(@"info_type",
			    @"Type: %[type]",
			    @"type", type)];
			[of_stdout writeString: OF_LOCALIZED(@"info_size",
			    @"  Size: %[size]\n",
			[of_stdout writeString: @"  "];
			[of_stdout writeLine: OF_LOCALIZED(@"info_size",
			    @"Size: %[size]",
			    @"size", lengthString)];
		}
	}

	if ([_outputPath isEqual: @"-"])
		_output = of_stdout;
	else {
		if (!_continue && !_force &&
		    [fileManager fileExistsAtPath: fileName]) {
			[of_stderr writeString:
			    OF_LOCALIZED(@"ouput_already_exists",
			    @"%[prog]: File %[filename] already exists!\n",
			[of_stderr writeLine:
			    OF_LOCALIZED(@"output_already_exists",
			    @"%[prog]: File %[filename] already exists!",
			    @"prog", [OFApplication programName],
			    @"filename", fileName)];

			_errorCode = 1;
			goto next;
		}

		@try {
			OFString *mode =
			    ([response statusCode] == 206 ? @"ab" : @"wb");
			_output = [[OFFile alloc] initWithPath: fileName
							  mode: mode];
		} @catch (OFOpenItemFailedException *e) {
			[of_stderr writeString:
			[of_stderr writeLine:
			    OF_LOCALIZED(@"failed_to_open_output",
			    @"%[prog]: Failed to open file %[filename]: "
			    @"%[exception]\n",
			    @"%[exception]",
			    @"prog", [OFApplication programName],
			    @"filename",fileName,
			    @"exception", e)];

			_errorCode = 1;
			goto next;
		}