1 /* yydecode utility -- http://nerv.cx/liyang/
2 
3    Copyright (C) 1994, 1995 Free Software Foundation, Inc.
4 
5    This product is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; either version 2, or (at your option)
8    any later version.
9 
10    This product is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14 
15    You should have received a copy of the GNU General Public License
16    along with this product; see the file COPYING.  If not, write to
17    the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
18    02111-1307, USA.  */
19 
20 #ifdef HAVE_CONFIG_H
21 # include <config.h>
22 #endif
23 
24 #include "system.h"
25 #include "getopt.h"
26 #include "crc32.h"
27 #include "yydecode.h"
28 #include "evilchar.h"
29 #include "base64tab.h"
30 
31 /* entry point */
32 int main(int argc, char * const *argv);
33 
34 /* static prototypes */
35 static u_int32_t atocrc32(char *str);
36 static int decoded_file_open(struct decoded_file *f);
37 /*static struct decoded_file *decoded_file_lookup(struct decoded_file **list, const char *filename, const char *forced_outname);
38 static int read_yenc(const char *inname, const char *forced_outname, struct decoded_file **decoded_list, int whinge);
39 static int read_stduu(const char *inname, struct decoded_file *f);
40 static int read_base64(const char *inname, struct decoded_file *f);*/
41 static int decode(const char *inname, const char *forced_outname, struct decoded_file **decoded_list);
42 static void usage(int status);
43 
44 /* extern variables */
45 const char *program_name = NULL;
46 
47 /* command-line flags */
48 static const char *opt_directory = NULL;
49 static int opt_evil_filename = 0;
50 static int opt_clobber_filename = 0;
51 static int opt_force_overwrite = 0;
52 static int opt_write_broken = 0;
53 static int opt_broken_encoder = 0;
54 static int opt_remove_broken = 0;
55 static int opt_large_parts = 0;
56 static int opt_verbose = 0;
57 
58 /* code starts here */
59 
atocrc32(char * str)60 static u_int32_t atocrc32(char *str)
61 {
62 	return (u_int32_t)strtoul((char *)str, NULL, 16);
63 }
64 
fgets_(char * s,int size,FILE * stream)65 static char *fgets_(char *s, int size, FILE *stream)
66 {
67 	int i;
68 	int c = '\0', d;
69 	char *p = s;
70 
71 	if(size <= 1)
72 		return NULL;
73 
74 	c = fgetc(stream);
75 	if(c == EOF)
76 		return NULL;
77 	ungetc(c, stream);
78 
79 	for(i = size; i > 2; i--)
80 	{
81 		c = fgetc(stream);
82 		if((c == '\x0a') || (c == '\x0d'))
83 		{
84 			/* ick. Still, don't leave any surprises. */
85 			*p++ = '\n';
86 			break;
87 		}
88 		if(c == EOF)
89 			break;
90 		*p++ = (char)c;
91 	}
92 	*p++ = '\0';
93 
94 	/* We could do with all the empty lines being eaten up, but, as above... */
95 	do
96 	{
97 		d = fgetc(stream);
98 		if(d == EOF)
99 			return s;
100 	}
101 	while(((d == '\x0a') || (d == '\x0d')) && (d != c));
102 	ungetc(d, stream);
103 
104 	return s;
105 }
106 
decoded_file_open(struct decoded_file * f)107 static int decoded_file_open(struct decoded_file *f)
108 {
109 	int fd;
110 
111 	if(f->handle)
112 		return EXIT_SUCCESS;
113 
114 	if(!strcmp(f->outname, "-"))
115 	{
116 		f->handle = stdout;
117 		return EXIT_SUCCESS;
118 	}
119 
120 	if(f->previously_existed)
121 		return EXIT_FAILURE;
122 
123 	/* is_seekable (despite the misleading name) is always zero on the
124 	 * first run. Make sure here that we're not trying to re-open a
125 	 * seekable file. */
126 	if(!f->is_seekable)
127 	{
128 		/* blame wget for the name */
129 		int clobber_num;
130 		char *clobber_part;
131 
132 		/* If it's writable, we'll say it exists. (because that's the
133 		 * only case when we can overwrite it, and that's all we care
134 		 * about.) Otherwise, we pretend it doesn't, and let open()
135 		 * below whinge instead. */
136 		f->previously_existed = access(f->outname, W_OK) ? 0 : 1;
137 
138 		for(clobber_num = 1, clobber_part = f->outname + strlen(f->outname);
139 			f->previously_existed && !opt_force_overwrite && opt_clobber_filename;
140 			clobber_num++)
141 		{
142 			if(opt_verbose > 1)
143 				error(0, 0, _("%s: Output exists; trying .%i"), f->outname, clobber_num);
144 
145 			sprintf(clobber_part, ".%i", clobber_num);
146 			f->previously_existed = access(f->outname, W_OK) ? 0 : 1;
147 		}
148 
149 		fd = open(f->outname, O_WRONLY | O_CREAT
150 		/* Ask for exclusiveness if the file existed before we
151 		 * started, and we didn't force overwriting. In a way, we
152 		 * already know that this is going to fail, but we're lazy,
153 		 * so we let open() generate the error message for us. */
154 				| (f->previously_existed && !opt_force_overwrite
155 					? O_EXCL : 0)
156 		/* Note that we truncate (overwriting in the process) a file
157 		 * only if we're not writing broken parts. */
158 				| (f->previously_existed && opt_force_overwrite && !opt_write_broken
159 					? O_TRUNC : 0),
160 			f->mode);
161 		if(fd >= 0)
162 			close(fd);
163 		else
164 		{
165 			f->previously_existed = 1;
166 			return EXIT_FAILURE;
167 		}
168 	}
169 
170 	/* w+ truncates the file; if that's what we had wanted, then open()
171 	 * above would have already done it for us. */
172 	if(!(f->handle = fopen(f->outname, "r+b")))
173 	{
174 		f->previously_existed = 1;
175 		return EXIT_FAILURE;
176 	}
177 
178 	/* If it didn't fail above, then we 0wnz this file now <g> */
179 	f->previously_existed = 0;
180 
181 	f->is_seekable = ftell(f->handle) >= 0;
182 
183 	return EXIT_SUCCESS;
184 }
185 
decoded_file_lookup(struct decoded_file ** list,const char * filename,int mode,const char * forced_outname)186 static struct decoded_file * decoded_file_lookup(struct decoded_file **list,
187 	const char *filename, int mode, const char *forced_outname)
188 {
189 	struct decoded_file **last = list;
190 	struct decoded_file *p = *list;
191 	char *pf;
192 
193 	for(; p; p = p->next)
194 	{
195 		if(!strcmp(p->filename, filename))
196 			return p;
197 		last = &p->next;
198 	}
199 
200 	/* Requested file doesn't exist in list -- set one up. */
201 	p = malloc(sizeof(struct decoded_file));
202 	memset(p, 0, sizeof(struct decoded_file));
203 
204 	/* Not sure why we bother checking /dev/stdout -- uudecode does. */
205 	if(forced_outname && !strcmp(forced_outname, "/dev/stdout"))
206 		forced_outname = "-";
207 
208 	p->filename = strdup(filename);
209 	p->mode = mode;
210 	{
211 		const char *fn;
212 		fn = forced_outname ? forced_outname : filename;
213 		p->outname = malloc(strlen(fn) + 32);	/* leave some space for clobbering the file name */
214 		strcpy(p->outname, fn);
215 	}
216 
217 	/* don't apply evil char checking for forced output filenames */
218 	if(!forced_outname)
219 	{
220 		for(pf = p->outname; *pf; pf++)
221 		{
222 			if((int)char_evilness[*pf & 0xff] > opt_evil_filename)
223 				*pf = '_';
224 		}
225 	}
226 
227 	crc32_init_ctx(&p->crc32_context);
228 
229 	return *last = p;
230 }
231 
232 /* Various macros, although only mostly used by read_yenc() */
233 #define ASSERT(expr)							\
234 	if(!(expr))							\
235 	{								\
236 		error(0, 0, _("%s: Assertion `%s' failed -- output may be corrupt"),	\
237 			c->inname, #expr);				\
238 		return EXIT_FAILURE;					\
239 	}
240 
241 #define B64DECODE(c) base64_tab[(size_t)(c)]
242 
243 #define UUDECODE(c)	(((c) - ' ') & 077)
244 
245 #define SEARCH_MARKER(marker)				\
246 	if(!fgets_(buf, BUFSIZ, stdin))			\
247 	{								\
248 		error(0, 0, _("%s: %s:%i: No `%s' marker found"),	\
249 			c->inname, f->outname, c->part, (marker));	\
250 		return EXIT_FAILURE;					\
251 	}								\
252 	if(!strncmp(buf, marker, lenof(marker)))			\
253 		break;
254 
255 #define PARSE_TAG_OPTIONAL(tag, convertfn, assignto)			\
256 	if((p = strstr(buf, tag)))		\
257 		assignto = convertfn(p + lenof(tag))
258 
259 #define PARSE_TAG(tag, convertfn, assignto)				\
260 	PARSE_TAG_OPTIONAL(tag, convertfn, assignto);			\
261 	else								\
262 	{								\
263 		error(0, 0, _("%s: %s:%i: No `%s' tag found"),		\
264 			c->inname, f->outname, c->part, tag);		\
265 		return EXIT_FAILURE;					\
266 	}
267 
read_base64(struct decode_context * c,struct decoded_file * f,char * buf)268 static int read_base64(struct decode_context *c, struct decoded_file *f, char *buf)
269 {
270 	int exit_status = EXIT_EOF;
271 	int last_data = 0;
272 
273 	/* We don't use c->out, so the CRC32 is never calculated. */
274 	c->total_parts = 1;
275 
276 	if(opt_verbose > 0)
277 		error(0, 0, _("%s: %s: base64 encoding"), c->inname, f->outname);
278 
279 	for(;;)
280 	{
281 		int i;
282 		char *pi, *po;
283 
284 		if(!fgets_(buf, BUFSIZ, stdin))
285 		{
286 			error(0, 0, _("%s: %s: Short file"), c->inname, f->outname);
287 			return EXIT_FAILURE;
288 		}
289 
290 		if(!memcmp(buf, B64MARKER_END, lenof(B64MARKER_END)))
291 			break;
292 
293 		if(last_data)
294 		{
295 			error(0, 0, _("%s: %s: warning: Data following `=' padding character"), c->inname, f->outname);
296 			exit_status = EXIT_WARNING;
297 			break;
298 		}
299 
300 		/* Rather than taking the convoluted (and frankly, nasty) algo-
301 		 * rithm used by uudecode to ignore characters outside of that
302 		 * defined by RFC2045 (which obsoletes RFC1521, referred to by
303 		 * uuencode), we make two passes over the input: once to remove
304 		 * said characters, while a second pass decodes the input. */
305 
306 		/* Note this also removes the EOL markers fgets_ leaves behind. */
307 		for(po = pi = buf; *pi; pi++)
308 		{
309 			if((*pi & 0x80) || (base64_tab[(size_t)*pi] == 0177))
310 				continue;
311 			*po++ = *pi;
312 			if(*pi == '=')
313 			{
314 				last_data = 1;
315 				exit_status = EXIT_SUCCESS;
316 				if((po - buf) % 4)
317 					continue;
318 				break;
319 			}
320 			if(last_data)
321 			{
322 				error(0, 0, _("%s: %s: warning: Data following `=' padding character"), c->inname, f->outname);
323 				exit_status = EXIT_WARNING;
324 				po--;	/* undo the last character */
325 				break;
326 			}
327 		}
328 		*po = '\0';
329 
330 		/* the resulting input line length should be a multiple of 4 */
331 		i = po - buf;
332 		if(i % 4)
333 		{
334 			error(0, 0, _("%s: %s: Illegal line -- ignored"), c->inname, f->outname);
335 			exit_status = EXIT_FAILURE;
336 			continue;
337 		}
338 		else if(i == 0)
339 			continue;
340 		i /= 4;
341 
342 		for(po = pi = buf; i; pi += 4, i--)
343 		{
344 			*po++ = B64DECODE(pi[0]) << 2 | B64DECODE(pi[1]) >> 4;
345 			*po++ = B64DECODE(pi[1]) << 4 | B64DECODE(pi[2]) >> 2;
346 			*po++ = B64DECODE(pi[2]) << 6 | B64DECODE(pi[3]);
347 
348 			if(pi[3] == '=')
349 			{
350 				po--;
351 				if(pi[2] == '=')
352 				{
353 					po--;
354 					if(pi[1] == '=')
355 						po--;
356 				}
357 				break;
358 			}
359 		}
360 
361 #undef B64DECODE
362 
363 		if(fwrite(buf, po - buf, 1, f->handle) != 1)
364 		{
365 			error(0, errno, _("%s: %s"), c->inname, f->outname);
366 			return EXIT_FAILURE;
367 		}
368 	}
369 
370 	c->status = part_intact;
371 	return exit_status;
372 }
373 
read_stduu(struct decode_context * c,struct decoded_file * f,char * buf)374 static int read_stduu(struct decode_context *c, struct decoded_file *f, char *buf)
375 {
376 	int exit_status = EXIT_EOF;
377 	int line;
378 
379 	/* We don't use c->out, so the CRC32 is never calculated. */
380 	c->total_parts = 1;
381 
382 	if(opt_verbose > 0)
383 		error(0, 0, _("%s: %s: uu encoding"), c->inname, f->outname);
384 
385 	for(line = 1; ; line++)
386 	{
387 		int i, n, l;
388 		char *pi, *po;
389 		int pad = 0;
390 
391 		if(!fgets_(buf, BUFSIZ, stdin))
392 		{
393 			error(0, 0, _("%s: %s: unexpected end of file at line #%i"), c->inname, f->outname, line);
394 			return EXIT_FAILURE;
395 		}
396 
397 		po = pi = buf;
398 		n = *pi++ - ' ';
399 		if(n < 0)	/* ignores empty lines */
400 			continue;
401 		n &= 077;
402 		if(n == 0)
403 		{
404 			exit_status = EXIT_SUCCESS;
405 			break;
406 		}
407 
408 		for(l = 0; pi[l] >= ' '; )
409 			l++;
410 
411 		/* count should be a multiple of 4 */
412 		if(l != ((n + 2) / 3) * 4)
413 		{
414 			i = (l * 3) / 4;
415 			if(opt_write_broken)
416 			{
417 				/* write however many bytes we can see */
418 				n = i;
419 				error(0, 0, _("%s: %s: warning: malformed line #%i"), c->inname, f->outname, line);
420 			}
421 			else
422 			{
423 				if(n > i)
424 				{
425 					pad = n - i;
426 					n = i;
427 					error(0, 0, _("%s: %s: short line #%i (output padded)"), c->inname, f->outname, line);
428 				}
429 				else
430 					error(0, 0, _("%s: %s: overlong line #%i (truncated)"), c->inname, f->outname, line);
431 				exit_status = EXIT_FAILURE;
432 			}
433 		}
434 
435 		/* At this point, we're guaranteed (ha!) to have enough
436 		 * input to work with, whatever the value of n, so no need
437 		 * to check for malformed lines. */
438 		for(i = n / 3; i; pi += 4, i--)
439 		{
440 			*po++ = UUDECODE(pi[0]) << 2 | UUDECODE(pi[1]) >> 4;
441 			*po++ = UUDECODE(pi[1]) << 4 | UUDECODE(pi[2]) >> 2;
442 			*po++ = UUDECODE(pi[2]) << 6 | UUDECODE(pi[3]);
443 		}
444 
445 		switch(n % 3)
446 		{
447 		case 2:
448 			*po++ = UUDECODE(pi[0]) << 2 | UUDECODE(pi[1]) >> 4;
449 			*po++ = UUDECODE(pi[1]) << 4 | UUDECODE(pi[2]) >> 2;
450 			break;
451 		case 1:
452 			*po++ = UUDECODE(pi[0]) << 2 | UUDECODE(pi[1]) >> 4;
453 			break;
454 		}
455 
456 #undef UUDECODE
457 
458 		if(pad)
459 		{
460 			memset(po, 0, pad);
461 			po += pad;
462 		}
463 
464 		if(fwrite(buf, po - buf, 1, f->handle) != 1)
465 		{
466 			error(0, errno, _("%s: %s:1"), c->inname, f->outname);
467 			return EXIT_FAILURE;
468 		}
469 	}
470 
471 	if((fgets_(buf, BUFSIZ, stdin) == NULL) || strcmp(buf, UUMARKER_END))
472 	{
473 		error(0, 0, _("%s: %s:1: No `end' line"), c->inname, f->outname);
474 		return EXIT_FAILURE;
475 	}
476 
477 	c->status = part_intact;
478 	return exit_status;
479 }
480 
read_yenc(struct decode_context * c,struct decoded_file * f,char * buf)481 static int read_yenc(struct decode_context *c, struct decoded_file *f, char *buf)
482 {
483 	int is_multi_part = 0;
484 	int has_more_parts = 0;
485 	char *p;
486 	char *pout;
487 
488 	int unrecognised_escapes[0x100];
489 	off_t part_begin = 0;
490 	off_t part_end = 0;
491 
492 	memset(unrecognised_escapes, 0, sizeof(unrecognised_escapes));
493 
494 	/* ick. Non-zero on entry. Needed to detect multi-part files */
495 	c->part = -1;
496 	c->total_parts = 0;
497 	PARSE_TAG_OPTIONAL(YTAG_PART, (int)atol, c->part);
498 	PARSE_TAG_OPTIONAL(YTAG_TOTAL, (int)atol, c->total_parts);
499 
500 	/* Pretend single-part articles have a `part=1' tag */
501 	is_multi_part = c->part != -1;
502 	if(!is_multi_part)
503 		c->part = 1;
504 
505 	ASSERT(c->part > 0);
506 
507 	if(c->total_parts)
508 	{
509 		ASSERT(c->part <= c->total_parts);
510 	}
511 	else
512 	{
513 		/* we've at least this many parts; can't tell for now */
514 		has_more_parts = 1;
515 		if(c->total_parts < c->part)
516 			c->total_parts = c->part;
517 	}
518 
519 	{	/* Assert that we can handle the line width.
520 		 * sizeof(buf) should be 8kb, hope that's sufficient. */
521 		int line_width;
522 
523 		PARSE_TAG(YTAG_LINE, (int)atol, line_width);
524 		ASSERT(line_width >= 0);
525 		ASSERT(line_width < BUFSIZ);
526 	}
527 
528 	/* opt_verbose == 1: report the first part only.
529 	 * opt_verbose == 2: report all parts. */
530 	if((!f->total_parts && (opt_verbose > 0))
531 			|| (opt_verbose > 1))
532 		error(0, 0, _("%s: %s:%i: yEnc file (%s%i parts)"), c->inname,
533 			f->outname, c->part, has_more_parts ? ">=" : "", c->total_parts);
534 
535 	{
536 		off_t file_size = 0;
537 
538 		PARSE_TAG(YTAG_SIZE, (off_t)atol, file_size);
539 		ASSERT(file_size >= 0);
540 
541 		if(f->total_parts && (f->total_size != file_size))
542 			error(0, 0, _("%s: %s:%i: warning: File size mismatch -- previous parts says %li, this part %li; ignoring this part"),
543 				c->inname, f->outname, c->part, f->total_size, file_size);
544 		else
545 			f->total_size = file_size;
546 	}
547 
548 	if(!is_multi_part)
549 		part_end = f->total_size;
550 
551 	/* make sure we've got a valid handle in f->handle */
552 	if(decoded_file_open(f) != EXIT_SUCCESS)
553 	{
554 		/* Only show error for the first part. */
555 		if(!f->total_parts)
556 			error(0, errno, _("%s: %s:%i"), c->inname, f->outname, c->part);
557 		return EXIT_FAILURE;
558 	}
559 
560 	/* Is this a multi-part article? */
561 	if(is_multi_part)
562 	{
563 		for(;;)
564 		{
565 			SEARCH_MARKER(YMARKER_PART);
566 			error(0, 0, _("%s: %s:%i: No `=ypart' line found after `=ybegin'"),
567 				c->inname, f->outname, c->part);
568 			return EXIT_FAILURE;
569 		}
570 
571 		PARSE_TAG(YTAG_BEGIN, (off_t)atol, part_begin);
572 		part_begin--;
573 		ASSERT(part_begin >= 0);
574 
575 		PARSE_TAG(YTAG_END, (off_t)atol, part_end);
576 		ASSERT(part_end >= 0);
577 		ASSERT(part_end >= part_begin);
578 		ASSERT(part_end <= f->total_size);
579 
580 		/* This fails on stdout, but we ignore that. */
581 		(void)fseek(f->handle, part_begin, SEEK_SET);
582 	}
583 
584 	/* Prepare the output buffer. */
585 	if((part_end - part_begin > PART_SIZE_SOFT_LIMIT) && !opt_large_parts)
586 	{
587 		error(0, 0, _("%s: %s:%i: Not going to malloc() %lu bytes (broken header?); use --large-parts"),
588 			c->inname, f->outname, c->part, part_end - part_begin);
589 		return EXIT_FAILURE;
590 	}
591 
592 	if(!(pout = c->out = malloc((size_t)(part_end - part_begin))))
593 	{
594 		error(0, 0, _("%s: %s:%i: Unable to malloc() %lu bytes"),
595 			c->inname, f->outname, c->part, part_end - part_begin);
596 		return EXIT_FAILURE;
597 	}
598 
599 	/* Begin decoding! */
600 	for(;/* each line */;)
601 	{
602 		/* Breaks out if =yend found. */
603 		SEARCH_MARKER(YMARKER_END);
604 
605 		p = buf + strlen(buf) - 1;
606 		while((p >= buf) && ((*p == '\r') || (*p == '\n')))
607 			--p;
608 		*++p = '\0';
609 
610 		for(p = buf; *p; p++)
611 		{
612 			if(pout - c->out >= part_end - part_begin)
613 			{
614 				error(0, 0, _("%s: %s:%i: Part longer than expected"),
615 					c->inname, f->outname, c->part);
616 				c->out_size = pout - c->out;
617 				return EXIT_FAILURE;
618 			}
619 			if(*p != '=')
620 			{
621 				*pout++ = *p - 42;
622 				continue;
623 			}
624 
625 			switch(*++p)
626 			{
627 			default:	/* Be liberal in what we accept. */
628 				if(!unrecognised_escapes[*p & 0xff])
629 				{	/* Just once is enough. */
630 					error(0, 0, _("%s: %s:%i: warning: Unrecognised escape code `\\%o' (allowing it anyway)"),
631 						c->inname, f->outname, c->part, *p);
632 				}
633 				unrecognised_escapes[*p & 0xff]++;
634 			/*   NUL       TAB        LF        CR */
635 			case '@': case 'I': case 'J': case 'M':
636 			/*    =         .        ??? */
637 			case '}': case 'n': case '`':
638 				*pout++ = *p - '@' - 42;
639 				break;
640 			}
641 		}
642 	}
643 	c->out_size = pout - c->out;
644 
645 	{	/* verify the CRC32 sum */
646 		u_int32_t part_crc32;
647 		struct crc32_ctx crc32_context;
648 
649 		crc32_init_ctx(&crc32_context);
650 		crc32_process_bytes(c->out, c->out_size, &crc32_context);
651 		crc32_finish_ctx(&crc32_context);
652 
653 		if(is_multi_part)
654 		{
655 			u_int32_t file_crc32 = 0;
656 
657 			PARSE_TAG_OPTIONAL(YTAG_CRC32, atocrc32, file_crc32);
658 			if(file_crc32 && f->total_parts && f->crc32 && (f->crc32 != file_crc32))
659 			{
660 				error(0, 0, _("%s: %s:%i: warning: File CRC mismatch in trailer -- previous parts says %08x, this part %08x -- ignoring this part"),
661 					c->inname, f->outname, c->part, f->crc32, file_crc32);
662 			}
663 			else if(file_crc32)
664 				f->crc32 = file_crc32;
665 
666 			PARSE_TAG(YTAG_PCRC32, atocrc32, part_crc32);
667 		}
668 		else
669 		{
670 			PARSE_TAG(YTAG_CRC32, atocrc32, part_crc32);
671 		}
672 
673 		if(part_crc32 != crc32_read_ctx(&crc32_context))
674 		{
675 			error(0, 0, _("%s: %s:%i: Part CRC32 error -- got 0x%08x, should be 0x%08x"),
676 				c->inname, f->outname, c->part, crc32_read_ctx(&crc32_context), part_crc32);
677 			return EXIT_FAILURE;
678 		}
679 	}
680 
681 	/* Check the trailer part number. */
682 	{
683 		int trailer_part = 0;
684 		PARSE_TAG_OPTIONAL(YTAG_PART, (int)atol, trailer_part);
685 		if(!is_multi_part)
686 			trailer_part++;
687 		ASSERT(trailer_part > 0);
688 
689 		if(trailer_part != c->part)
690 		{
691 			error(0, 0, _("%s: %s:%i: warning: Part number mismatch in trailer (part %i); ignoring trailer"),
692 				c->inname, f->outname, c->part, trailer_part);
693 		}
694 	}
695 
696 	/* All of the following are warnings, since the CRC matched. And
697 	 * frankly, I trust the CRC more than I trust the ability of some
698 	 * random sample of the human population to be able to code. */
699 	{
700 		off_t part_size = 0;
701 
702 		PARSE_TAG(YTAG_SIZE, (off_t)atol, part_size);
703 		ASSERT(part_size >= 0);
704 
705 		if((size_t)part_size != c->out_size)
706 		{
707 			error(0, 0, _("%s: %s:%i: warning: Wrong part size -- decoded %lu bytes, expected %lu"),
708 				c->inname, f->outname, c->part, c->out_size, part_size);
709 		}
710 
711 		if(part_size != part_end - part_begin)
712 		{
713 			error(0, 0, _("%s: %s:%i: warning: Part size/range mismatch -- %lu != %lu-%lu"),
714 				c->inname, f->outname, c->part, part_size, part_end, part_begin);
715 		}
716 	}
717 
718 	c->status = part_intact;
719 
720 	return EXIT_SUCCESS;
721 }
722 
decode(inname,forced_outname,decoded_list)723 static int decode(inname, forced_outname, decoded_list)
724 	const char *inname;
725 	const char *forced_outname;
726 	struct decoded_file **decoded_list;
727 {
728 	int exit_status = EXIT_SUCCESS;
729 	static struct decoded_file *f;
730 	static read_enc re = NULL;
731 	int no_begin = 1;
732 
733 	char buf[BUFSIZ];
734 	char name[BUFSIZ];
735 	char *p;
736 
737 	struct decode_context c;
738 
739 #define INIT_DECODE_CTX()	\
740 	memset(&c, 0, sizeof(c));	\
741 	c.inname = inname;		\
742 	c.status = part_broken;	/* Assume broken unless we verify otherwise */
743 
744 	INIT_DECODE_CTX();
745 
746 	if(opt_verbose > 1)
747 		error(0, 0, _("%s: Processing"), inname);
748 
749 	while(re || fgets_(buf, BUFSIZ, stdin))
750 	{
751 		int mode = 0644;
752 		const char *outname;
753 
754 		if(re)
755 			goto read_file;
756 
757 		outname = name;
758 		if(!strncmp(buf, YMARKER_BEGIN, lenof(YMARKER_BEGIN)))
759 		{
760 			/* This is parsed again in read_yenc(). Somewhat wasteful. ;_; */
761 			PARSE_TAG_OPTIONAL(YTAG_PART, (int)atol, c.part);
762 			if(!c.part)
763 				c.part++;
764 
765 			/* parse the name tag */
766 			if(!(p = strstr(buf, YTAG_NAME)))
767 			{
768 				error(0, 0, _("%s: No `%s' tag found"), inname, YTAG_NAME);
769 				exit_status = EXIT_FAILURE;
770 				continue;
771 			}
772 			strcpy(name, p + lenof(YTAG_NAME));
773 			p = name;
774 
775 			/* yEnc 1.3 allows for trailing spaces and quoted filenames */
776 			p += strlen(p) - 1;
777 			while((p > outname) && ((*p == '\r') || (*p == '\n') || (*p == ' ')))
778 				*p-- = '\0';
779 			if((p > outname) && (*p == '"') && (*outname == '"'))
780 			{
781 				*p = '\0';
782 				++outname;
783 			}
784 
785 			re = read_yenc;
786 		}
787 		else if(!strncmp(buf, UUMARKER_BEGIN, lenof(UUMARKER_BEGIN)))
788 		{
789 			if(sscanf(buf + lenof(UUMARKER_BEGIN), "%o %[^\n]", &mode, name) != 2)
790 			{
791 				exit_status = EXIT_FAILURE;
792 				continue;
793 			}
794 			c.part = 1;
795 			re = read_stduu;
796 		}
797 		else if(!strncmp(buf, B64MARKER_BEGIN, lenof(B64MARKER_BEGIN)))
798 		{
799 			if(sscanf(buf + lenof(B64MARKER_BEGIN), "%o %[^\n]", &mode, name) != 2)
800 			{
801 				exit_status = EXIT_FAILURE;
802 				continue;
803 			}
804 			c.part = 1;
805 			re = read_base64;
806 		}
807 		else continue;
808 
809 		f = decoded_file_lookup(decoded_list, outname, mode, forced_outname);
810 
811 		/* Don't bother decoding duplicated parts. */
812 		if((f->total_parts >= c.part)
813 			&& ((f->status[c.part - 1] == part_intact)
814 				|| (f->status[c.part - 1] == part_duplicated)))
815 		{
816 			/* This is OK, because out == NULL */
817 			c.status = part_duplicated;
818 			exit_status = EXIT_SUCCESS;
819 			goto exit_skip;
820 		}
821 
822 		/* make sure we've got a valid handle in f->handle */
823 		if(decoded_file_open(f) != EXIT_SUCCESS)
824 		{
825 			/* Only show error for the first part. */
826 			if(!f->total_parts)
827 				error(0, errno, _("%s: %s"), c.inname, f->outname);
828 
829 			/* NASTY HACK: total parts isn't parsed by this stage
830 			 * yet; but we force it nonzero so that f->total_parts
831 			 * will get incremented below. */
832 			c.total_parts = c.part;
833 
834 			exit_status = EXIT_FAILURE;
835 			goto exit_skip;
836 		}
837 
838 read_file:
839 
840 		exit_status = (*re)(&c, f, buf);
841 
842 exit_skip:
843 
844 		if(c.out)
845 		{
846 			/* Commit the part to file. */
847 			if((c.status == part_intact) || opt_write_broken)
848 			{
849 				/* f always set before out */
850 				if(fwrite(c.out, c.out_size, 1, f->handle) != 1)
851 				{
852 					error(0, errno, _("%s: %s:%i"), c.inname, f->outname, c.part);
853 					return EXIT_FAILURE;
854 				}
855 				f->bytes_written += c.out_size;
856 			}
857 
858 			/* are we getting the parts sequentially? */
859 			if(c.part == f->crc32_prev_part + 1)
860 			{
861 				crc32_process_bytes(c.out, c.out_size, &f->crc32_context);
862 				f->crc32_prev_part = c.part;
863 			}
864 			else
865 				f->crc32_prev_part = -1;
866 
867 			free(c.out);
868 			c.out = NULL;
869 		}
870 
871 		if(f)
872 		{
873 			if(f->total_parts < c.total_parts)
874 			{
875 				enum part_status *ps;
876 
877 				ps = (enum part_status *)malloc(c.total_parts * sizeof(enum part_status));
878 				memcpy(ps, f->status, f->total_parts * sizeof(enum part_status));
879 				memset(ps + f->total_parts, 0, (c.total_parts - f->total_parts) * sizeof(enum part_status));
880 				if(f->status)
881 					free(f->status);
882 				f->status = ps;
883 				f->total_parts = c.total_parts;
884 			}
885 			f->status[c.part - 1] = c.status;
886 
887 			if(f->is_seekable && f->handle && (f->handle != stdout))
888 			{
889 				fclose(f->handle);
890 				f->handle = NULL;
891 			}
892 		}
893 
894 		no_begin = 0;
895 
896 		/* does the handler want to hold on to stdin? */
897 		if(exit_status == EXIT_EOF)
898 		{
899 			exit_status = EXIT_SUCCESS;
900 			break;
901 		}
902 
903 		re = NULL;
904 		INIT_DECODE_CTX();
905 	}
906 
907 	if(no_begin)
908 	{
909 		error(0, 0, _("%s: No `=ybegin' or `begin' line found"), inname);
910 		exit_status = EXIT_FAILURE;
911 	}
912 
913 	return exit_status;
914 }
915 
916 
917 #undef PARSE_TAG
918 #undef PARSE_TAG_OPTIONAL
919 #undef SEARCH_MARKER
920 #undef UUDECODE
921 #undef B64DECODE
922 #undef ASSERT
923 
924 static void
usage(status)925 usage(status)
926 	int status;
927 {
928 	if(status)
929 		fprintf(stderr, _("Try `%s --help' for more information.\n"),
930 			program_name);
931 	else
932 	{
933 #ifdef __DJGPP__
934 	setmode(fileno(stdout), O_TEXT);
935 #endif /* __DJGPP__ */
936 		printf(_("\
937 %s %s -- Copyright (C) Liyang HU 2002-2003, http://nerv.cx/liyang/\n\
938 Usage: %s [FILE] ...\n\
939   -o, --output-file=FILE   direct all output to FILE (use - for stdout)\n\
940   -D, --directory=DIR      write output files to DIR\n\
941   -e, --evil-filename      allow evil characters in filename\n\
942   -c, --clobber-filename   append .n to filename if it already exists\n\
943   -f, --force-overwrite    overwrite output file; truncate unless -b is used.\n\
944   -b, --write-broken       write decoded part even if verified to be broken,\n\
945                            and don't append .broken to the filename\n\
946   -i, --broken-encoder     work around broken encoders (where CRC=00000001)\n\
947   -r, --remove-broken      unlink instead of renaming broken files\n\
948   -l, --large-parts        expect parts larger than %uk\n\
949   -h, --help               display this help and exit\n\
950   -v, --verbose            detailed progress reports (repeat for more)\n\
951   -V, --version            output version information and exit\n\
952 Refer to the man page for details.\n"),
953 			PACKAGE, VERSION,
954 			program_name,
955 			PART_SIZE_SOFT_LIMIT >> 10);
956 	}
957 	exit(status);
958 }
959 
960 int
main(argc,argv)961 main(argc, argv)
962 	int argc;
963 	char * const *argv;
964 {
965 	int opt;
966 	int exit_status;
967 	const char *outname;
968 	struct decoded_file *decoded_list = NULL;
969 
970 	struct option longopts[] =
971 	{
972 		{ "output-file", required_argument, NULL, (int)'o' },
973 		{ "directory", required_argument, NULL, (int)'D' },
974 		{ "evil-filename", no_argument, NULL, (int)'e' },
975 		{ "clobber-filename", no_argument, NULL, (int)'c' },
976 		{ "force-overwrite", no_argument, NULL, (int)'f' },
977 		{ "write-broken", no_argument, NULL, (int)'b' },
978 		{ "broken-encoder", no_argument, NULL, (int)'i' },
979 		{ "remove-broken", no_argument, NULL, (int)'r' },
980 		{ "large-parts", no_argument, NULL, (int)'l' },
981 		{ "help", no_argument, NULL, (int)'h' },
982 		{ "verbose", no_argument, NULL, (int)'v' },
983 		{ "version", no_argument, NULL, (int)'V' },
984 		{ NULL, 0, NULL, 0 }
985 	};
986 
987 	program_name = argv[0];
988 	outname = NULL;
989 
990 #ifdef WIN32
991 	/* Never underestimate stupid people in large numbers... */
992 	_setmode(_fileno(stdin), _O_BINARY);
993 	_setmode(_fileno(stdout), _O_BINARY);
994 #endif
995 
996 #ifdef __DJGPP__
997 	_fmode = O_BINARY;
998 	setmode(fileno(stdout), O_BINARY);
999 	if (isatty(fileno(stdin)) == 0)
1000 		setmode(fileno(stdin), O_BINARY);
1001 	if (isatty(fileno(stdin)) !=0)
1002 		setbuf(stdin, NULL);
1003 #endif /* __DJGPP__ */
1004 
1005 	while(opt = getopt_long(argc, argv, "o:D:ecfbirlhvV", longopts, (int *)NULL),
1006 		opt != EOF)
1007 	{
1008 		switch(opt)
1009 		{
1010 		case 'o':
1011 			outname = optarg;
1012 			break;
1013 
1014 		case 'D':
1015 			opt_directory = optarg;
1016 			break;
1017 
1018 		case 'e':
1019 			opt_evil_filename++;
1020 			break;
1021 
1022 		case 'c':
1023 			opt_clobber_filename = 1;
1024 			break;
1025 
1026 		case 'f':
1027 			opt_force_overwrite = 1;
1028 			break;
1029 
1030 		case 'b':
1031 			opt_write_broken = 1;
1032 			break;
1033 
1034 		case 'i':
1035 			opt_broken_encoder = 1;
1036 			break;
1037 
1038 		case 'r':
1039 			opt_remove_broken = 1;
1040 			break;
1041 
1042 		case 'l':
1043 			opt_large_parts = 1;
1044 			break;
1045 
1046 		case 'h':
1047 			usage(EXIT_SUCCESS);
1048 
1049 		case 'v':
1050 			opt_verbose++;
1051 			break;
1052 
1053 		case 'V':
1054 			printf("%s %s\n", PACKAGE, VERSION);
1055 			exit(EXIT_SUCCESS);
1056 
1057 		case 0:
1058 			break;
1059 
1060 		default:
1061 			usage(EXIT_FAILURE);
1062 		}
1063 	}
1064 
1065 	if(optind == argc)
1066 		exit_status = decode("stdin", outname, &decoded_list);
1067 	else
1068 	{
1069 		char current_directory[BUFSIZ];
1070 
1071 		if(opt_directory)
1072 			getcwd(current_directory, sizeof(current_directory));
1073 
1074 		exit_status = EXIT_SUCCESS;
1075 		do
1076 		{
1077 			int det_return;
1078 
1079 			/* note the order: we want to exit this loop in
1080 			 * opt_directory, if it is specified. */
1081 			if(opt_directory)
1082 				chdir(current_directory);
1083 
1084 			if(!freopen(argv[optind], "rb", stdin))
1085 			{
1086 				error(0, errno, "%s", argv[optind]);
1087 				exit_status = EXIT_FAILURE;
1088 				break;
1089 			}
1090 
1091 			if(opt_directory && chdir(opt_directory))
1092 			{
1093 				error(0, errno, "%s", opt_directory);
1094 				exit_status = EXIT_FAILURE;
1095 				break;
1096 			}
1097 
1098 			if((det_return = decode(argv[optind], outname, &decoded_list))
1099 					!= EXIT_SUCCESS)
1100 				exit_status = det_return;
1101 		}
1102 		while(++optind < argc);
1103 	}
1104 
1105 	/* determine exit_status from decoded_list instead: */
1106 	exit_status = EXIT_SUCCESS;
1107 	while(decoded_list)
1108 	{
1109 		int broken_file = 0;
1110 		struct decoded_file *f = decoded_list;
1111 		int i;
1112 
1113 		if(!f->previously_existed)
1114 		{
1115 			/* Did we write to this file at all? Yes: */
1116 
1117 			for(i = 0; i < f->total_parts; i++)
1118 			{
1119 				switch(f->status[i])
1120 				{
1121 				case part_missing:
1122 					error(0, 0, _("%s:%i: Part missing (of %i parts)"),
1123 						f->outname, i + 1, f->total_parts);
1124 					break;
1125 				case part_broken:
1126 					error(0, 0, _("%s:%i: Part broken (of %i parts)"),
1127 						f->outname, i + 1, f->total_parts);
1128 					break;
1129 				case part_duplicated:
1130 					error(0, 0, _("%s:%i: warning: Part duplicated (of %i parts); ignored"),
1131 						f->outname, i + 1, f->total_parts);
1132 				case part_intact:
1133 					continue;
1134 				}
1135 				broken_file = 1;
1136 				exit_status = EXIT_FAILURE;
1137 			}
1138 
1139 			if(f->bytes_written != f->total_size)
1140 			{
1141 				error(0, 0, _("%s: wrote %li bytes; should have been %li"),
1142 					f->outname, f->bytes_written, f->total_size);
1143 				broken_file = 1;
1144 				exit_status = EXIT_FAILURE;
1145 			}
1146 
1147 			/* go over the output file once again to calculate the CRC */
1148 			if((f->crc32_prev_part == -1) && f->is_seekable)
1149 			{
1150 				char buf[BUFSIZ];
1151 				size_t bytes_read;
1152 
1153 				decoded_file_open(f);
1154 				fseek(f->handle, 0, SEEK_SET);
1155 				crc32_init_ctx(&f->crc32_context);
1156 				while((bytes_read = fread(buf, 1, BUFSIZ, f->handle)))
1157 					crc32_process_bytes(buf, bytes_read, &f->crc32_context);
1158 
1159 				f->crc32_prev_part = 0;
1160 			}
1161 
1162 			if(f->handle && (f->handle != stdout))
1163 				fclose(f->handle);
1164 
1165 			crc32_finish_ctx(&f->crc32_context);
1166 			if((f->crc32_prev_part >= 0) && f->crc32 && (f->crc32 != crc32_read_ctx(&f->crc32_context)))
1167 			{
1168 				if (!opt_broken_encoder || f->crc32 != 0x00000001)
1169 				{
1170 					error(0, 0, _("%s: File CRC error -- got 0x%08x, should be 0x%08x"),
1171 						f->outname, crc32_read_ctx(&f->crc32_context), f->crc32);
1172 					broken_file = 1;
1173 					exit_status = EXIT_FAILURE;
1174 				}
1175 				else
1176 					error(0, 0, _("%s: warning: File encoded with a broken encoder (fake CRC: 0x00000001, should be 0x%08x) -- please pester the sender to upgrade their software"),
1177 						f->outname, crc32_read_ctx(&f->crc32_context));
1178 
1179 			}
1180 
1181 			if(broken_file)
1182 			{
1183 
1184 			/* If we have a broken file:
1185 			 * WB	RB	ACTION
1186 			 * 0	0	rename
1187 			 * 0	1	unlink
1188 			 * 1	0	nothing
1189 			 * 1	1	rename		*/
1190 
1191 		/* When both WB and RB are specified, the file is `removed' in
1192 		 * the sense that there is no longer a file with that name. */
1193 
1194 				if(opt_write_broken == opt_remove_broken)
1195 				{
1196 					char *outname_broken = malloc(strlen(f->outname)
1197 							+ lenof(YYDEC_BROKEN_SUFFIX) + 1);
1198 					strcpy(outname_broken, f->outname);
1199 					strcat(outname_broken, YYDEC_BROKEN_SUFFIX);
1200 					error(0, 0, "%s: renamed to %s", f->outname, outname_broken);
1201 					rename(f->outname, outname_broken);
1202 					free(outname_broken);
1203 				}
1204 				else if(!opt_write_broken && opt_remove_broken)
1205 					unlink(f->outname);
1206 			}
1207 
1208 		} /* previously_existed == we haven't touched this file:
1209 		   *   do nothing. (except release malloc()'d storage) */
1210 
1211 		decoded_list = f->next;
1212 		if(f->filename)
1213 			free(f->filename);
1214 		if(f->outname)
1215 			free(f->outname);
1216 		if(f->status)
1217 			free(f->status);
1218 		free(f);
1219 	}
1220 
1221 	exit(exit_status);
1222 }
1223 
1224