updates.c 28.3 KB
Newer Older
Bernhard Link's avatar
Bernhard Link committed
1
/*  This file is part of "reprepro"
2
 *  Copyright (C) 2003,2004 Bernhard R. Link
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
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
#include <config.h>

#include <errno.h>
#include <assert.h>
#include <stdio.h>
#include <stdarg.h>
#include <unistd.h>
#include <malloc.h>
#include <string.h>
#include <ctype.h>
#include <zlib.h>
#include <db.h>
#include "error.h"
#include "mprintf.h"
31
#include "strlist.h"
32
#include "dirs.h"
33 34
#include "names.h"
#include "chunks.h"
35
#include "signature.h"
36
#include "aptmethod.h"
37
#include "updates.h"
38
#include "upgradelist.h"
39
#include "distribution.h"
40
#include "terms.h"
41 42 43

// TODO: what about other signatures? Is hard-coding ".gpg" sensible?

44 45
extern int verbose;

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
/* The data structures of this one: ("u_" is short for "update_")

updates_getpatterns read a list of patterns from <confdir>/updates:

   u_pattern --> u_pattern --> u_pattern --> NULL
       / \           / \          / \ / \
        |             \            |   |
	 \             ----\       |   |
	  ------------     |       |   |
	               \   .       |   .
                        |          |
updates_getupstreams instances them for a given distribution:
                        |          |
   distribution --> u_origin -> u_origin --> NULL
      |   |          / \ / \    / \ / \
      |  \ /          |   |      |   |
      | u_target -> u_index -> u_index -> NULL
      |   |              |        |
      |  \ /             |        |
      | u_target -> u_index -> u_index -> NULL
      |   |
      |  \ /                     
      |  NULL              .           .
     \ /                   |           |
   distribution ---> u_origin -> u_origin -> NULL
      |   |            / \          / \
      |  \ /            |            |
      | u_target --> u_index ---> u_index -> NULL
      |   |
      |  \ /
      |  NULL
      |
     \ /
     NULL

*/

/* the data for some upstream part to get updates from, some
 * some fields can be NULL or empty */
struct update_pattern {
	struct update_pattern *next;
87 88 89 90
	//e.g. "Name: woody"
	char *name;
	//e.g. "Method: ftp://ftp.uni-freiburg.de/pub/linux/debian"
	char *method;
91 92
	//e.g. "Config: Dir=/"
	char *config;
93 94 95 96 97 98 99
	//e.g. "Suite: woody" or "Suite: <asterix>/updates" (NULL means "*")
	char *suite_from;
	//e.g. "IgnoreRelease: Yes" for 1 (default is 0)
	int ignorerelease;
	//e.g. "ReleaseCheck: B629A24C38C6029A" (NULL means not check)
	char *verifyrelease;
	//e.g. "Architectures: i386 sparc mips" (empty means all)
100 101
	struct strlist architectures_from;
	struct strlist architectures_into;
102 103 104 105
	//e.g. "Components: main>main non-free>non-free contrib>contrib"
	// (empty means all)
	struct strlist components_from;
	struct strlist components_into;
106 107 108 109
	//e.g. "UDebComponents: main>main"
	// (empty means all)
	struct strlist udebcomponents_from;
	struct strlist udebcomponents_into;
110 111
	// NULL means no condition
	term *includecondition;
112 113 114 115 116 117
};

struct update_origin {
	struct update_origin *next;
	const struct update_pattern *pattern;
	char *suite_from;
118
	const struct distribution *distribution;
119 120
	char *releasefile,*releasegpgfile;
	// is set when fetching packages..
121
	struct aptmethod *download;
122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
	struct strlist checksums;
};

struct update_index {
	struct update_index *next;
	struct update_origin *origin;
	char *filename;
	char *upstream;
};

struct update_target {
	struct update_target *next;
	struct update_index *indices;
	struct target *target;
	struct upgradelist *upgradelist;
137 138
};

139
void update_pattern_free(struct update_pattern *update) {
140 141 142
	if( update == NULL )
		return;
	free(update->name);
143
	free(update->config);
144 145 146
	free(update->method);
	free(update->suite_from);
	free(update->verifyrelease);
147 148
	strlist_done(&update->architectures_from);
	strlist_done(&update->architectures_into);
149 150
	strlist_done(&update->components_from);
	strlist_done(&update->components_into);
151 152
	strlist_done(&update->udebcomponents_from);
	strlist_done(&update->udebcomponents_into);
153
	term_free(update->includecondition);
154 155 156
	free(update);
}

157 158 159
void updates_freepatterns(struct update_pattern *p) {
	while( p ) {
		struct update_pattern *pattern;
160

161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176
		pattern = p;
		p = pattern->next;
		update_pattern_free(pattern);
	}
}
void updates_freeorigins(struct update_origin *o) {
	while( o ) {
		struct update_origin *origin;

		origin = o;
		o = origin->next;
		free(origin->suite_from);
		free(origin->releasefile);
		free(origin->releasegpgfile);
		strlist_done(&origin->checksums);
		free(origin);
177 178
	}
}
179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200
void updates_freetargets(struct update_target *t) {
	while( t ) {
		struct update_target *ut;

		ut = t;
		t = ut->next;
		free(ut);
	}
}
static inline retvalue newupdatetarget(struct update_target **ts,struct target *target) {
	struct update_target *ut;

	ut = malloc(sizeof(struct update_target));
	if( ut == NULL )
		return RET_ERROR_OOM;
	ut->target = target;
	ut->next = *ts;
	ut->indices = NULL;
	ut->upgradelist = NULL;
	*ts = ut;
	return RET_OK;
}
201

202 203 204
static retvalue splitlist(struct strlist *from,
				struct strlist *into,
				const struct strlist *list) {
205
	retvalue r;
206
	int i;
207

208
	r = strlist_init(from);
209 210 211
	if( RET_WAS_ERROR(r) ) {
		return r;
	}
212
	r = strlist_init(into);
213
	if( RET_WAS_ERROR(r) ) {
214
		strlist_done(from);
215 216 217
		return r;
	}

218 219
	/* * Iterator over components to update * */
	r = RET_NOTHING;
220 221 222 223 224 225 226 227 228 229 230 231 232 233 234
	for( i = 0 ; i < list->count ; i++ ) {
		const char *item,*seperator;
		char *origin,*destination;

		item = list->values[i];
		// TODO: isn't this broken for the !*(dest+1) case ?
		if( !(seperator = strchr(item,'>')) ) {
			destination = strdup(item);
			origin = strdup(item);
		} else if( seperator == item ) {
			destination = strdup(seperator+1);
			origin = strdup(seperator+1);
		} else if( *(seperator+1) == '\0' ) {
			destination = strndup(item,seperator-item);
			origin = strndup(item,seperator-item);
235
		} else {
236 237
			origin = strndup(item,seperator-item);
			destination = strdup(seperator+1);
238 239 240
		}
		if( !origin || ! destination ) {
			free(origin);free(destination);
241 242
			strlist_done(from);
			strlist_done(into);
243 244
			return RET_ERROR_OOM;
		}
245
		r = strlist_add(from,origin);
246 247
		if( RET_WAS_ERROR(r) ) {
			free(destination);
248 249
			strlist_done(from);
			strlist_done(into);
250
			return r;
251
		}
252
		r = strlist_add(into,destination);
253
		if( RET_WAS_ERROR(r) ) {
254 255
			strlist_done(from);
			strlist_done(into);
256
			return r;
257
		}
258 259 260 261 262
		r = RET_OK;
	}
	return r;
}

263 264
inline static retvalue parse_pattern(const char *chunk, struct update_pattern **pattern) {
	struct update_pattern *update;
265
	struct strlist componentslist,architectureslist;
266
	char *formula;
267 268
	retvalue r;

269
	update = calloc(1,sizeof(struct update_pattern));
270 271 272 273 274 275 276
	if( update == NULL )
		return RET_ERROR_OOM;
	r = chunk_getvalue(chunk,"Name",&update->name);
	if( r == RET_NOTHING ) {
		fprintf(stderr,"Unexpected chunk in updates-file: '%s'.\n",chunk);
		return RET_ERROR;
	}
277
	if( RET_WAS_ERROR(r) ) {
278
		free(update);
279
		return r;
280 281 282 283 284 285 286 287 288 289 290 291
	}
	if( verbose > 30 ) {
		fprintf(stderr,"parsing update-chunk '%s'\n",update->name);
	}

	/* * Look where we are getting it from: * */

	r = chunk_getvalue(chunk,"Method",&update->method);
	if( !RET_IS_OK(r) ) {
		if( r == RET_NOTHING ) {
			fprintf(stderr,"No Method found in update-block!\n");
			r = RET_ERROR_MISSING;
292
		}
293
		update_pattern_free(update);
294
		return r;
295 296
	}

297 298 299
	/* * Is there config for the method? * */
	r = chunk_getwholedata(chunk,"Config",&update->config);
	if( RET_WAS_ERROR(r) ) {
300
		update_pattern_free(update);
301 302 303 304 305
		return r;
	}
	if( r == RET_NOTHING )
		update->config = NULL;

306 307
	/* * Check which suite to update from * */
	r = chunk_getvalue(chunk,"Suite",&update->suite_from);
308
	if( RET_WAS_ERROR(r) ) {
309
		update_pattern_free(update);
310 311
		return r;
	}
312 313 314 315
	if( r == RET_NOTHING )
		update->suite_from = NULL;

	/* * Check which architectures to update from * */
316
	r = chunk_getwordlist(chunk,"Architectures",&architectureslist);
317
	if( RET_WAS_ERROR(r) ) {
318
		update_pattern_free(update);
319
		return r;
320
	}
321
	if( r == RET_NOTHING ) {
322 323 324 325 326 327 328 329 330 331
		update->architectures_from.count = 0;
		update->architectures_into.count = 0;
	} else {
		r = splitlist(&update->architectures_from,
				&update->architectures_into,&architectureslist);
		strlist_done(&architectureslist);
		if( RET_WAS_ERROR(r) ) {
			update_pattern_free(update);
			return r;
		}
332
	}
333

334 335
	/* * Check which components to update from * */
	r = chunk_getwordlist(chunk,"Components",&componentslist);
336
	if( RET_WAS_ERROR(r) ) {
337
		update_pattern_free(update);
338 339
		return r;
	}
340 341 342 343
	if( r == RET_NOTHING ) {
		update->components_from.count = 0;
		update->components_into.count = 0;
	} else {
344
		r = splitlist(&update->components_from,
345 346 347
				&update->components_into,&componentslist);
		strlist_done(&componentslist);
		if( RET_WAS_ERROR(r) ) {
348
			update_pattern_free(update);
349 350 351
			return r;
		}
	}
352

353 354 355 356 357 358 359 360 361 362
	/* * Check which components to update udebs from * */
	r = chunk_getwordlist(chunk,"UDebComponents",&componentslist);
	if( RET_WAS_ERROR(r) ) {
		update_pattern_free(update);
		return r;
	}
	if( r == RET_NOTHING ) {
		update->udebcomponents_from.count = 0;
		update->udebcomponents_into.count = 0;
	} else {
363
		r = splitlist(&update->udebcomponents_from,
364 365 366 367 368 369 370 371 372
				&update->udebcomponents_into,&componentslist);
		strlist_done(&componentslist);
		if( RET_WAS_ERROR(r) ) {
			update_pattern_free(update);
			return r;
		}
	}


373
	/* * Check if we should get the Release-file * */
374
	r = chunk_gettruth(chunk,"IgnoreRelease");
375
	if( RET_WAS_ERROR(r) ) {
376
		update_pattern_free(update);
377 378 379
		return r;
	}
	update->ignorerelease = (r == RET_OK);
380

381
	/* * Check if we should get the Release.gpg file * */
382
	r = chunk_getvalue(chunk,"VerifyRelease",&update->verifyrelease);
383
	if( RET_WAS_ERROR(r) ) {
384
		update_pattern_free(update);
385
		return r;
386
	}
387 388 389
	if( r == RET_NOTHING )
		update->verifyrelease = NULL;

390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406
	/* * Check if there is a Include condition * */
	r = chunk_getvalue(chunk,"FilterFormula",&formula);
	if( RET_WAS_ERROR(r) ) {
		update_pattern_free(update);
		return r;
	}
	if( r != RET_NOTHING ) {
		r = term_compile(&update->includecondition,formula,
			T_OR|T_BRACKETS|T_NEGATION|T_VERSION|T_NOTEQUAL);
		free(formula);
		if( RET_WAS_ERROR(r) ) {
			update->includecondition = NULL;
			update_pattern_free(update);
			return r;
		}
		assert( r != RET_NOTHING );
	}
407

408
	*pattern = update;
409
	return RET_OK;
410
}
411

412

413 414
static retvalue instance_pattern(const char *listdir,
		const struct update_pattern *pattern,
415
		const struct distribution *distribution,
416
		struct update_origin **origins) {
417

418
	struct update_origin *update;
419

420
	update = calloc(1,sizeof(struct update_origin));
421 422
	if( update == NULL )
		return RET_ERROR_OOM;
423

424 425 426 427 428 429 430 431 432 433 434
	if( pattern->suite_from == NULL || strcmp(pattern->suite_from,"*")== 0)
		update->suite_from = strdup(distribution->codename);
	else {
		if( pattern->suite_from[0] == '*' &&
				pattern->suite_from[1] == '/' )
			update->suite_from = calc_dirconcat(distribution->codename,pattern->suite_from+2);
		else if( index(pattern->suite_from,'*') == NULL )
			update->suite_from = strdup(pattern->suite_from);
		else {
			//TODO: implement this...
			fprintf(stderr,"Unsupported pattern '%s'\n",pattern->suite_from);
435
			free(update);
436
			return RET_ERROR;
437
		}
438 439
	}
	if( update->suite_from == NULL ) {
440
		free(update);
441
		return RET_ERROR_OOM;
442
	}
443

444
	update->distribution = distribution;
445 446 447
	update->pattern = pattern;

	if( !pattern->ignorerelease ) {
448
		update->releasefile = calc_downloadedlistfile(listdir,distribution->codename,pattern->name,"Release","data","rel");
449 450 451 452 453
		if( update->releasefile == NULL ) {
			updates_freeorigins(update);
			return RET_ERROR_OOM;
		}
		if( pattern->verifyrelease ) {
454
			update->releasegpgfile = calc_downloadedlistfile(listdir,distribution->codename,pattern->name,"Release","gpg","rel");
455 456 457 458 459 460
			if( update->releasegpgfile == NULL ) {
				updates_freeorigins(update);
				return RET_ERROR_OOM;
			}
		}
	}
461
	
462
	*origins = update;
463
	return RET_OK;
464 465
}

466
static retvalue parsechunk(void *data,const char *chunk) {
467 468
	struct update_pattern *update;
	struct update_pattern **patterns = data;
469 470
	retvalue r;

471 472
	r = parse_pattern(chunk,&update);
	if( RET_IS_OK(r) ) {
473 474
		update->next = *patterns;
		*patterns = update;
475
	}
476 477
	return r;
}
478

479
retvalue updates_getpatterns(const char *confdir,struct update_pattern **patterns,int force) {
480
	char *updatesfile;
481
	struct update_pattern *update = NULL;
482
	retvalue r;
483

484 485 486 487 488 489 490
	updatesfile = calc_dirconcat(confdir,"updates");
	if( !updatesfile ) 
		return RET_ERROR_OOM;
	r = chunk_foreach(updatesfile,parsechunk,&update,force,0);
	free(updatesfile);
	if( RET_IS_OK(r) )
		*patterns = update;
491 492 493
	return r;
}

494 495
static retvalue getorigins(const char *listdir,const struct update_pattern *patterns,const struct distribution *distribution,struct update_origin **origins) {
	struct update_origin *updates = NULL;
496
	retvalue result;
497
	int i;
498

499
	result = RET_NOTHING;
500 501 502
	for( i = 0; i < distribution->updates.count ; i++ ) {
		const char *name = distribution->updates.values[i];
		const struct update_pattern *pattern;
503
		struct update_origin *update;
504
		retvalue r;
505

506 507 508 509 510 511 512 513 514 515
		for( pattern = patterns ; pattern ; pattern = pattern->next ) {
			if( strcmp(name,pattern->name) == 0 )
				break;
		}
		if( pattern == NULL ) {
			fprintf(stderr,"Cannot find definition of upgrade-rule '%s' for distribution '%s'!\n",name,distribution->codename);
			RET_UPDATE(result,RET_ERROR);
			break;
		}

516
		r = instance_pattern(listdir,pattern,distribution,&update);
517 518 519 520 521 522 523 524
		RET_UPDATE(result,r);
		if( RET_WAS_ERROR(r) )
			break;
		if( RET_IS_OK(r) ) {
			update->next = updates;
			updates = update;
		}
	}
525

526
	if( RET_WAS_ERROR(result) ) {
527
		updates_freeorigins(updates);
528
	} else {
529
		*origins = updates;
530
	}
531 532 533
	return result;
}

534 535 536
static inline retvalue newindex(struct update_index **indices,
		const char *listdir,
		struct update_origin *origin,struct target *target,
537
		const char *architecture_from,
538 539 540 541 542 543 544 545 546
		const char *component_from) {
	struct update_index *index;

	index = malloc(sizeof(struct update_index));
	if( index == NULL )
		return RET_ERROR_OOM;

	index->filename = calc_downloadedlistfile(listdir,
			target->codename,origin->pattern->name,
547 548
			component_from,architecture_from,
			target->suffix);
549 550 551 552 553
	if( index->filename == NULL ) {
		free(index);
		return RET_ERROR_OOM;
	}
	index->upstream = (*target->getupstreamindex)(target,
554
		origin->suite_from,component_from,architecture_from);
555 556 557 558 559 560 561 562 563 564 565
	if( index->upstream == NULL ) {
		free(index->filename);
		free(index);
		return RET_ERROR_OOM;
	}
	index->origin = origin;
	index->next = *indices;
	*indices = index;
	return RET_OK;
}

566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618

static retvalue addorigintotarget(const char *listdir,struct update_origin *origin,
		struct target *target,
		struct distribution *distribution,
		struct update_target *updatetargets ) {
	const struct update_pattern *p = origin->pattern;
	const struct strlist *c_from,*c_into;
	const struct strlist *a_from,*a_into;
	int ai,ci;
	retvalue r;

	if( p->architectures_into.count == 0 ) {
		a_from = &distribution->architectures;
		a_into = &distribution->architectures;
	} else {
		a_from = &p->architectures_from;
		a_into = &p->architectures_into;
	}
	if( strcmp(target->suffix,"udeb") == 0 )  {
		if( p->udebcomponents_from.count > 0 ) {
			c_from = &p->udebcomponents_from;
			c_into = &p->udebcomponents_into;
		} else {
			c_from = &distribution->udebcomponents;
			c_into = &distribution->udebcomponents;
		}
	} else {
		if( p->components_from.count > 0 ) {
			c_from = &p->components_from;
			c_into = &p->components_into;
		} else {
			c_from = &distribution->components;
			c_into = &distribution->components;
		}
	}

	for( ai = 0 ; ai < a_into->count ; ai++ ) {
		if( strcmp(a_into->values[ai],target->architecture) != 0 )
			continue;

		for( ci = 0 ; ci < c_into->count ; ci++ ) {
			if( strcmp(c_into->values[ci],target->component) != 0 )
				continue;

			r = newindex(&updatetargets->indices,listdir,origin,target,
					a_from->values[ai],c_from->values[ci]);
			if( RET_WAS_ERROR(r) )
				return r;
		}
	}
	return RET_OK;
}

619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634
static retvalue gettargets(const char *listdir,struct update_origin *origins,struct distribution *distribution,struct update_target **ts) {
	struct target *target;
	struct update_origin *origin;
	struct update_target *updatetargets;
	retvalue r;

	updatetargets = NULL;

	for( target = distribution->targets ; target ; target = target->next) {
		r = newupdatetarget(&updatetargets,target);
		if( RET_WAS_ERROR(r) ) {
			updates_freetargets(updatetargets);
			return r;
		}

		for( origin = origins ; origin ; origin=origin->next ) {
635 636 637 638
			r=addorigintotarget(listdir,origin,target,distribution,updatetargets);
			if( RET_WAS_ERROR(r) ) {
				updates_freetargets(updatetargets);
				return r;
639 640 641 642 643 644 645 646 647 648
			}
		}
	}

	*ts = updatetargets;

	return RET_OK;
}

static inline retvalue findmissingupdate(int count,const struct distribution *distribution,struct update_origin *updates) {
Bernhard Link's avatar
Bernhard Link committed
649 650 651 652 653 654 655 656 657
	retvalue result;

	result = RET_OK;

	if( count != distribution->updates.count ) {
		int i;

		for( i=0;i<distribution->updates.count;i++ ){
			const char *update = distribution->updates.values[i];
658
			struct update_origin *u;
Bernhard Link's avatar
Bernhard Link committed
659 660

			u = updates;
661
			while( u && strcmp(u->pattern->name,update) != 0 ) 
Bernhard Link's avatar
Bernhard Link committed
662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677
				u = u->next;
			if( u == NULL ) {
				fprintf(stderr,"Update '%s' is listed in distribution '%s', but was not found!\n",update,distribution->codename);
				result = RET_ERROR_MISSING;
				break;
			}
		}
		if( RET_IS_OK(result) ) {
			fprintf(stderr,"Did you write an update two times in the update-line of '%s'?\n",distribution->codename);
			result = RET_NOTHING;
		}
	}

	return result;
}

678
retvalue updates_getindices(const char *listdir,const struct update_pattern *patterns,struct distribution *distributions) {
679
	struct distribution *distribution;
680

681
	for( distribution = distributions ; distribution ; distribution = distribution->next ) {
682 683
		struct update_origin *update;
		struct update_target *targets;
684
		retvalue r;
685

686
		r = getorigins(listdir,patterns,distribution,&update);
687

688
		if( RET_WAS_ERROR(r) )
689
			return r;
690
		if( RET_IS_OK(r) ) {
691
			struct update_origin *last;
Bernhard Link's avatar
Bernhard Link committed
692
			int count;
693 694 695

			assert(update);
			last = update;
Bernhard Link's avatar
Bernhard Link committed
696 697
			count = 1;
			while( last->next ) {
698
				last = last->next;
Bernhard Link's avatar
Bernhard Link committed
699 700 701 702 703
				count++;
			}
			/* Check if we got all: */
			r = findmissingupdate(count,distribution,update);
			if( RET_WAS_ERROR(r) ) {
704 705
				updates_freeorigins(update);
				return r;
Bernhard Link's avatar
Bernhard Link committed
706 707
			}

708 709 710 711 712 713 714
			r = gettargets(listdir,update,distribution,&targets);
			if( RET_WAS_ERROR(r) ) {
				updates_freeorigins(update);
				return r;
			}
			distribution->updateorigins = update;
			distribution->updatetargets = targets;
715 716
		}
	}
717
	return RET_OK;
718 719 720
}

/******************* Fetch all Lists for an update **********************/
721 722
static inline retvalue prepareorigin(struct aptmethodrun *run,struct update_origin *origin,struct distribution *distribution) {
	char *toget;
723 724
	struct aptmethod *method;
	retvalue r;
725
	const struct update_pattern *p = origin->pattern;
726

727
	r = aptmethod_newmethod(run,p->method,p->config,&method);
728 729 730
	if( RET_WAS_ERROR(r) ) {
		return r;
	}
731 732 733 734 735 736 737 738 739 740
	origin->download = method;

	if( p->ignorerelease )
		return RET_NOTHING;

	toget = mprintf("dists/%s/Release",origin->suite_from);
	r = aptmethod_queueindexfile(method,toget,origin->releasefile);
	free(toget);
	if( RET_WAS_ERROR(r) )
		return r;
741

742 743 744 745
	if( p->verifyrelease != NULL ) {
		toget = mprintf("dists/%s/Release.gpg",origin->suite_from);
		r = aptmethod_queueindexfile(method,toget,origin->releasegpgfile);
		free(toget);
746 747
		if( RET_WAS_ERROR(r) )
			return r;
748 749 750
	}
	return RET_OK;
}
751

752 753 754 755 756 757 758 759 760 761 762 763 764 765
static inline retvalue readchecksums(struct update_origin *origin) {
	retvalue r;

	if( origin->releasefile == NULL )
		return RET_NOTHING;

	/* if there is nothing said, then there is nothing to check... */
	if( origin->releasegpgfile != NULL ) {

		r = signature_check(origin->pattern->verifyrelease,
				origin->releasegpgfile,
				origin->releasefile);
		if( RET_WAS_ERROR(r) ) {
			return r;
766
		}
767
	}
768 769 770 771
	r = release_getchecksums(origin->releasefile,&origin->checksums);
	assert( r != RET_NOTHING );
	return r;
}
772

773 774 775 776
static inline retvalue queueindex(struct update_index *index,int force) {
	const struct update_origin *origin = index->origin;
	int i;
	size_t l;
777

778 779 780
	if( origin->releasefile == NULL )
		return aptmethod_queueindexfile(origin->download,
			index->upstream,index->filename);
781

782 783
	//TODO: this is a very crude hack, think of something better...
	l = 7 + strlen(origin->suite_from); // == strlen("dists/%s/")
784

785 786 787 788
	for( i = 0 ; i+1 < origin->checksums.count ; i+=2 ) {

		assert( strlen(index->upstream) > l );
		if( strcmp(index->upstream+l,origin->checksums.values[i]) == 0 ){
789

790 791 792 793 794
			return aptmethod_queuefile(origin->download,
				index->upstream,index->filename,
				origin->checksums.values[i+1],NULL,NULL);
		}
		
795
	}
796 797 798 799 800 801
	fprintf(stderr,"Could not find '%s' within the Releasefile of '%s':\n'%s'\n",index->upstream,origin->pattern->name,origin->releasefile);
	if( force > 2 ) {
		aptmethod_queueindexfile(origin->download,
				index->upstream,index->filename);
	}
	return RET_ERROR_WRONG_MD5;
802
}
803

804 805


806
static retvalue updates_prepare(struct aptmethodrun *run,struct distribution *distribution) {
807
	retvalue result,r;
808
	struct update_origin *origin;
809 810

	result = RET_NOTHING;
811 812
	for( origin=distribution->updateorigins;origin; origin=origin->next ) {
		r = prepareorigin(run,origin,distribution);
813 814
		RET_UPDATE(result,r);
		if( RET_WAS_ERROR(r) )
815
			return r;
816 817 818 819
	}
	return result;
}

820
static retvalue updates_queuelists(struct aptmethodrun *run,struct distribution *distribution,int force) {
821
	retvalue result,r;
822 823 824
	struct update_origin *origin;
	struct update_target *target;
	struct update_index *index;
825

826 827 828
	result = RET_NOTHING;
	for( origin=distribution->updateorigins;origin; origin=origin->next ) {
		r = readchecksums(origin);
829
		RET_UPDATE(result,r);
830 831 832 833 834 835
		if( RET_WAS_ERROR(r) && !force )
			return r;
	}
	for( target=distribution->updatetargets;target; target=target->next ) {
		for( index=target->indices ; index; index=index->next ) {
			r = queueindex(index,force);
836
			RET_UPDATE(result,r);
837 838
			if( RET_WAS_ERROR(r) && ! force )
				return r;
839 840
		}
	}
841
	return RET_OK;;
842 843
}

844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902

upgrade_decision ud_decide_by_pattern(void *privdata, const char *package,const char *old_version,const char *new_version,const char *newcontrolchunk) {
	struct update_pattern *pattern = privdata;

	if( pattern->includecondition ) {
		struct term_atom *atom = pattern->includecondition;
		while( atom ) {
			int correct;char *value;
			enum term_comparison c = atom->comparison;
			retvalue r;

			r = chunk_getvalue(newcontrolchunk,atom->key,&value);
			// gna..., why is there no way to report errors?
			// TODO: fix this insanity...
			if( RET_WAS_ERROR(r) )
				r = RET_NOTHING;
			if( r == RET_NOTHING ) {
//				fprintf(stderr,"not found %s\n",atom->key);
				correct = ( c != tc_notequal );
			} else if( c == tc_none) {
				correct = 1;
				free(value);
			} else {
				int i;
//				fprintf(stderr,"found %s as '%s' (will compare with '%s')\n",atom->key,value,atom->comparewith);
				i = strcmp(value,atom->comparewith);
				free(value);
				if( i < 0 ) 
					correct = c == tc_strictless
					     || c == tc_lessorequal
					     || c == tc_notequal;
				else if( i > 0 ) 
					correct = c == tc_strictmore
					     || c == tc_moreorequal
					     || c == tc_notequal;
				else 
					correct = c == tc_lessorequal
					     || c == tc_moreorequal
					     || c == tc_equal;
			}
			if( atom->negated )
				correct = !correct;
			if( correct ) {
				atom = atom->nextiftrue;
			} else {
				atom = atom->nextiffalse;
				if( atom == NULL) {
					// fprintf(stderr,"Rejecting %s\n",package);
					return UD_NO;
				}
			}

		}
	}
	// fprintf(stderr,"Accepting %s\n",package);

	return UD_UPGRADE;
}

903
static inline retvalue searchformissing(const char *dbdir,struct downloadcache *cache,filesdb filesdb,struct update_target *u,upgrade_decide_function *decide,void *decision_data,int force) {
904 905
	struct update_index *index;
	retvalue result,r;
906

907 908
	if( verbose > 2 )
		fprintf(stderr,"  processing updates for '%s'\n",u->target->identifier);
909
	r = upgradelist_initialize(&u->upgradelist,u->target,dbdir,decide,decision_data);
910 911
	if( RET_WAS_ERROR(r) )
		return r;
912

913
	result = RET_NOTHING;
Bernhard Link's avatar
Bernhard Link committed
914

915
	for( index=u->indices ; index ; index=index->next ) {
Bernhard Link's avatar
Bernhard Link committed
916

917 918
		if( verbose > 4 )
			fprintf(stderr,"  reading '%s'\n",index->filename);
919 920
		assert(index->origin->download);
		r = upgradelist_update(u->upgradelist,
921
				index->origin->download,index->filename,
922 923
				ud_decide_by_pattern,
				(void*)index->origin->pattern,
924
				force);
925 926 927
		RET_UPDATE(result,r);
		if( RET_WAS_ERROR(r) && !force)
			return result;
928
	}
929 930 931 932 933
	//TODO: when upgradelist supports removing unavail packages,
	//do not forget to disable this here in case of error and force...
	
	r = upgradelist_enqueue(u->upgradelist,cache,filesdb,force);
	RET_UPDATE(result,r);
934

935 936
	return result;
}
937

938
static retvalue updates_readindices(const char *dbdir,struct downloadcache *cache,filesdb filesdb,struct distribution *distribution,int force) {
939 940
	retvalue result,r;
	struct update_target *u;
941

942 943
	result = RET_NOTHING;
	for( u=distribution->updatetargets ; u ; u=u->next ) {
944
		r = searchformissing(dbdir,cache,filesdb,u,ud_always,NULL,force);
945 946 947 948 949
		RET_UPDATE(result,r);
		if( RET_WAS_ERROR(r) && !force )
			break;
	}
	return result;
950
}
951

952

953
static retvalue updates_install(const char *dbdir,filesdb filesdb,DB *refsdb,struct distribution *distribution,int force) {
954
	retvalue result,r;
955
	struct update_target *u;
956 957

	result = RET_NOTHING;
958
	for( u=distribution->updatetargets ; u ; u=u->next ) {
959
		r = upgradelist_install(u->upgradelist,dbdir,filesdb,refsdb,force);
960
		RET_UPDATE(result,r);
961 962
		upgradelist_free(u->upgradelist);
		u->upgradelist = NULL;
963 964 965 966 967
		if( RET_WAS_ERROR(r) && !force )
			break;
	}
	return result;
}
968

969 970
retvalue updates_update(const char *dbdir,const char *listdir,const char *methoddir,filesdb filesdb,DB *refsdb,struct distribution *distributions,int force) {
	struct distribution *distribution;
971
	retvalue result,r;
972 973 974 975 976 977
	struct aptmethodrun *run;
	struct downloadcache *cache;

	r = aptmethod_initialize_run(&run);
	if( RET_WAS_ERROR(r) )
		return r;
978 979

	result = RET_NOTHING;
980 981
	/* first get all "Release" and "Release.gpg" files */
	for( distribution=distributions ; distribution ; distribution=distribution->next) {
982 983 984 985
		if( distribution->override || distribution->srcoverride ) {
			if( verbose >= 0 )
				fprintf(stderr,"Warning: Override-Files of '%s' ignored as not yet supported while updating!\n",distribution->codename);
		}
986 987 988 989 990 991 992 993 994
		r = updates_prepare(run,distribution);
		RET_UPDATE(result,r);
		if( RET_WAS_ERROR(r) && ! force )
			break;
	}
	if( RET_WAS_ERROR(result) && !force ) {
		aptmethod_shutdown(run);
		return result;
	}
995

996 997
	r = aptmethod_download(run,methoddir,filesdb);
	if( RET_WAS_ERROR(r) && !force ) {
998
		RET_UPDATE(result,r);
999
		aptmethod_shutdown(run);
1000
		return result;
1001
	}
1002

1003 1004 1005 1006 1007 1008 1009 1010
	/* Then get all index files (with perhaps md5sums from the above) */
	for( distribution=distributions ; distribution ; distribution=distribution->next) {
		r = updates_queuelists(run,distribution,force);
		RET_UPDATE(result,r);
		if( RET_WAS_ERROR(r) && ! force )
			break;
	}
	if( RET_WAS_ERROR(result) && !force ) {
1011
		RET_UPDATE(result,r);
1012 1013
		aptmethod_shutdown(run);
		return result;
1014 1015
	}

1016 1017
	r = aptmethod_download(run,methoddir,filesdb);
	if( RET_WAS_ERROR(r) && !force ) {
1018
		RET_UPDATE(result,r);
1019 1020 1021
		aptmethod_shutdown(run);
		return result;
	}
1022

1023
	/* Then get all packages */
1024 1025
	if( verbose >= 0 )
		fprintf(stderr,"Calculating packages to get...\n");
1026 1027 1028
	r = downloadcache_initialize(&cache);
	if( !RET_IS_OK(r) ) {
		aptmethod_shutdown(run);
1029
		RET_UPDATE(result,r);
1030 1031 1032 1033 1034 1035 1036
		return result;
	}
	
	for( distribution=distributions ; distribution ; distribution=distribution->next) {
		r = updates_readindices(dbdir,cache,filesdb,distribution,force);
		RET_UPDATE(result,r);
		if( RET_WAS_ERROR(r) && ! force )
1037 1038
			break;
	}
1039 1040
	if( verbose >= 0 )
		fprintf(stderr,"Getting packages...\n");
1041 1042
	r = aptmethod_download(run,methoddir,filesdb);
	RET_UPDATE(result,r);
1043 1044
	if( verbose > 0 )
		fprintf(stderr,"Freeing some memory...\n");
1045 1046
	r = downloadcache_free(cache);
	RET_UPDATE(result,r);
1047 1048
	if( verbose > 0 )
		fprintf(stderr,"Shutting down aptmethods...\n");
1049 1050 1051 1052 1053 1054
	r = aptmethod_shutdown(run);
	RET_UPDATE(result,r);

	if( RET_WAS_ERROR(result) && !force ) {
		return result;
	}
1055 1056
	if( verbose >= 0 )
		fprintf(stderr,"Installing packages...\n");
1057 1058

	for( distribution=distributions ; distribution ; distribution=distribution->next) {
1059
		r = updates_install(dbdir,filesdb,refsdb,distribution,force);
1060 1061 1062 1063 1064
		RET_UPDATE(result,r);
		if( RET_WAS_ERROR(r) && ! force )
			break;
	}

1065 1066
	return result;
}