tool.c 81.2 KB
Newer Older
1
/*  This file is part of "reprepro"
2
 *  Copyright (C) 2006,2007,2008,2009,2010 Bernhard R. Link
3
 *  This program is free software; you can redistribute it and/or modify
4
 *  it under the terms of the GNU General Public License version 2 as
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
 *  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 General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02111-1301  USA
 */
#include <config.h>

#include <errno.h>
#include <assert.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <getopt.h>
#include <string.h>
#include <strings.h>
#include <fcntl.h>
#include <signal.h>
28
#include <time.h>
29
#include "error.h"
30
#include "filecntl.h"
31 32 33 34
#include "mprintf.h"
#include "strlist.h"
#include "names.h"
#include "dirs.h"
35
#include "checksums.h"
36
#include "chunks.h"
37
#include "chunkedit.h"
38
#include "signature.h"
39
#include "debfile.h"
40
#include "sourceextraction.h"
41
#include "uncompression.h"
42 43 44

/* for compatibility with used code */
int verbose=0;
45 46 47
bool interrupted(void) {
	return false;
}
48

49 50
static void about(bool help) NORETURN;
static void about(bool help) {
51
	fprintf(help?stdout:stderr,
52 53
"changestool: Modify a Debian style .changes file\n"
"Syntax: changestool [--create] <changesfile> <commands>\n"
54 55
"Possible commands include:\n"
" verify\n"
56 57
" updatechecksums [<files to update>]\n"
" includeallsources [<files to copy from .dsc to .changes>]\n"
58
" adddeb <.deb filenames>\n"
59 60
" adddsc <.dsc filenames>\n"
" addrawfile <filenames>\n"
61
" add <filenames processed by filename suffix>\n"
62
" setdistribution <distributions to list>\n"
63
" dumbremove <filenames>\n"
64
);
65
	if (help)
66 67 68 69 70 71 72 73 74
		exit(EXIT_SUCCESS);
	else
		exit(EXIT_FAILURE);
}

struct binaryfile {
	struct binaryfile *next; // in binaries.files list
	struct binary *binary; // parent
	struct fileentry *file;
75 76
	char *controlchunk;
	char *name, *version, *architecture;
77 78 79 80
	char *sourcename, *sourceversion;
	char *maintainer;
	char *section, *priority;
	char *shortdescription;
81
	bool hasmd5sums;
82 83 84
};

static void binaryfile_free(struct binaryfile *p) {
85
	if (p == NULL)
86 87
		return;

88
	free(p->controlchunk);
89 90
	free(p->name);
	free(p->version);
91
	free(p->architecture);
92 93 94 95 96 97 98 99 100 101
	free(p->sourcename);
	free(p->sourceversion);
	free(p->maintainer);
	free(p->section);
	free(p->priority);
	free(p->shortdescription);
	free(p);
}

enum filetype { ft_UNKNOWN,
102
			ft_TAR, ft_ORIG_TAR, ft_DIFF,
103 104 105
#define ft_MaxInSource ft_DSC-1
			ft_DSC, ft_DEB, ft_UDEB , ft_Count};
#define ft_Max ft_Count-1
106 107 108 109 110 111 112 113 114 115 116 117 118 119

static const struct {
	const char *suffix;
	size_t len;
	bool allowcompressed;
} typesuffix[ft_Count] = {
	{ "?", -1, false},
	{ ".tar", 4, true},
	{ ".orig.tar", 9, true},
	{ ".diff", 5, true},
	{ ".dsc", 4, false},
	{ ".deb", 4, false},
	{ ".udeb", 5, false}
};
120 121 122 123 124 125 126 127 128 129 130

struct dscfile {
	struct fileentry *file;
	char *name;
	char *version;
	struct strlist binaries;
	char *maintainer;
	char *controlchunk;
	// hard to get:
	char *section, *priority;
	// TODO: check Architectures?
131 132
	struct checksumsarray expected;
	struct fileentry **uplink;
133
	bool parsed, modified;
134 135 136
};

static void dscfile_free(struct dscfile *p) {
137
	if (p == NULL)
138 139 140 141 142 143 144 145
		return;

	free(p->name);
	free(p->version);
	free(p->maintainer);
	free(p->controlchunk);
	free(p->section);
	free(p->priority);
146 147
	checksumsarray_done(&p->expected);
	free(p->uplink);
148 149 150 151 152 153 154
	free(p);
}

struct fileentry {
	struct fileentry *next;
	char *basename; size_t namelen;
	char *fullfilename;
155 156
	/* NULL means was not listed there yet: */
	struct checksums *checksumsfromchanges,
157
			 *realchecksums;
158 159
	char *section, *priority;
	enum filetype type;
160
	enum compression compression;
161 162 163 164 165 166 167
	/* only if type deb or udeb */
	struct binaryfile *deb;
	/* only if type dsc */
	struct dscfile *dsc;
	int refcount;
};
struct changes;
168
static struct fileentry *add_fileentry(struct changes *c, const char *basefilename, size_t len, bool source, /*@null@*//*@out@*/size_t *ofs_p);
169 170

struct changes {
Bernhard Link's avatar
Bernhard Link committed
171 172 173 174 175
	/* the filename of the .changes file */
	char *filename;
	/* directory of filename */
	char *basedir;
	/* Contents of the .changes file: */
176 177 178
	char *name;
	char *version;
	char *maintainer;
179
	char *control;
180 181 182 183 184 185 186
	struct strlist architectures;
	struct strlist distributions;
	size_t binarycount;
	struct binary {
		char *name;
		char *description;
		struct binaryfile *files;
187
		bool missedinheader, uncheckable;
188 189
	} *binaries;
	struct fileentry *files;
190
	bool modified;
191 192
};

193
static void fileentry_free(/*@only@*/struct fileentry *f) {
194
	if (f == NULL)
195 196 197
		return;
	free(f->basename);
	free(f->fullfilename);
198 199
	checksums_free(f->checksumsfromchanges);
	checksums_free(f->realchecksums);
200 201
	free(f->section);
	free(f->priority);
202
	if (f->type == ft_DEB || f->type == ft_UDEB) {
203
		binaryfile_free(f->deb);
204
	} else if (f->type == ft_DSC) {
205 206 207 208 209
		dscfile_free(f->dsc);
	}
	free(f);
}

210
static void changes_free(struct changes *c) {
211
	unsigned int i;
212

213
	if (c == NULL)
214 215
		return;

Bernhard Link's avatar
Bernhard Link committed
216 217
	free(c->filename);
	free(c->basedir);
218 219 220
	free(c->name);
	free(c->version);
	free(c->maintainer);
221
	free(c->control);
222 223
	strlist_done(&c->architectures);
	strlist_done(&c->distributions);
224
	for (i = 0 ; i < c->binarycount ; i++) {
225 226 227 228 229
		free(c->binaries[i].name);
		free(c->binaries[i].description);
		// .files belongs elsewhere
	}
	free(c->binaries);
230
	while (c->files) {
231 232
		struct fileentry *f = c->files;
		c->files = f->next;
233
		fileentry_free(f);
234 235 236 237
	}
	free(c);
}

Bernhard Link's avatar
Bernhard Link committed
238
static struct fileentry **find_fileentry(struct changes *c, const char *basefilename, size_t basenamelen, size_t *ofs_p) {
239 240
	struct fileentry **fp = &c->files;
	struct fileentry *f;
241 242
	size_t ofs = 0;

243 244 245
	while ((f=*fp) != NULL) {
		if (f->namelen == basenamelen &&
		    strncmp(basefilename, f->basename, basenamelen) == 0) {
246
			break;
Bernhard Link's avatar
Bernhard Link committed
247
		}
248
		fp = &f->next;
249
		ofs++;
250
	}
251
	if (ofs_p != NULL)
Bernhard Link's avatar
Bernhard Link committed
252 253 254 255 256 257 258 259 260
		*ofs_p = ofs;
	return fp;
}

static struct fileentry *add_fileentry(struct changes *c, const char *basefilename, size_t len, bool source, size_t *ofs_p) {
	size_t ofs = 0;
	struct fileentry **fp = find_fileentry(c, basefilename, len, &ofs);
	struct fileentry *f = *fp;

261
	if (f == NULL) {
262 263
		enum compression;

264 265
		f = zNEW(struct fileentry);
		if (FAILEDTOALLOC(f))
266
			return NULL;
267
		f->basename = strndup(basefilename, len);
268
		f->namelen = len;
269

270 271 272 273 274 275
		if (FAILEDTOALLOC(f->basename)) {
			free(f);
			return NULL;
		}
		*fp = f;

276 277 278
		/* guess compression */
		f->compression = compression_by_suffix(f->basename, &len);

279
		/* guess type */
280 281
		for (f->type = source?ft_MaxInSource:ft_Max ;
				f->type > ft_UNKNOWN ; f->type--) {
282 283
			size_t l = typesuffix[f->type].len;

284 285
			if (f->compression != c_none &&
					!typesuffix[f->type].allowcompressed)
286
				continue;
287
			if (len <= l)
288
				continue;
289
			if (strncmp(f->basename + (len-l),
290
						typesuffix[f->type].suffix,
291
						l) == 0)
292 293 294
				break;
		}
	}
295
	if (ofs_p != NULL)
296
		*ofs_p = ofs;
297 298 299
	return f;
}

300
static retvalue searchforfile(const char *changesdir, const char *basefilename, /*@null@*/const struct strlist *searchpath, /*@null@*/const char *searchfirstin, char **result) {
301
	int i; bool found;
302 303
	char *fullname;

304
	if (searchfirstin != NULL) {
305
		fullname = calc_dirconcat(searchfirstin, basefilename);
306
		if (FAILEDTOALLOC(fullname))
307
			return RET_ERROR_OOM;
308
		if (isregularfile(fullname)) {
309 310 311 312 313 314
			*result = fullname;
			return RET_OK;
		}
		free(fullname);
	}

315
	fullname = calc_dirconcat(changesdir, basefilename);
316
	if (FAILEDTOALLOC(fullname))
317 318 319 320
		return RET_ERROR_OOM;

	found = isregularfile(fullname);
	i = 0;
321
	while (!found && searchpath != NULL && i < searchpath->count) {
322 323
		free(fullname);
		fullname = calc_dirconcat(searchpath->values[i],
324
				basefilename);
325
		if (FAILEDTOALLOC(fullname))
326
			return RET_ERROR_OOM;
327
		if (isregularfile(fullname)) {
328
			found = true;
Bernhard Link's avatar
Bernhard Link committed
329 330 331
			break;
		}
		i++;
332
	}
333
	if (found) {
334 335 336 337 338 339 340 341
		*result = fullname;
		return RET_OK;
	} else {
		free(fullname);
		return RET_NOTHING;
	}
}

342
static retvalue findfile(const char *filename, const struct changes *c, /*@null@*/const struct strlist *searchpath, /*@null@*/const char *searchfirstin, char **result) {
Bernhard Link's avatar
Bernhard Link committed
343
	char *fullfilename;
344

345
	if (rindex(filename, '/') == NULL) {
Bernhard Link's avatar
Bernhard Link committed
346 347
		retvalue r;

348 349
		r = searchforfile(c->basedir, filename,
				searchpath, searchfirstin, &fullfilename);
350
		if (!RET_IS_OK(r))
Bernhard Link's avatar
Bernhard Link committed
351 352
			return r;
	} else {
353
		if (!isregularfile(filename))
Bernhard Link's avatar
Bernhard Link committed
354 355
			return RET_NOTHING;
		fullfilename = strdup(filename);
356
		if (FAILEDTOALLOC(fullfilename))
Bernhard Link's avatar
Bernhard Link committed
357
			return RET_ERROR_OOM;
358
	}
Bernhard Link's avatar
Bernhard Link committed
359 360 361 362
	*result = fullfilename;
	return RET_OK;
}

363 364
static retvalue add_file(struct changes *c, /*@only@*/char *basefilename, /*@only@*/char *fullfilename, enum filetype type, struct fileentry **file) {
	size_t basenamelen = strlen(basefilename);
Bernhard Link's avatar
Bernhard Link committed
365 366 367 368 369
	struct fileentry **fp;
	struct fileentry *f;

	fp = find_fileentry(c, basefilename, basenamelen, NULL);
	f = *fp;
370

371
	if (f != NULL) {
372 373 374 375 376
		*file = f;
		free(basefilename);
		free(fullfilename);
		return RET_NOTHING;
	}
377 378 379
	assert (f == NULL);
	f = zNEW(struct fileentry);
	if (FAILEDTOALLOC(f)) {
380 381
		free(basefilename);
		free(fullfilename);
382 383
		return RET_ERROR_OOM;
	}
384
	f->basename = basefilename;
385
	f->namelen = basenamelen;
Bernhard Link's avatar
Bernhard Link committed
386
	f->fullfilename = fullfilename;
387
	f->type = type;
388
	f->compression = c_none;
389 390 391 392 393 394

	*fp = f;
	*file = f;
	return RET_OK;
}

395 396

static struct binary *get_binary(struct changes *c, const char *p, size_t len) {
397
	unsigned int j;
398

399 400 401
	for (j = 0 ; j < c->binarycount ; j++) {
		if (strncmp(c->binaries[j].name, p, len) == 0 &&
				c->binaries[j].name[len] == '\0')
402 403
			break;
	}
404
	if (j == c->binarycount) {
405 406 407
		char *name = strndup(p, len);
		struct binary *n;

408
		if (FAILEDTOALLOC(name))
409
			return NULL;
410 411
		n = realloc(c->binaries, (j+1)*sizeof(struct binary));
		if (FAILEDTOALLOC(n)) {
412 413 414 415 416 417 418 419
			free(name);
			return NULL;
		}
		c->binaries = n;
		c->binarycount = j+1;
		c->binaries[j].name = name;
		c->binaries[j].description = NULL;
		c->binaries[j].files = NULL;
420 421
		c->binaries[j].missedinheader = true;
		c->binaries[j].uncheckable = false;
422
	}
423
	assert (j < c->binarycount);
424 425 426 427 428 429
	return &c->binaries[j];
}

static retvalue parse_changes_description(struct changes *c, struct strlist *tmp) {
	int i;

430
	for (i = 0 ; i < tmp->count ; i++) {
431 432 433 434
		struct binary *b;
		const char *p = tmp->values[i];
		const char *e = p;
		const char *d;
435
		while (*e != '\0' && *e != ' ' && *e != '\t')
436 437
			e++;
		d = e;
438
		while (*d == ' ' || *d == '\t')
439
			d++;
440
		if (*d == '-')
441
			d++;
442
		while (*d == ' ' || *d == '\t')
443 444 445
			d++;

		b = get_binary(c, p, e-p);
446
		if (FAILEDTOALLOC(b))
447 448 449
			return RET_ERROR_OOM;

		b->description = strdup(d);
450
		if (FAILEDTOALLOC(b->description))
451 452 453 454 455
			return RET_ERROR_OOM;
	}
	return RET_OK;
}

456
static retvalue parse_changes_files(struct changes *c, struct strlist filelines[cs_hashCOUNT]) {
457 458
	int i;
	struct fileentry *f;
459
	retvalue r;
460 461 462 463 464 465
	struct hashes *hashes;
	struct strlist *tmp;
	size_t ofs, count = 0;
	enum checksumtype cs;

	tmp = &filelines[cs_md5sum];
466 467
	hashes = nzNEW(tmp->count, struct hashes);
	if (FAILEDTOALLOC(hashes))
468
		return RET_ERROR_OOM;
469

470
	for (i = 0 ; i < tmp->count ; i++) {
471
		char *p;
472 473 474
		const char *md5start, *md5end, *sizestart, *sizeend,
		           *sectionstart, *sectionend, *priostart, *prioend,
		           *filestart, *fileend;
475 476 477
		p = tmp->values[i];
#undef xisspace
#define xisspace(c) (c == ' ' || c == '\t')
478
		while (*p !='\0' && xisspace(*p))
479 480
			p++;
		md5start = p;
481 482 483 484
		while ((*p >= '0' && *p <= '9') ||
				(*p >= 'A' && *p <= 'F') ||
				(*p >= 'a' && *p <= 'f')) {
			if (*p >= 'A' && *p <= 'F')
485
				(*p) += 'a' - 'A';
486
			p++;
487
		}
488
		md5end = p;
489
		while (*p !='\0' && !xisspace(*p))
490
			p++;
491
		while (*p !='\0' && xisspace(*p))
492
			p++;
493
		while (*p == '0' && ('0' <= p[1] && p[1] <= '9'))
494
			p++;
495
		sizestart = p;
496
		while ((*p >= '0' && *p <= '9'))
497 498
			p++;
		sizeend = p;
499
		while (*p !='\0' && !xisspace(*p))
500
			p++;
501
		while (*p !='\0' && xisspace(*p))
502 503
			p++;
		sectionstart = p;
504
		while (*p !='\0' && !xisspace(*p))
505 506
			p++;
		sectionend = p;
507
		while (*p !='\0' && xisspace(*p))
508 509
			p++;
		priostart = p;
510
		while (*p !='\0' && !xisspace(*p))
511 512
			p++;
		prioend = p;
513
		while (*p !='\0' && xisspace(*p))
514 515
			p++;
		filestart = p;
516
		while (*p !='\0' && !xisspace(*p))
517 518
			p++;
		fileend = p;
519
		while (*p !='\0' && xisspace(*p))
520
			p++;
521 522 523 524
		if (*p != '\0') {
			fprintf(stderr,
"Unexpected sixth argument in '%s'!\n",
					tmp->values[i]);
525
			free(hashes);
526 527
			return RET_ERROR;
		}
528
		if (fileend - filestart == 0)
529 530
			continue;
		f = add_fileentry(c, filestart, fileend-filestart, false, &ofs);
531 532
		assert (ofs <= count);
		if (ofs == count)
533
			count++;
534 535 536
		if (hashes[ofs].hashes[cs_md5sum].start != NULL) {
			fprintf(stderr,
"WARNING: Multiple occourance of '%s' in .changes file!\nIgnoring all but the first one.\n",
537 538 539
					f->basename);
			continue;
		}
540 541 542 543 544
		hashes[ofs].hashes[cs_md5sum].start = md5start;
		hashes[ofs].hashes[cs_md5sum].len = md5end - md5start;
		hashes[ofs].hashes[cs_length].start = sizestart;
		hashes[ofs].hashes[cs_length].len = sizeend - sizestart;

545
		if (sectionend - sectionstart == 1 && *sectionstart == '-') {
546 547
			f->section = NULL;
		} else {
548 549 550
			f->section = strndup(sectionstart,
					sectionend - sectionstart);
			if (FAILEDTOALLOC(f->section))
551 552
				return RET_ERROR_OOM;
		}
553
		if (prioend - priostart == 1 && *priostart == '-') {
554 555
			f->priority = NULL;
		} else {
556 557
			f->priority = strndup(priostart, prioend - priostart);
			if (FAILEDTOALLOC(f->priority))
558 559 560
				return RET_ERROR_OOM;
		}
	}
Bernhard Link's avatar
Bernhard Link committed
561
	const char * const hashname[cs_hashCOUNT] = {"Md5", "Sha1", "Sha256" };
562
	for (cs = cs_firstEXTENDED ; cs < cs_hashCOUNT ; cs++) {
563 564
		tmp = &filelines[cs];

565
		for (i = 0 ; i < tmp->count ; i++) {
566
			char *p;
567 568
			const char *hashstart, *hashend, *sizestart, *sizeend,
			      *filestart, *fileend;
569
			p = tmp->values[i];
570
			while (*p !='\0' && xisspace(*p))
571 572
				p++;
			hashstart = p;
573 574 575 576
			while ((*p >= '0' && *p <= '9') ||
					(*p >= 'A' && *p <= 'F') ||
					(*p >= 'a' && *p <= 'f') ) {
				if (*p >= 'A' && *p <= 'F')
577 578 579 580
					(*p) += 'a' - 'A';
				p++;
			}
			hashend = p;
581
			while (*p !='\0' && !xisspace(*p))
582
				p++;
583
			while (*p !='\0' && xisspace(*p))
584
				p++;
585
			while (*p == '0' && ('0' <= p[1] && p[1] <= '9'))
586 587
				p++;
			sizestart = p;
588
			while ((*p >= '0' && *p <= '9'))
589 590
				p++;
			sizeend = p;
591
			while (*p !='\0' && !xisspace(*p))
592
				p++;
593
			while (*p !='\0' && xisspace(*p))
594 595
				p++;
			filestart = p;
596
			while (*p !='\0' && !xisspace(*p))
597 598
				p++;
			fileend = p;
599
			while (*p !='\0' && xisspace(*p))
600
				p++;
601 602 603 604
			if (*p != '\0') {
				fprintf(stderr,
"Unexpected forth argument in '%s'!\n",
						tmp->values[i]);
605 606
				return RET_ERROR;
			}
607
			if (fileend - filestart == 0)
608
				continue;
609 610 611
			f = add_fileentry(c, filestart, fileend-filestart,
					false, &ofs);
			assert (ofs <= count);
612
			// until md5sums are no longer obligatory:
613
			if (ofs == count)
614
				continue;
615 616 617 618
			if (hashes[ofs].hashes[cs].start != NULL) {
				fprintf(stderr,
"WARNING: Multiple occourance of '%s' in Checksums-'%s' of .changes file!\n"
"Ignoring all but the first one.\n",
619 620 621 622 623
						f->basename, hashname[cs]);
				continue;
			}
			hashes[ofs].hashes[cs].start = hashstart;
			hashes[ofs].hashes[cs].len = hashend - hashstart;
624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642

			size_t sizelen = sizeend - sizestart;

			if (hashes[ofs].hashes[cs_length].start == NULL) {
				hashes[ofs].hashes[cs_length].start = sizestart;
				hashes[ofs].hashes[cs_length].len = sizelen;

			} else if (hashes[ofs].hashes[cs_length].len != sizelen
			           || memcmp(sizestart,
			                     hashes[ofs].hashes[cs_length].start,
			                     sizelen) != 0) {
				fprintf(stderr,
"Error: Contradicting file size information for '%s' ('%.*s' vs '%.*s') in .changes file\n",
					f->basename,
					(int)sizelen, sizestart,
					(int)hashes[ofs].hashes[cs_length].len,
					hashes[ofs].hashes[cs_length].start);
				return RET_ERROR;
			}
643 644 645
		}
	}
	ofs = 0;
646
	for (f = c->files ; f != NULL ; f = f->next, ofs++) {
647 648
		r = checksums_initialize(&f->checksumsfromchanges,
				hashes[ofs].hashes);
649
		if (RET_WAS_ERROR(r))
650 651
			return r;
	}
652
	assert (count == ofs);
Bernhard Link's avatar
Bernhard Link committed
653
	free(hashes);
654

655 656 657
	return RET_OK;
}

658
static retvalue read_dscfile(const char *fullfilename, struct dscfile **dsc) {
659
	struct dscfile *n;
660 661
	struct strlist filelines[cs_hashCOUNT];
	enum checksumtype cs;
662 663
	retvalue r;

664 665
	n = zNEW(struct dscfile);
	if (FAILEDTOALLOC(n))
666
		return RET_ERROR_OOM;
667
	r = signature_readsignedchunk(fullfilename, fullfilename,
668
			&n->controlchunk, NULL, NULL);
669
	assert (r != RET_NOTHING);
670
	// TODO: can this be ignored sometimes?
671
	if (RET_WAS_ERROR(r)) {
672 673 674
		free(n);
		return r;
	}
675
	r = chunk_getname(n->controlchunk, "Source", &n->name, false);
676
	if (RET_WAS_ERROR(r)) {
677 678 679
		dscfile_free(n);
		return r;
	}
680 681
	r = chunk_getvalue(n->controlchunk, "Maintainer", &n->maintainer);
	if (RET_WAS_ERROR(r)) {
682 683 684
		dscfile_free(n);
		return r;
	}
685 686
	r = chunk_getvalue(n->controlchunk, "Version", &n->version);
	if (RET_WAS_ERROR(r)) {
687 688 689 690
		dscfile_free(n);
		return r;
	}

691
	/* usually not here, but hidden in the contents */
692 693
	r = chunk_getvalue(n->controlchunk, "Section", &n->section);
	if (RET_WAS_ERROR(r)) {
694 695 696 697
		dscfile_free(n);
		return r;
	}
	/* dito */
698 699
	r = chunk_getvalue(n->controlchunk, "Priority", &n->priority);
	if (RET_WAS_ERROR(r)) {
700 701 702
		dscfile_free(n);
		return r;
	}
703

704 705
	for (cs = cs_md5sum ; cs < cs_hashCOUNT ; cs++) {
		assert (source_checksum_names[cs] != NULL);
706 707
		r = chunk_getextralinelist(n->controlchunk,
				source_checksum_names[cs], &filelines[cs]);
708 709
		if (r == RET_NOTHING) {
			if (cs == cs_md5sum) {
710 711 712
				fprintf(stderr,
"Error: Missing 'Files' entry in '%s'!\n",             fullfilename);
				r = RET_ERROR;
713
			}
714 715
			strlist_init(&filelines[cs]);
		}
716 717
		if (RET_WAS_ERROR(r)) {
			while (cs-- > cs_md5sum) {
718
				strlist_done(&filelines[cs]);
719
			}
720 721
			dscfile_free(n);
			return r;
722
		}
723 724
	}
	r = checksumsarray_parse(&n->expected, filelines, fullfilename);
725
	for (cs = cs_md5sum ; cs < cs_hashCOUNT ; cs++) {
726 727
		strlist_done(&filelines[cs]);
	}
728
	if (RET_WAS_ERROR(r)) {
729 730 731
		dscfile_free(n);
		return r;
	}
732 733 734
	if (n->expected.names.count > 0) {
		n->uplink = nzNEW(n->expected.names.count, struct fileentry *);
		if (FAILEDTOALLOC(n->uplink)) {
735 736 737 738
			dscfile_free(n);
			return RET_ERROR_OOM;
		}
	}
739 740 741 742 743 744 745
	*dsc = n;
	return RET_OK;
}

static retvalue parse_dsc(struct fileentry *dscfile, struct changes *changes) {
	struct dscfile *n;
	retvalue r;
746
	int i;
747

748
	if (dscfile->fullfilename == NULL)
749 750
		return RET_NOTHING;
	r = read_dscfile(dscfile->fullfilename, &n);
751 752
	assert (r != RET_NOTHING);
	if (RET_WAS_ERROR(r))
753
		return r;
754
	for (i =  0 ; i < n->expected.names.count ; i++) {
755
		const char *basefilename = n->expected.names.values[i];
756
		n->uplink[i] = add_fileentry(changes,
757
				basefilename, strlen(basefilename),
758
				true, NULL);
759
		if (FAILEDTOALLOC(n->uplink[i])) {
760 761 762 763
			dscfile_free(n);
			return RET_ERROR_OOM;
		}
	}
764 765 766
	dscfile->dsc = n;
	return RET_OK;
}
767

768 769 770 771 772 773
#define DSC_WRITE_FILES 1
#define DSC_WRITE_ALL 0xFFFF
#define flagset(a) (flags & a) != 0

static retvalue write_dsc_file(struct fileentry *dscfile, unsigned int flags) {
	struct dscfile *dsc = dscfile->dsc;
774
	int i;
775 776 777
	struct chunkeditfield *cef;
	retvalue r;
	char *control; size_t controllen;
778
	struct checksums *checksums;
779
	char *destfilename;
780
	enum checksumtype cs;
781

782
	if (flagset(DSC_WRITE_FILES)) {
783
		cef = NULL;
784
		for (cs = cs_hashCOUNT ; (cs--) > cs_md5sum ; ) {
785 786 787
			cef = cef_newfield(source_checksum_names[cs],
					CEF_ADD, CEF_LATE,
					dsc->expected.names.count, cef);
788
			if (FAILEDTOALLOC(cef))
789
				return RET_ERROR_OOM;
790 791 792
			for (i = 0 ; i < dsc->expected.names.count ; i++) {
				const char *basefilename =
					dsc->expected.names.values[i];
793 794 795
				const char *hash, *size;
				size_t hashlen, sizelen;

796
				if (!checksums_gethashpart(dsc->expected.checksums[i],
797
							cs, &hash, &hashlen,
798 799
							&size, &sizelen)) {
					assert (cs != cs_md5sum);
800 801 802
					cef = cef_pop(cef);
					break;
				}
803 804
				cef_setline2(cef, i, hash, hashlen,
						size, sizelen,
805
						1, basefilename, NULL);
806
			}
807 808 809 810 811 812
		}
	} else
		cef = NULL;

	r = chunk_edit(dsc->controlchunk, &control, &controllen, cef);
	cef_free(cef);
813
	if (RET_WAS_ERROR(r))
814
		return r;
815
	assert (RET_IS_OK(r));
816 817 818 819

	// TODO: try to add the signatures to it again...

	// TODO: add options to place changed files in different directory...
820
	if (dscfile->fullfilename != NULL)
821
		destfilename = strdup(dscfile->fullfilename);
822
	else
823
		destfilename = strdup(dscfile->basename);
824
	if (FAILEDTOALLOC(destfilename)) {
825 826 827 828
		free(control);
		return RET_ERROR_OOM;
	}

829
	r = checksums_replace(destfilename, control, controllen, &checksums);
830
	if (RET_WAS_ERROR(r)) {
831 832 833 834
		free(destfilename);
		free(control);
		return r;
	}
835
	assert (RET_IS_OK(r));
836 837 838

	free(dscfile->fullfilename);
	dscfile->fullfilename = destfilename;
839 840
	checksums_free(dscfile->realchecksums);
	dscfile->realchecksums = checksums;
841 842 843 844 845
	free(dsc->controlchunk);
	dsc->controlchunk = control;
	return RET_OK;
}

Bernhard Link's avatar
Bernhard Link committed
846
static retvalue read_binaryfile(const char *fullfilename, struct binaryfile **result) {
847 848 849
	retvalue r;
	struct binaryfile *n;

850 851
	n = zNEW(struct binaryfile);
	if (FAILEDTOALLOC(n))
852 853
		return RET_ERROR_OOM;

Bernhard Link's avatar
Bernhard Link committed
854
	r = extractcontrol(&n->controlchunk, fullfilename);
855
	if (!RET_IS_OK(r)) {
856
		free(n);
857
		if (r == RET_ERROR_OOM)
858 859 860 861 862
			return r;
		else
			return RET_NOTHING;
	}

863
	r = chunk_getname(n->controlchunk, "Package", &n->name, false);
864
	if (RET_WAS_ERROR(r)) {
865 866 867 868
		binaryfile_free(n);
		return r;
	}
	r = chunk_getvalue(n->controlchunk, "Version", &n->version);
869
	if (RET_WAS_ERROR(r)) {
870 871 872 873 874
		binaryfile_free(n);
		return r;
	}
	r = chunk_getnameandversion(n->controlchunk, "Source",
			&n->sourcename, &n->sourceversion);
875
	if (RET_WAS_ERROR(r)) {
876 877 878 879
		binaryfile_free(n);
		return r;
	}
	r = chunk_getvalue(n->controlchunk, "Maintainer", &n->maintainer);
880
	if (RET_WAS_ERROR(r)) {
881 882 883 884
		binaryfile_free(n);
		return r;
	}
	r = chunk_getvalue(n->controlchunk, "Architecture", &n->architecture);
885
	if (RET_WAS_ERROR(r)) {
886 887 888
		binaryfile_free(n);
		return r;
	}
889 890
	r = chunk_getvalue(n->controlchunk, "Section", &n->section);
	if (RET_WAS_ERROR(r)) {
891 892 893
		binaryfile_free(n);
		return r;
	}
894 895
	r = chunk_getvalue(n->controlchunk, "Priority", &n->priority);
	if (RET_WAS_ERROR(r)) {
896 897 898
		binaryfile_free(n);
		return r;
	}
899 900
	r = chunk_getvalue(n->controlchunk, "Description", &n->shortdescription);
	if (RET_WAS_ERROR(r)) {
901 902 903
		binaryfile_free(n);
		return r;
	}
Bernhard Link's avatar
Bernhard Link committed
904 905 906 907 908 909 910 911
	*result = n;
	return RET_OK;
}

static retvalue parse_deb(struct fileentry *debfile, struct changes *changes) {
	retvalue r;
	struct binaryfile *n;

912
	if (debfile->fullfilename == NULL)
Bernhard Link's avatar
Bernhard Link committed
913 914
		return RET_NOTHING;
	r = read_binaryfile(debfile->fullfilename, &n);
915
	if (!RET_IS_OK(r))
Bernhard Link's avatar
Bernhard Link committed
916
		return r;
917
	if (n->name != NULL) {
918
		n->binary = get_binary(changes, n->name, strlen(n->name));
919
		if (FAILEDTOALLOC(n->binary)) {
920 921 922 923 924 925 926 927 928
			binaryfile_free(n);
			return RET_ERROR_OOM;
		}
		n->next = n->binary->files;
		n->binary->files = n;
	}

	debfile->deb = n;
	return RET_OK;
929 930 931 932 933 934 935 936 937
}

static retvalue processfiles(const char *changesfilename, struct changes *changes,
		const struct strlist *searchpath) {
	char *dir;
	struct fileentry *file;
	retvalue r;

	r = dirs_getdirectory(changesfilename, &dir);
938
	if (RET_WAS_ERROR(r))
939 940
		return r;

941 942
	for (file = changes->files; file != NULL ; file = file->next) {
		assert (file->fullfilename == NULL);
943

944 945
		r = searchforfile(dir, file->basename, searchpath, NULL,
				&file->fullfilename);
946

947 948 949 950 951 952
		if (RET_IS_OK(r)) {
			if (file->type == ft_DSC)
				r = parse_dsc(file, changes);
			else if (file->type == ft_DEB || file->type == ft_UDEB)
				r = parse_deb(file, changes);
			if (RET_WAS_ERROR(r)) {
953
				free(dir);
954
				return r;
955 956
			}
		}
957

958
		if (r == RET_NOTHING) {
959
			/* apply heuristics when not readable */
960 961
			if (file->type == ft_DSC) {
			} else if (file->type == ft_DEB || file->type == ft_UDEB) {
962 963 964
				struct binary *b; size_t len;

				len = 0;
965 966
				while (file->basename[len] != '_' &&
						file->basename[len] != '\0')
967 968
					len++;
				b = get_binary(changes, file->basename, len);
969
				if (FAILEDTOALLOC(b)) {
970
					free(dir);
971
					return RET_ERROR_OOM;
972
				}
973
				b->uncheckable = true;
974 975 976 977 978 979 980 981 982 983
			}
		}
	}
	free(dir);
	return RET_OK;
}

static retvalue parse_changes(const char *changesfile, const char *chunk, struct changes **changes, const struct strlist *searchpath) {
	retvalue r;
	struct strlist tmp;
984 985
	struct strlist filelines[cs_hashCOUNT];
	enum checksumtype cs;
986
#define R if (RET_WAS_ERROR(r)) { changes_free(n); return r; }
987

988 989
	struct changes *n = zNEW(struct changes);
	if (FAILEDTOALLOC(n))
990
		return RET_ERROR_OOM;
Bernhard Link's avatar
Bernhard Link committed
991
	n->filename = strdup(changesfile);
992
	if (FAILEDTOALLOC(n->filename)) {
Bernhard Link's avatar
Bernhard Link committed
993 994 995 996 997
		changes_free(n);
		return RET_ERROR_OOM;
	}
	r = dirs_getdirectory(changesfile, &n->basedir);
	R;
998 999 1000
	// TODO: do getname here? trim spaces?
	r = chunk_getvalue(chunk, "Source", &n->name);
	R;
1001 1002 1003
	if (r == RET_NOTHING) {
		fprintf(stderr, "Missing 'Source:' field in %s!\n",
				changesfile);
1004 1005 1006 1007
		n->name = NULL;
	}
	r = chunk_getvalue(chunk, "Version", &n->version);
	R;
1008 1009 1010
	if (r == RET_NOTHING) {
		fprintf(stderr, "Missing 'Version:' field in %s!\n",
				changesfile);
1011 1012 1013 1014
		n->version = NULL;
	}
	r = chunk_getwordlist(chunk, "Architecture", &n->architectures);
	R;
1015
	if (r == RET_NOTHING)
1016 1017 1018
		strlist_init(&n->architectures);
	r = chunk_getwordlist(chunk, "Distribution", &n->distributions);
	R;
1019
	if (r == RET_NOTHING)
1020 1021 1022
		strlist_init(&n->distributions);
	r = chunk_getvalue(chunk, "Maintainer", &n->maintainer);
	R;
1023 1024 1025
	if (r == RET_NOTHING) {
		fprintf(stderr, "Missing 'Maintainer:' field in %s!\n",
				changesfile);
1026 1027
		n->maintainer = NULL;
	}
1028
	r = chunk_getuniqwordlist(chunk, "Binary", &tmp);
1029
	R;
1030
	if (r == RET_NOTHING) {
1031 1032 1033 1034
		n->binaries = NULL;
	} else {
		int i;

1035 1036 1037
		assert (RET_IS_OK(r));
		n->binaries = nzNEW(tmp.count, struct binary);
		if (FAILEDTOALLOC(n->binaries)) {
1038 1039 1040
			changes_free(n);
			return RET_ERROR_OOM;
		}
1041
		for (i = 0 ; i < tmp.count ; i++) {
1042 1043 1044 1045 1046 1047 1048
			n->binaries[i].name = tmp.values[i];
		}
		n->binarycount = tmp.count;
		free(tmp.values);
	}
	r = chunk_getextralinelist(chunk, "Description", &tmp);
	R;
1049
	if (RET_IS_OK(r)) {
1050 1051
		r = parse_changes_description(n, &tmp);
		strlist_done(&tmp);
1052
		if (RET_WAS_ERROR(r)) {
1053
			changes_free(n);
1054
			return r;
1055 1056
		}
	}
1057 1058
	for (cs = cs_md5sum ; cs < cs_hashCOUNT ; cs++) {
		assert (changes_checksum_names[cs] != NULL);
1059 1060
		r = chunk_getextralinelist(chunk,
				changes_checksum_names[cs], &filelines[cs]);
1061 1062
		if (r == RET_NOTHING) {
			if (cs == cs_md5sum)
1063 1064 1065
				break;
			strlist_init(&filelines[cs]);
		}
1066 1067
		if (RET_WAS_ERROR(r)) {
			while (cs-- > cs_md5sum) {
1068 1069 1070 1071 1072 1073
				strlist_done(&filelines[cs]);
			}
			changes_free(n);
			return r;
		}
	}
1074
	if (cs == cs_hashCOUNT) {
1075
		r = parse_changes_files(n, filelines);
1076
		for (cs = cs_md5sum ; cs < cs_hashCOUNT ; cs++) {
1077 1078
			strlist_done(&filelines[cs]);
		}
1079
		if (RET_WAS_ERROR(r)) {
1080
			changes_free(n);
1081
			return r;
1082 1083 1084 1085 1086 1087 1088 1089
		}
	}
	r = processfiles(changesfile, n, searchpath);
	R;
	*changes = n;
	return RET_OK;
}

1090 1091 1092 1093 1094 1095
#define CHANGES_WRITE_FILES		0x01
#define CHANGES_WRITE_BINARIES		0x02
#define CHANGES_WRITE_SOURCE		0x04
#define CHANGES_WRITE_VERSION		0x08
#define CHANGES_WRITE_ARCHITECTURES	0x10
#define CHANGES_WRITE_MAINTAINER 	0x20
1096
#define CHANGES_WRITE_DISTRIBUTIONS 	0x40
1097 1098
#define CHANGES_WRITE_ALL 	      0xFFFF

1099
static retvalue write_changes_file(const char *changesfilename, struct changes *c, unsigned int flags, bool fakefields) {
1100 1101 1102 1103 1104 1105 1106 1107 1108
	struct chunkeditfield *cef;
	char datebuffer[100];
	retvalue r;
	char *control; size_t controllen;
	unsigned int filecount = 0;
	struct fileentry *f;
	struct tm *tm; time_t t;
	unsigned int i;
	struct strlist binaries;
1109
	enum checksumtype cs;
1110 1111 1112

	strlist_init(&binaries);

1113 1114
	for (f = c->files; f != NULL ; f = f->next) {
		if (f->checksumsfromchanges != NULL)
1115 1116 1117
			filecount++;
	}

1118
	if (flagset(CHANGES_WRITE_FILES)) {
1119
		cef = NULL;
1120
		for (cs = cs_md5sum ; cs < cs_hashCOUNT ; cs++) {
1121 1122
			cef = cef_newfield(changes_checksum_names[cs],
					CEF_ADD, CEF_LATE, filecount, cef);
1123
			if (FAILEDTOALLOC(cef))
1124 1125
				return RET_ERROR_OOM;
			i = 0;
1126
			for (f = c->files; f != NULL ; f = f->next) {
1127 1128 1129
				const char *hash, *size;
				size_t hashlen, sizelen;

1130
				if (f->checksumsfromchanges == NULL)
1131
					continue;
1132
				if (!checksums_gethashpart(f->checksumsfromchanges,
1133
							cs, &hash, &hashlen,
1134 1135
							&size, &sizelen)) {
					assert (cs != cs_md5sum);
1136 1137 1138
					cef = cef_pop(cef);
					break;
				}
1139
				if (cs == cs_md5sum)
1140 1141
					cef_setline2(cef, i,
						hash, hashlen, size, sizelen,
1142 1143 1144 1145
						3,
						f->section?f->section:"-",
						f->priority?f->priority:"-",
						f->basename, NULL);
1146 1147 1148 1149 1150 1151 1152
				else
					/* strange way, but as dpkg-genchanges
					 * does it this way... */
					cef_setline2(cef, i,
						hash, hashlen, size, sizelen,
						1,
						f->basename, NULL);
1153 1154
				i++;
			}
1155
			assert (f != NULL || i == filecount);
1156 1157 1158
		}
	} else {
		cef = cef_newfield("Files", CEF_KEEP, CEF_LATE, 0, NULL);
1159
		if (FAILEDTOALLOC(cef))
1160 1161
			return RET_ERROR_OOM;
	}
1162
	if (fakefields) {
1163
		cef = cef_newfield("Changes", CEF_ADDMISSED, CEF_LATE, 0, cef);
1164
		if (FAILEDTOALLOC(cef))
1165
			return RET_ERROR_OOM;
1166 1167
		cef_setdata(cef,
"\n Changes information missing, as not an original .changes file");
1168 1169
	} else {
		cef = cef_newfield("Changes", CEF_KEEP, CEF_LATE, 0, cef);
1170
		if (FAILEDTOALLOC(cef))
1171 1172
			return RET_ERROR_OOM;
	}
1173
	cef = cef_newfield("Closes", CEF_KEEP, CEF_LATE, 0, cef);
1174 1175 1176
	if (FAILEDTOALLOC(cef))
		return RET_ERROR_OOM;
	if (flagset(CHANGES_WRITE_BINARIES)) {
1177
		unsigned int count = 0