1/*
2 * Copyright 2014, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
4 */
5
6
7#include <errno.h>
8#include <fcntl.h>
9#include <getopt.h>
10#include <stdio.h>
11#include <stdlib.h>
12#include <string.h>
13#include <unistd.h>
14
15#include <new>
16
17#include <File.h>
18#include <String.h>
19
20#include <package/hpkg/HPKGDefs.h>
21#include <package/hpkg/PackageWriter.h>
22
23#include <DataPositionIOWrapper.h>
24#include <FdIO.h>
25#include <SHA256.h>
26
27#include "package.h"
28#include "PackageWriterListener.h"
29
30
31using BPackageKit::BHPKG::BPackageWriter;
32using BPackageKit::BHPKG::BPackageWriterListener;
33using BPackageKit::BHPKG::BPackageWriterParameters;
34
35
36struct ChecksumIO : BDataIO {
37	ChecksumIO()
38	{
39		fChecksummer.Init();
40	}
41
42	virtual ssize_t Write(const void* buffer, size_t size)
43	{
44		if (size > 0)
45			fChecksummer.Update(buffer, size);
46		return (ssize_t)size;
47	}
48
49	BString Digest()
50	{
51		const uint8* digest = fChecksummer.Digest();
52		BString hex;
53		size_t length = fChecksummer.DigestLength();
54		char* buffer = hex.LockBuffer(length * 2);
55		if (buffer == NULL)
56		{
57			throw std::bad_alloc();
58		}
59
60		for (size_t i = 0; i < length; i++)
61			snprintf(buffer + 2 * i, 3, "%02x", digest[i]);
62
63		hex.UnlockBuffer();
64		return hex;
65	}
66
67private:
68	SHA256	fChecksummer;
69};
70
71
72static BPositionIO*
73create_stdio(bool isInput)
74{
75	BFdIO* dataIO = new BFdIO(isInput ? 0 : 1, false);
76	return new BDataPositionIOWrapper(dataIO);
77}
78
79
80int
81command_checksum(int argc, const char* const* argv)
82{
83	bool quiet = false;
84	bool verbose = false;
85
86	while (true) {
87		static struct option sLongOptions[] = {
88			{ "help", no_argument, 0, 'h' },
89			{ "quiet", no_argument, 0, 'q' },
90			{ "verbose", no_argument, 0, 'v' },
91			{ 0, 0, 0, 0 }
92		};
93
94		opterr = 0; // don't print errors
95		int c = getopt_long(argc, (char**)argv, "+hqv",
96			sLongOptions, NULL);
97		if (c == -1)
98			break;
99
100		switch (c) {
101			case 'h':
102				print_usage_and_exit(false);
103				break;
104
105			case 'q':
106				quiet = true;
107				break;
108
109			case 'v':
110				verbose = true;
111				break;
112
113			default:
114				print_usage_and_exit(true);
115				break;
116		}
117	}
118
119	// The optional remaining argument is the package file.
120	if (argc - optind > 1)
121		print_usage_and_exit(true);
122
123	const char* packageFileName = optind < argc ? argv[optind++] : NULL;
124
125	// open the input package
126	status_t error = B_OK;
127	BPositionIO* inputFile;
128	if (packageFileName == NULL || strcmp(packageFileName, "-") == 0) {
129		inputFile = create_stdio(true);
130	} else {
131		BFile* inputFileFile = new BFile;
132		error = inputFileFile->SetTo(packageFileName, O_RDONLY);
133		if (error != B_OK) {
134			fprintf(stderr, "Error: Failed to open input file \"%s\": %s\n",
135				packageFileName, strerror(error));
136			return 1;
137		}
138		inputFile = inputFileFile;
139	}
140
141	// write the output package to a BDataIO that computes the checksum
142	BPackageWriterParameters writerParameters;
143	writerParameters.SetCompressionLevel(0);
144	writerParameters.SetCompression(
145		BPackageKit::BHPKG::B_HPKG_COMPRESSION_NONE);
146
147	PackageWriterListener listener(verbose, quiet);
148	BPackageWriter packageWriter(&listener);
149	ChecksumIO outputFile;
150	error = packageWriter.Init(new BDataPositionIOWrapper(&outputFile), true,
151		&writerParameters);
152	if (error != B_OK)
153		return 1;
154
155	error = packageWriter.Recompress(inputFile);
156	if (error != B_OK)
157		return 1;
158
159	printf("%s\n", outputFile.Digest().String());
160
161	return 0;
162}
163