1 /* vim: set ts=8 sts=4 sw=4 tw=80 noet: */
2 /*======================================================================
3 Copyright (C) 2004,2005,2009,2013 Walter Doekes <walter+tthsum@wjd.nu>
4 This file is part of tthsum.
5
6 tthsum is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 tthsum is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with tthsum. If not, see <http://www.gnu.org/licenses/>.
18 ======================================================================*/
19 #include "tthsum.h"
20
21 #include "base32.h"
22 #include "escape.h"
23 #include "read.h"
24 #include "thex.h"
25 #include "types.h"
26 #include "utf8.h"
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30
31 #ifdef USE_TEXTS
32 # include "texts.h"
33 #else /* !USE_TEXTS */
34 # define get_error() "Fail"
35 #endif /* !USE_TEXTS */
36
37 #define MY_PATH_MAX 32768
38
39
40 static void tthsum_progress(uint64_t current_pos, uint64_t end_pos);
41 static struct rofile* tthsum_rofopen(const char* filename,
42 const struct tthsum_options* options);
43
tthsum_generate_root(const char * filename,const struct tthsum_options * opt)44 static int tthsum_generate_root(const char* filename,
45 const struct tthsum_options* opt) {
46 struct rofile* stream;
47 uint64_t hash[3];
48 char hash_base32[40];
49 /* unicode will be at most equal sized to the multibyte charset */
50 wchar_t filename_uni[MY_PATH_MAX + 1];
51 /* utf8/C-escaping will be at most 4 times the mbcs equivalent */
52 char filename_buf[MY_PATH_MAX * 4 + 1];
53
54 if (filename != NULL && strlen(filename) > MY_PATH_MAX) {
55 #ifdef USE_TEXTS
56 set_error("tthsum_generate_root", TTHSUM_FILENAME_TOO_LARGE);
57 #endif /* USE_TEXTS */
58 return -1;
59 }
60
61 if ((stream = tthsum_rofopen(filename, opt)) == NULL
62 || thex_tiger_root(stream, hash,
63 opt->progress_every_mib ? tthsum_progress : NULL) == -1) {
64 if (stream != NULL)
65 rofclose(stream);
66 return -1;
67 }
68 rofclose(stream);
69
70 if (uint64tobase32(hash_base32, hash, 3) == -1)
71 return -1;
72 if (filename != NULL) {
73 /* the order of calling to_utf8 or to_ctrlesc doesn't matter,
74 as they operate on chars 0-127 and 128-255 respectively. */
75 #ifdef _WIN32
76 /* change all \\ to / on Windows */
77 char filename_tmp[MY_PATH_MAX + 1];
78 char* p;
79 for (p = filename_tmp; *filename != '\0'; ++filename)
80 if (*filename == '\\')
81 *p++ = '/';
82 else
83 *p++ = *filename;
84 *p = '\0';
85 strtoctrlesc(filename_buf, filename_tmp);
86 #else /* !_WIN32 */
87 strtoctrlesc(filename_buf, filename);
88 #endif /* !_WIN32 */
89 if (mbstowcs(filename_uni, filename_buf, MY_PATH_MAX + 1)
90 == (size_t)-1) {
91 if (!opt->has_locale)
92 fprintf(stderr, "tthsum: warning: Locale settings are C/POSIX. "
93 "See the tthsum manpage for help.\n");
94 #ifdef USE_TEXTS
95 set_error("mbstowcs", ERROR_FROM_OS);
96 #endif /* USE_TEXTS */
97 return -1;
98 }
99 if (wcstoutf8(filename_buf, filename_uni, MY_PATH_MAX * 4 + 1)
100 == (size_t)-1)
101 return -1;
102 } else {
103 strcpy(filename_buf, "-");
104 }
105
106 /* print our root hash with utf8/escaped filename */
107 printf("%s %s\n", hash_base32, filename_buf);
108 return 0;
109 }
110
tthsum_generate_roots(const char * filenames[],int files,const struct tthsum_options * opt)111 int tthsum_generate_roots(const char* filenames[], int files,
112 const struct tthsum_options* opt) {
113 int i;
114 for (i = 0; i < files; ++i) {
115 if (tthsum_generate_root(filenames[i], opt) == -1)
116 #ifdef USE_TEXTS
117 fprintf(stderr, "tthsum: `%s': %s\n",
118 filenames[i] ? filenames[i] : "-", get_error());
119 #else /* !USE_TEXTS */
120 fprintf(stderr, "tthsum: `%s': Fail\n",
121 filenames[i] ? filenames[i] : "-");
122 #endif /* !USE_TEXTS */
123 }
124 return 0;
125 }
126
tthsum_check_digest_line(char * line,unsigned line_len,const struct tthsum_options * opt)127 static int tthsum_check_digest_line(char* line,
128 unsigned line_len, const struct tthsum_options* opt) {
129 /* 39char base32, 2 spaces, MY_PATH_MAX utf8/C-esc file, 1 terminating \0 */
130 wchar_t filename_uni[MY_PATH_MAX * 4 + 1];
131 char filename_mbs[MY_PATH_MAX * 4 + 1];
132 char filename[MY_PATH_MAX * 4 + 1];
133 struct rofile* stream;
134 uint64_t hash_line[3] = {_ULL(0), _ULL(0), _ULL(0)};
135 uint64_t hash_file[3];
136 char* linep = line;
137 char tmp;
138
139 /* must be at least 39 base32 + 2 spaces + 1 filename long */
140 if (line_len < 42 || line_len > (MY_PATH_MAX * 4 + 39 + 2)) {
141 #ifdef USE_TEXTS
142 set_error("tthsum_check_digest_line", TTHSUM_LINE_CORRUPT);
143 #endif /* USE_TEXTS */
144 return -1;
145 }
146 /* read base32 hash */
147 if (base32touint64(hash_line, linep, 3) == -1)
148 return -1;
149 /* check the two spaces */
150 linep += 39;
151 if (*linep != ' ' || *++linep != ' ') {
152 #ifdef USE_TEXTS
153 set_error("tthsum_check_digest_line", TTHSUM_LINE_CORRUPT);
154 #endif /* USE_TEXTS */
155 return -1;
156 }
157 ++linep;
158 /* fetch filename and convert to local codepage.
159 * unfortunately our windows utf8 routine doesn't return success unless
160 * it has found a terminating null, sigh */
161 tmp = *(line + line_len);
162 *(line + line_len) = '\0';
163 if (utf8towcs(filename_uni, linep, MY_PATH_MAX * 4 + 1)
164 == (size_t)-1) {
165 *(line + line_len) = tmp;
166 return -1;
167 }
168 *(line + line_len) = tmp; /* yes, uncorrupting the crap */
169 filename_uni[MY_PATH_MAX * 4] = '\0'; /* make sure we're terminated */
170 /* proceed */
171 if (wcstombs(filename_mbs, filename_uni, MY_PATH_MAX * 4 + 1)
172 == (size_t)-1) {
173 #ifdef USE_TEXTS
174 set_error("wcstombs", ERROR_FROM_OS);
175 #endif /* USE_TEXTS */
176 return -1;
177 }
178 filename_mbs[MY_PATH_MAX * 4] = '\0'; /* make sure we're terminated */
179 if (ctrlesctostr(filename, filename_mbs) == -1)
180 return -1;
181
182 /* w00t, got filename. go check TTH on it */
183 if ((stream = tthsum_rofopen(filename, opt)) == NULL
184 || thex_tiger_root(stream, hash_file,
185 opt->progress_every_mib ? tthsum_progress : NULL) == -1) {
186 if (opt->verbose)
187 fprintf(stderr, "%-14s FAILED (%s)\n", filename, get_error());
188 else
189 fprintf(stderr, "tthsum: `%s': %s\n", filename, get_error());
190 if (stream != NULL)
191 rofclose(stream);
192 return 0;
193 }
194 rofclose(stream);
195
196 /* compare tth's */
197 if (memcmp(hash_line, hash_file, 3 * 8) == 0) {
198 if (opt->verbose)
199 printf("%-14s OK\n", filename);
200 return 1;
201 }
202
203 /* not equal, print an error */
204 if (!opt->verbose) {
205 #ifdef USE_TEXTS
206 fprintf(stderr, "tthsum: %s `%s'\n", get_text(TTHSUM_MISMATCHED_TTH),
207 filename);
208 #else /* !USE_TEXTS */
209 fprintf(stderr, "tthsum: TTH check failed for `%s'\n", filename);
210 #endif /* !USE_TEXTS */
211 } else {
212 printf("%-14s FAILED\n", filename);
213 }
214 return 0;
215 }
216
tthsum_check_digest(const char * filename,const struct tthsum_options * opt)217 int tthsum_check_digest(const char* filename,
218 const struct tthsum_options* opt) {
219 unsigned filesize;
220 char* file_buf = rof_readall(
221 filename == NULL ? rofopen_sysfd_stdin() : rofopen_sysfile(filename),
222 &filesize
223 );
224 char* file_buf_end;
225 char* s;
226 char* e;
227 unsigned line_no = 1;
228 unsigned lines_correct = 0;
229 unsigned lines_incorrect = 0;
230 unsigned files_correct = 0;
231
232 if (filename == NULL)
233 filename = "-";
234 if (file_buf == NULL) {
235 #ifdef USE_TEXTS
236 fprintf(stderr, "tthsum: `%s': %s\n", filename, get_error());
237 #else /* !USE_TEXTS */
238 fprintf(stderr, "tthsum: `%s': Fail\n", filename);
239 #endif /* !USE_TEXTS */
240 return 1;
241 }
242 /* Operate on a line by line basis.. we would like to get the most out
243 * of corrupt or un-decodable files (esp. if the decoding fails for some
244 * files only) */
245 file_buf_end = file_buf + filesize;
246 s = e = file_buf;
247 while (1) {
248 int ret;
249 /* find \r or \n, or really, any ctrl-character will do, as they're all
250 escaped */
251 while (e != file_buf_end && (unsigned char)*e >= 0x20)
252 ++e;
253 /* test the line */
254 ret = tthsum_check_digest_line(s, e - s, opt);
255 if (ret == 1) {
256 ++lines_correct;
257 ++files_correct;
258 } else if (ret == 0) {
259 ++lines_correct;
260 } else {
261 if (opt->warn)
262 #ifdef USE_TEXTS
263 fprintf(stderr, "tthsum: `%s': %u: %s\n", filename, line_no,
264 get_error());
265 #else /* !USE_TEXTS */
266 fprintf(stderr, "tthsum: `%s': %u: Fail\n", filename, line_no);
267 #endif /* !USE_TEXTS */
268 ++lines_incorrect;
269 }
270 /* find the only true line separator, the \n */
271 s = e;
272 while (s != file_buf_end && *s != '\n')
273 ++s;
274 if (s == file_buf_end || s + 1 == file_buf_end) /* expect \n at eof */
275 break;
276 /* start on the next line */
277 e = ++s;
278 ++line_no;
279 }
280 /* free our readfile data */
281 free(file_buf);
282
283 if (files_correct == 0 && lines_correct == 0) {
284 fprintf(stderr, "tthsum: `%s': No files checked\n", filename);
285 return -1;
286 } else if (lines_correct != files_correct) {
287 if (opt->verbose)
288 fprintf(stderr, "tthsum: %u of %u file(s) failed TTH check\n",
289 lines_correct - files_correct, lines_correct);
290 return -1;
291 }
292 return 0;
293 }
294
tthsum_progress(uint64_t current_pos,uint64_t end_pos)295 static void tthsum_progress(uint64_t current_pos, uint64_t end_pos) {
296 if (end_pos != (uint64_t)-1)
297 fprintf(stderr, "(%0.1f%%)\r",
298 100.0 * (float)current_pos / (float)end_pos);
299 else
300 fprintf(stderr, "(%" PRIu64 ")\r", current_pos);
301 }
302
tthsum_rofopen(const char * filename,const struct tthsum_options * options)303 static struct rofile* tthsum_rofopen(const char* filename,
304 const struct tthsum_options* options) {
305 if (filename == NULL)
306 return rofopen_sysfd_stdin();
307 else if (!options->use_mmap)
308 return rofopen_sysfile(filename);
309 return rofopen_mmap(filename);
310 }
311