File: OFHash.m

package info (click to toggle)
objfw 1.4.2-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 8,644 kB
  • sloc: objc: 101,491; asm: 5,083; sh: 4,092; makefile: 1,574; ansic: 709; xml: 367; pascal: 214
file content (368 lines) | stat: -rw-r--r-- 9,901 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
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
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
145
146
147
148
149
150
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
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
223
224
225
226
227
228
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
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
355
356
357
358
359
360
361
362
363
364
365
366
367
368
/*
 * Copyright (c) 2008-2025 Jonathan Schleifer <js@nil.im>
 *
 * All rights reserved.
 *
 * This program is free software: you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License version 3.0 only,
 * as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
 * version 3.0 for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3.0 along with this program. If not, see
 * <https://www.gnu.org/licenses/>.
 */

#include "config.h"

#import "OFApplication.h"
#import "OFArray.h"
#import "OFDate.h"
#import "OFFile.h"
#import "OFIRI.h"
#import "OFIRIHandler.h"
#import "OFLocale.h"
#import "OFMD5Hash.h"
#import "OFOptionsParser.h"
#import "OFRIPEMD160Hash.h"
#import "OFSHA1Hash.h"
#import "OFSHA224Hash.h"
#import "OFSHA256Hash.h"
#import "OFSHA384Hash.h"
#import "OFSHA512Hash.h"
#import "OFSandbox.h"
#import "OFSecureData.h"
#import "OFStdIOStream.h"
#import "OFSystemInfo.h"

#ifdef HAVE_TLS_SUPPORT
# import "ObjFWTLS.h"
#endif

#import "OFOpenItemFailedException.h"
#import "OFReadFailedException.h"

#define BUFFER_SIZE 16384

#ifdef OF_AMIGAOS
const char *VER = "$VER: ofhash " OF_PREPROCESSOR_STRINGIFY(OBJFW_VERSION_MAJOR)
    "." OF_PREPROCESSOR_STRINGIFY(OBJFW_VERSION_MINOR) " (" BUILD_DATE ") "
    "\xA9 2008-2025 Jonathan Schleifer";
#endif

@interface OFHash: OFObject <OFApplicationDelegate>
{
	uint8_t _buffer[BUFFER_SIZE];
}

#ifdef OF_AMIGAOS
- (void)handleBreakCtrlC: (ULONG)signal;
#endif
@end

#ifdef HAVE_TLS_SUPPORT
void
_reference_to_ObjFWTLS(void)
{
	_ObjFWTLS_reference = 1;
}
#endif

OF_APPLICATION_DELEGATE(OFHash)

static void
help(void)
{
	[OFStdErr writeLine: OF_LOCALIZED(@"usage",
	    @"Usage: %[prog] [--md5] [--ripemd160] [--sha1] [--sha224] "
	    @"[--sha256] [--sha384] [--sha512] [--iri] [--version] "
	    @"file1 [file2 ...]",
	    @"prog", [OFApplication programName])];

	[OFApplication terminateWithStatus: 1];
}

static void
version(void)
{
	[OFStdOut writeFormat: @"ofhash %@ (ObjFW %@) "
			       @"<https://objfw.nil.im/>\n"
			       @"Copyright (c) 2008-2025 Jonathan Schleifer "
			       @"<js@nil.im>\n"
			       @"Licensed under the LGPL 3.0 "
			       @"<https://www.gnu.org/licenses/lgpl-3.0.html>"
			       @"\n",
			       @PACKAGE_VERSION, [OFSystemInfo ObjFWVersion]];
	[OFApplication terminate];
}

static void
printHash(OFString *algo, OFString *path, id <OFCryptographicHash> hash)
{
	size_t digestSize = hash.digestSize;
	const unsigned char *digest;

	[hash calculate];
	digest = hash.digest;

	[OFStdOut writeFormat: @"%@ ", algo];

	for (size_t i = 0; i < digestSize; i++)
		[OFStdOut writeFormat: @"%02x", digest[i]];

	[OFStdOut writeFormat: @"  %@\n", path];
}

@implementation OFHash
- (void)applicationDidFinishLaunching: (OFNotification *)notification
{
	int exitStatus = 0;
	bool calculateMD5, calculateRIPEMD160, calculateSHA1, calculateSHA224;
	bool calculateSHA256, calculateSHA384, calculateSHA512, isIRI;
	const OFOptionsParserOption options[] = {
		{ '\0', @"md5", 0, &calculateMD5, NULL },
		{ '\0', @"ripemd160", 0, &calculateRIPEMD160, NULL },
		{ '\0', @"rmd160", 0, &calculateRIPEMD160, NULL },
		{ '\0', @"sha1", 0, &calculateSHA1, NULL },
		{ '\0', @"sha224", 0, &calculateSHA224, NULL },
		{ '\0', @"sha256", 0, &calculateSHA256, NULL },
		{ '\0', @"sha384", 0, &calculateSHA384, NULL },
		{ '\0', @"sha512", 0, &calculateSHA512, NULL },
		{ '\0', @"iri", 0, &isIRI, NULL },
		{ '\0', @"version", 0, NULL, NULL },
		{ '\0', nil, 0, NULL, NULL }
	};
	OFOptionsParser *optionsParser =
	    [OFOptionsParser parserWithOptions: options];
	OFUnichar option;
	OFMD5Hash *MD5Hash = nil;
	OFRIPEMD160Hash *RIPEMD160Hash = nil;
	OFSHA1Hash *SHA1Hash = nil;
	OFSHA224Hash *SHA224Hash = nil;
	OFSHA256Hash *SHA256Hash = nil;
	OFSHA384Hash *SHA384Hash = nil;
	OFSHA512Hash *SHA512Hash = nil;
#ifdef OF_AMIGAOS
	OFDate *distantPast = [OFDate distantPast];
#endif

#ifndef OF_AMIGAOS
	[OFLocale addLocalizationDirectoryIRI:
	    [OFIRI fileIRIWithPath: @LOCALIZATION_DIR]];
#else
	[OFLocale addLocalizationDirectoryIRI:
	    [OFIRI fileIRIWithPath: @"PROGDIR:/Data/ofhash/localization"]];
#endif

#ifdef OF_AMIGAOS
	[[OFRunLoop mainRunLoop] addExecSignal: SIGBREAKB_CTRL_C
					target: self
				      selector: @selector(handleBreakCtrlC:)];
#endif

	while ((option = [optionsParser nextOption]) != '\0') {
		switch (option) {
		case '-':
			if ([optionsParser.lastLongOption isEqual: @"version"])
				version();
			break;
		case '?':
			if (optionsParser.lastLongOption != nil)
				[OFStdErr writeLine: OF_LOCALIZED(
				    @"unknown_long_option",
				    @"%[prog]: Unknown option: --%[opt]",
				    @"prog", [OFApplication programName],
				    @"opt", optionsParser.lastLongOption)];
			else {
				OFString *optStr = [OFString stringWithFormat:
				    @"%C", optionsParser.lastOption];
				[OFStdErr writeLine: OF_LOCALIZED(
				    @"unknown_option",
				    @"%[prog]: Unknown option: -%[opt]",
				    @"prog", [OFApplication programName],
				    @"opt", optStr)];
			}

			[OFApplication terminateWithStatus: 1];
			break;
		}
	}

#ifdef OF_HAVE_SANDBOX
	OFSandbox *sandbox = [OFSandbox sandbox];
	@try {
		sandbox.allowsStdIO = true;
		sandbox.allowsReadingFiles = true;
		sandbox.allowsUserDatabaseReading = true;

		if (!isIRI)
			for (OFString *path in optionsParser.remainingArguments)
				[sandbox unveilPath: path permissions: @"r"];

		[sandbox unveilPath: @LOCALIZATION_DIR permissions: @"r"];

		[OFApplication of_activateSandbox: sandbox];
	} @finally {
		objc_release(sandbox);
	}
#endif

	if (!calculateMD5 && !calculateRIPEMD160 && !calculateSHA1 &&
	    !calculateSHA224 && !calculateSHA256 && !calculateSHA384 &&
	    !calculateSHA512)
		help();

	if (optionsParser.remainingArguments.count < 1)
		help();

	if (calculateMD5)
		MD5Hash = [OFMD5Hash hashWithAllowsSwappableMemory: true];
	if (calculateRIPEMD160)
		RIPEMD160Hash =
		    [OFRIPEMD160Hash hashWithAllowsSwappableMemory: true];
	if (calculateSHA1)
		SHA1Hash = [OFSHA1Hash hashWithAllowsSwappableMemory: true];
	if (calculateSHA224)
		SHA224Hash = [OFSHA224Hash hashWithAllowsSwappableMemory: true];
	if (calculateSHA256)
		SHA256Hash = [OFSHA256Hash hashWithAllowsSwappableMemory: true];
	if (calculateSHA384)
		SHA384Hash = [OFSHA384Hash hashWithAllowsSwappableMemory: true];
	if (calculateSHA512)
		SHA512Hash = [OFSHA512Hash hashWithAllowsSwappableMemory: true];

	for (OFString *path in optionsParser.remainingArguments) {
		void *pool = objc_autoreleasePoolPush();
		OFStream *file;

#ifdef OF_AMIGAOS
		/* Spin run loop once to see if we got Ctrl-C. */
		[[OFRunLoop mainRunLoop] runMode: OFDefaultRunLoopMode
				      beforeDate: distantPast];
#endif

		if (!isIRI && [path isEqual: @"-"])
			file = OFStdIn;
		else {
			@try {
				if (isIRI) {
					OFIRI *IRI =
					    [OFIRI IRIWithString: path];

					file = [OFIRIHandler
					    openItemAtIRI: IRI
						     mode: @"r"];
				} else
					file = [OFFile fileWithPath: path
							       mode: @"r"];
			} @catch (OFOpenItemFailedException *e) {
				OFString *error = [OFString
				    stringWithCString: strerror(e.errNo)
					     encoding: [OFLocale encoding]];
				OFString *filePath =
				    (e.IRI != nil ? e.IRI.string : e.path);

				[OFStdErr writeLine: OF_LOCALIZED(
				    @"failed_to_open_file",
				    @"Failed to open file %[file]: %[error]",
				    @"file", filePath,
				    @"error", error)];

				exitStatus = 1;
				goto outer_loop_end;
			}
		}

		[MD5Hash reset];
		[RIPEMD160Hash reset];
		[SHA1Hash reset];
		[SHA224Hash reset];
		[SHA256Hash reset];
		[SHA384Hash reset];
		[SHA512Hash reset];

		while (!file.atEndOfStream) {
			size_t length;

#ifdef OF_AMIGAOS
			/* Spin run loop once to see if we got Ctrl-C. */
			[[OFRunLoop mainRunLoop] runMode: OFDefaultRunLoopMode
					      beforeDate: distantPast];
#endif

			@try {
				length = [file readIntoBuffer: _buffer
						       length: BUFFER_SIZE];
			} @catch (OFReadFailedException *e) {
				OFString *error = [OFString
				    stringWithCString: strerror(e.errNo)
					     encoding: [OFLocale encoding]];

				[OFStdErr writeLine: OF_LOCALIZED(
				    @"failed_to_read_file",
				    @"Failed to read %[file]: %[error]",
				    @"file", path,
				    @"error", error)];

				exitStatus = 1;
				goto outer_loop_end;
			}

			if (calculateMD5)
				[MD5Hash updateWithBuffer: _buffer
						   length: length];
			if (calculateRIPEMD160)
				[RIPEMD160Hash updateWithBuffer: _buffer
							 length: length];
			if (calculateSHA1)
				[SHA1Hash updateWithBuffer: _buffer
						    length: length];
			if (calculateSHA224)
				[SHA224Hash updateWithBuffer: _buffer
						      length: length];
			if (calculateSHA256)
				[SHA256Hash updateWithBuffer: _buffer
						      length: length];
			if (calculateSHA384)
				[SHA384Hash updateWithBuffer: _buffer
						      length: length];
			if (calculateSHA512)
				[SHA512Hash updateWithBuffer: _buffer
						      length: length];
		}

		[file close];

		if (calculateMD5)
			printHash(@"MD5", path, MD5Hash);
		if (calculateRIPEMD160)
			printHash(@"RIPEMD160", path, RIPEMD160Hash);
		if (calculateSHA1)
			printHash(@"SHA1", path, SHA1Hash);
		if (calculateSHA224)
			printHash(@"SHA224", path, SHA224Hash);
		if (calculateSHA256)
			printHash(@"SHA256", path, SHA256Hash);
		if (calculateSHA384)
			printHash(@"SHA384", path, SHA384Hash);
		if (calculateSHA512)
			printHash(@"SHA512", path, SHA512Hash);

outer_loop_end:
		objc_autoreleasePoolPop(pool);
	}

	[OFApplication terminateWithStatus: exitStatus];
}

#ifdef OF_AMIGAOS
- (void)handleBreakCtrlC: (ULONG)signal
{
	raise(SIGINT);
}
#endif
@end