1 /** \ingroup header
2  * \file lib/formats.c
3  */
4 
5 #include "system.h"
6 
7 #include <rpm/rpmtypes.h>
8 #include <rpm/rpmlib.h>
9 #include <rpm/rpmmacro.h>	/* XXX for %_i18ndomains */
10 #include <rpm/rpmfi.h>
11 #include <rpm/rpmstring.h>
12 #include <rpm/rpmlog.h>
13 #include "lib/misc.h"		/* tag function proto */
14 
15 #include "debug.h"
16 
17 struct headerTagFunc_s {
18     rpmTag tag;		/*!< Tag of extension. */
19     headerTagTagFunction func;	/*!< Pointer to formatter function. */
20 };
21 
22 /** \ingroup rpmfi
23  * Retrieve file names from header.
24  *
25  * The representation of file names in package headers changed in rpm-4.0.
26  * Originally, file names were stored as an array of absolute paths.
27  * In rpm-4.0, file names are stored as separate arrays of dirname's and
28  * basename's, * with a dirname index to associate the correct dirname
29  * with each basname.
30  *
31  * This function is used to retrieve file names independent of how the
32  * file names are represented in the package header.
33  *
34  * @param h		header
35  * @param tagN		RPMTAG_BASENAMES | PMTAG_ORIGBASENAMES
36  * @param withstate	take file state into account?
37  * @retval td		tag data container
38  * @return		1 on success
39  */
fnTag(Header h,rpmTag tagN,int withstate,rpmtd td)40 static int fnTag(Header h, rpmTag tagN, int withstate, rpmtd td)
41 {
42     const char **baseNames, **dirNames;
43     const char *fileStates = NULL;
44     uint32_t *dirIndexes;
45     rpm_count_t count, retcount, dncount;
46     size_t size = 0;
47     rpmTag dirNameTag = RPMTAG_DIRNAMES;
48     rpmTag dirIndexesTag = RPMTAG_DIRINDEXES;
49     int i, j;
50     int rc = 0; /* assume failure */
51     struct rpmtd_s bnames, dnames, dixs, fstates;
52 
53     if (tagN == RPMTAG_ORIGBASENAMES) {
54 	dirNameTag = RPMTAG_ORIGDIRNAMES;
55 	dirIndexesTag = RPMTAG_ORIGDIRINDEXES;
56     }
57 
58     if (!headerGet(h, tagN, &bnames, HEADERGET_MINMEM)) {
59 	return 0;		/* no file list */
60     }
61 
62     (void) headerGet(h, dirNameTag, &dnames, HEADERGET_MINMEM);
63     (void) headerGet(h, dirIndexesTag, &dixs, HEADERGET_MINMEM);
64 
65     retcount = count = rpmtdCount(&bnames);
66     dncount = rpmtdCount(&dnames);
67 
68     /* Basic sanity checking for our interrelated tags  */
69     if (rpmtdCount(&dixs) != count || dncount < 1 || dncount > count)
70 	td->flags |= RPMTD_INVALID;
71 
72     if (withstate) {
73 	/* no recorded states means no installed files */
74 	if (!headerGet(h, RPMTAG_FILESTATES, &fstates, HEADERGET_MINMEM))
75 	    goto exit;
76 	if (rpmtdCount(&fstates) != count)
77 	    td->flags |= RPMTD_INVALID;
78 	fileStates = fstates.data;
79     }
80 
81     if (td->flags & RPMTD_INVALID)
82 	goto exit;
83 
84     baseNames = bnames.data;
85     dirNames = dnames.data;
86     dirIndexes = dixs.data;
87 
88     /*
89      * fsm, psm and rpmfi assume the data is stored in a single allocation
90      * block, until those assumptions are removed we need to jump through
91      * a few hoops here and precalculate sizes etc
92      */
93     for (i = 0; i < count; i++) {
94 	if (fileStates && !RPMFILE_IS_INSTALLED(fileStates[i])) {
95 	    retcount--;
96 	    continue;
97 	}
98 	/* Sanity check  directory indexes are within bounds */
99 	if (dirIndexes[i] >= dncount) {
100 	    td->flags |= RPMTD_INVALID;
101 	    break;
102 	}
103 	size += strlen(baseNames[i]) + strlen(dirNames[dirIndexes[i]]) + 1;
104     }
105 
106     if (!(td->flags & RPMTD_INVALID)) {
107 	char **fileNames = xmalloc(size + (sizeof(*fileNames) * retcount));
108 	char *t = ((char *) fileNames) + (sizeof(*fileNames) * retcount);
109 	for (i = 0, j = 0; i < count; i++) {
110 	    if (fileStates && !RPMFILE_IS_INSTALLED(fileStates[i]))
111 		continue;
112 	    fileNames[j++] = t;
113 	    t = stpcpy( stpcpy(t, dirNames[dirIndexes[i]]), baseNames[i]);
114 	    *t++ = '\0';
115 	}
116 
117 	td->data = fileNames;
118 	td->count = retcount;
119 	td->type = RPM_STRING_ARRAY_TYPE;
120 	td->flags |= RPMTD_ALLOCED;
121 	rc = 1;
122     }
123 
124 exit:
125     rpmtdFreeData(&bnames);
126     rpmtdFreeData(&dnames);
127     rpmtdFreeData(&dixs);
128     /* only safe if the headerGet() on file states was actually called */
129     if (fileStates)
130 	rpmtdFreeData(&fstates);
131 
132     return rc;
133 }
134 
filedepTag(Header h,rpmTag tagN,rpmtd td,headerGetFlags hgflags)135 static int filedepTag(Header h, rpmTag tagN, rpmtd td, headerGetFlags hgflags)
136 {
137     rpmfi fi = rpmfiNew(NULL, h, RPMTAG_BASENAMES, RPMFI_NOHEADER);
138     rpmds ds = NULL;
139     char **fdeps = NULL;
140     int numfiles;
141     char deptype = 'R';
142     int fileix;
143     int rc = 0;
144 
145     numfiles = rpmfiFC(fi);
146     if (numfiles <= 0) {
147 	goto exit;
148     }
149 
150     if (tagN == RPMTAG_PROVIDENAME)
151 	deptype = 'P';
152     else if (tagN == RPMTAG_REQUIRENAME)
153 	deptype = 'R';
154 
155     ds = rpmdsNew(h, tagN, 0);
156     fdeps = xmalloc(numfiles * sizeof(*fdeps));
157 
158     while ((fileix = rpmfiNext(fi)) >= 0) {
159 	ARGV_t deps = NULL;
160 	const uint32_t * ddict = NULL;
161 	int ndx = rpmfiFDepends(fi, &ddict);
162 	if (ddict != NULL) {
163 	    while (ndx-- > 0) {
164 		const char * DNEVR;
165 		unsigned dix = *ddict++;
166 		char mydt = ((dix >> 24) & 0xff);
167 		if (mydt != deptype)
168 		    continue;
169 		dix &= 0x00ffffff;
170 		(void) rpmdsSetIx(ds, dix-1);
171 		if (rpmdsNext(ds) < 0)
172 		    continue;
173 		DNEVR = rpmdsDNEVR(ds);
174 		if (DNEVR != NULL) {
175 		    argvAdd(&deps, DNEVR + 2);
176 		}
177 	    }
178 	}
179 	fdeps[fileix] = deps ? argvJoin(deps, " ") : xstrdup("");
180 	argvFree(deps);
181     }
182     td->data = fdeps;
183     td->count = numfiles;
184     td->flags = RPMTD_ALLOCED | RPMTD_PTR_ALLOCED;
185     td->type = RPM_STRING_ARRAY_TYPE;
186     rc = 1;
187 
188 exit:
189     rpmfiFree(fi);
190     rpmdsFree(ds);
191     return rc;
192 }
193 
194 typedef enum tMode_e {
195     NORMALTRIGGER     = 0,
196     FILETRIGGER       = 1,
197     TRANSFILETRIGGER  = 2,
198 } tMode;
199 
200 /**
201  * Retrieve trigger info.
202  * @param mode		type of trigger (see tMode_e)
203  * @param h		header
204  * @retval td		tag data container
205  * @param hgflags	header get flags
206  * @return		1 on success
207  */
triggercondsTagFor(tMode mode,Header h,rpmtd td,headerGetFlags hgflags)208 static int triggercondsTagFor(tMode mode, Header h, rpmtd td,
209 				headerGetFlags hgflags)
210 {
211     uint32_t * indices;
212     int i, j;
213     char ** conds;
214     struct rpmtd_s nametd, indextd, flagtd, versiontd, scripttd;
215     int hgeflags = HEADERGET_MINMEM;
216     rpmTagVal triggername, triggerindex, triggerflags;
217     rpmTagVal triggerversion, triggerscripts;
218 
219     switch (mode) {
220 	case NORMALTRIGGER:
221 	    triggername = RPMTAG_TRIGGERNAME;
222 	    triggerindex = RPMTAG_TRIGGERINDEX;
223 	    triggerflags = RPMTAG_TRIGGERFLAGS;
224 	    triggerversion = RPMTAG_TRIGGERVERSION;
225 	    triggerscripts = RPMTAG_TRIGGERSCRIPTS;
226 	    break;
227 	case FILETRIGGER:
228 	    triggername = RPMTAG_FILETRIGGERNAME;
229 	    triggerindex = RPMTAG_FILETRIGGERINDEX;
230 	    triggerflags = RPMTAG_FILETRIGGERFLAGS;
231 	    triggerversion = RPMTAG_FILETRIGGERVERSION;
232 	    triggerscripts = RPMTAG_FILETRIGGERSCRIPTS;
233 	    break;
234 	case TRANSFILETRIGGER:
235 	    triggername = RPMTAG_TRANSFILETRIGGERNAME;
236 	    triggerindex = RPMTAG_TRANSFILETRIGGERINDEX;
237 	    triggerflags = RPMTAG_TRANSFILETRIGGERFLAGS;
238 	    triggerversion = RPMTAG_TRANSFILETRIGGERVERSION;
239 	    triggerscripts = RPMTAG_TRANSFILETRIGGERSCRIPTS;
240 	    break;
241 	default:
242 	    return 0;
243     }
244 
245     if (!headerGet(h, triggername, &nametd, hgeflags)) {
246 	return 0;
247     }
248 
249     headerGet(h, triggerindex, &indextd, hgeflags);
250     headerGet(h, triggerflags, &flagtd, hgeflags);
251     headerGet(h, triggerversion, &versiontd, hgeflags);
252     headerGet(h, triggerscripts, &scripttd, hgeflags);
253 
254     td->type = RPM_STRING_ARRAY_TYPE;
255     td->flags = RPMTD_ALLOCED | RPMTD_PTR_ALLOCED;
256     td->data = conds = xmalloc(sizeof(*conds) * rpmtdCount(&scripttd));
257     td->count = rpmtdCount(&scripttd);
258 
259     indices = indextd.data;
260 
261     while ((i = rpmtdNext(&scripttd)) >= 0) {
262 	rpm_flag_t *flag;
263 	char *flagStr, *item;
264 	ARGV_t items = NULL;
265 
266 	rpmtdInit(&nametd); rpmtdInit(&flagtd); rpmtdInit(&versiontd);
267 	while ((j = rpmtdNext(&nametd)) >= 0) {
268 	    /* flag and version arrays match name array size always */
269 	    rpmtdNext(&flagtd); rpmtdNext(&versiontd);
270 
271 	    if (indices[j] != i)
272 		continue;
273 
274 	    flag = rpmtdGetUint32(&flagtd);
275 	    if (flag && *flag & RPMSENSE_SENSEMASK) {
276 		flagStr = rpmtdFormat(&flagtd, RPMTD_FORMAT_DEPFLAGS, NULL);
277 		rasprintf(&item, "%s %s %s", rpmtdGetString(&nametd),
278 					     flagStr,
279 					     rpmtdGetString(&versiontd));
280 		free(flagStr);
281 	    } else {
282 		item = xstrdup(rpmtdGetString(&nametd));
283 	    }
284 
285 	    argvAdd(&items, item);
286 	    free(item);
287 	}
288 
289 	conds[i] = argvJoin(items, ", ");
290 	argvFree(items);
291     }
292 
293     rpmtdFreeData(&nametd);
294     rpmtdFreeData(&versiontd);
295     rpmtdFreeData(&flagtd);
296     rpmtdFreeData(&indextd);
297     rpmtdFreeData(&scripttd);
298     return 1;
299 }
300 
triggercondsTag(Header h,rpmtd td,headerGetFlags hgflags)301 static int triggercondsTag(Header h, rpmtd td, headerGetFlags hgflags)
302 {
303     return triggercondsTagFor(NORMALTRIGGER, h, td, hgflags);
304 }
305 
filetriggercondsTag(Header h,rpmtd td,headerGetFlags hgflags)306 static int filetriggercondsTag(Header h, rpmtd td, headerGetFlags hgflags)
307 {
308     return triggercondsTagFor(FILETRIGGER, h, td, hgflags);
309 }
310 
transfiletriggercondsTag(Header h,rpmtd td,headerGetFlags hgflags)311 static int transfiletriggercondsTag(Header h, rpmtd td, headerGetFlags hgflags)
312 {
313     return triggercondsTagFor(TRANSFILETRIGGER, h, td, hgflags);
314 }
315 
316 /**
317  * Retrieve trigger type info.
318  * @param mode		type of trigger (see tMode_e)
319  * @param h		header
320  * @retval td		tag data container
321  * @param hgflags	header get flags
322  * @return		1 on success
323  */
triggertypeTagFor(tMode mode,Header h,rpmtd td,headerGetFlags hgflags)324 static int triggertypeTagFor(tMode mode, Header h, rpmtd td,
325 				headerGetFlags hgflags)
326 {
327     int i;
328     char ** conds;
329     struct rpmtd_s indices, flags, scripts;
330     rpmTagVal triggerindex, triggerflags, triggerscripts;
331 
332     switch (mode) {
333 	case NORMALTRIGGER:
334 	    triggerindex = RPMTAG_TRIGGERINDEX;
335 	    triggerflags = RPMTAG_TRIGGERFLAGS;
336 	    triggerscripts = RPMTAG_TRIGGERSCRIPTS;
337 	    break;
338 	case FILETRIGGER:
339 	    triggerindex = RPMTAG_FILETRIGGERINDEX;
340 	    triggerflags = RPMTAG_FILETRIGGERFLAGS;
341 	    triggerscripts = RPMTAG_FILETRIGGERSCRIPTS;
342 	    break;
343 	case TRANSFILETRIGGER:
344 	    triggerindex = RPMTAG_TRANSFILETRIGGERINDEX;
345 	    triggerflags = RPMTAG_TRANSFILETRIGGERFLAGS;
346 	    triggerscripts = RPMTAG_TRANSFILETRIGGERSCRIPTS;
347 	    break;
348 	default:
349 	   return 0;
350     }
351 
352     if (!headerGet(h, triggerindex, &indices, HEADERGET_MINMEM)) {
353 	return 0;
354     }
355 
356     headerGet(h, triggerflags, &flags, HEADERGET_MINMEM);
357     headerGet(h, triggerscripts, &scripts, HEADERGET_MINMEM);
358 
359     td->flags = RPMTD_ALLOCED | RPMTD_PTR_ALLOCED;
360     td->count = rpmtdCount(&scripts);
361     td->data = conds = xmalloc(sizeof(*conds) * td->count);
362     td->type = RPM_STRING_ARRAY_TYPE;
363 
364     while ((i = rpmtdNext(&scripts)) >= 0) {
365 	rpm_flag_t *flag;
366 	rpmtdInit(&indices); rpmtdInit(&flags);
367 
368 	while (rpmtdNext(&indices) >= 0 && rpmtdNext(&flags) >= 0) {
369 	    if (*rpmtdGetUint32(&indices) != i)
370 		continue;
371 
372 	    flag = rpmtdGetUint32(&flags);
373 	    if (*flag & RPMSENSE_TRIGGERPREIN)
374 		conds[i] = xstrdup("prein");
375 	    else if (*flag & RPMSENSE_TRIGGERIN)
376 		conds[i] = xstrdup("in");
377 	    else if (*flag & RPMSENSE_TRIGGERUN)
378 		conds[i] = xstrdup("un");
379 	    else if (*flag & RPMSENSE_TRIGGERPOSTUN)
380 		conds[i] = xstrdup("postun");
381 	    else
382 		conds[i] = xstrdup("");
383 	    break;
384 	}
385     }
386     rpmtdFreeData(&indices);
387     rpmtdFreeData(&flags);
388     rpmtdFreeData(&scripts);
389 
390     return 1;
391 }
392 
triggertypeTag(Header h,rpmtd td,headerGetFlags hgflags)393 static int triggertypeTag(Header h, rpmtd td, headerGetFlags hgflags)
394 {
395     return triggertypeTagFor(NORMALTRIGGER, h, td, hgflags);
396 }
397 
filetriggertypeTag(Header h,rpmtd td,headerGetFlags hgflags)398 static int filetriggertypeTag(Header h, rpmtd td, headerGetFlags hgflags)
399 {
400     return triggertypeTagFor(FILETRIGGER, h, td, hgflags);
401 }
402 
transfiletriggertypeTag(Header h,rpmtd td,headerGetFlags hgflags)403 static int transfiletriggertypeTag(Header h, rpmtd td, headerGetFlags hgflags)
404 {
405     return triggertypeTagFor(TRANSFILETRIGGER, h, td, hgflags);
406 }
407 
408 /**
409  * Retrieve installed file paths.
410  * @param h		header
411  * @retval td		tag data container
412  * @param hgflags	header get flags
413  * @return		1 on success
414  */
instfilenamesTag(Header h,rpmtd td,headerGetFlags hgflags)415 static int instfilenamesTag(Header h, rpmtd td, headerGetFlags hgflags)
416 {
417     return fnTag(h, RPMTAG_BASENAMES, 1, td);
418 }
419 
420 /**
421  * Retrieve file paths.
422  * @param h		header
423  * @retval td		tag data container
424  * @param hgflags	header get flags
425  * @return		1 on success
426  */
filenamesTag(Header h,rpmtd td,headerGetFlags hgflags)427 static int filenamesTag(Header h, rpmtd td, headerGetFlags hgflags)
428 {
429     return fnTag(h, RPMTAG_BASENAMES, 0, td);
430 }
431 
432 /**
433  * Retrieve original file paths (wrt relocation).
434  * @param h		header
435  * @retval td		tag data container
436  * @param hgflags	header get flags
437  * @return		1 on success
438  */
origfilenamesTag(Header h,rpmtd td,headerGetFlags hgflags)439 static int origfilenamesTag(Header h, rpmtd td, headerGetFlags hgflags)
440 {
441     return fnTag(h, RPMTAG_ORIGBASENAMES, 0, td);
442 }
443 
444 /*
445  * Attempt to generate libmagic-style file class if missing from header:
446  * we can easily generate this for symlinks and other special types.
447  * Always return malloced strings to simplify life in fileclassTag().
448  */
makeFClass(rpmfi fi)449 static char *makeFClass(rpmfi fi)
450 {
451     char *fclass = NULL;
452     const char *hc = rpmfiFClass(fi);
453 
454     if (hc != NULL && hc[0] != '\0') {
455 	fclass = xstrdup(hc);
456     } else {
457 	switch (rpmfiFMode(fi) & S_IFMT) {
458 	case S_IFBLK:
459 	    fclass = xstrdup("block special");
460 	    break;
461 	case S_IFCHR:
462 	    fclass = xstrdup("character special");
463 	    break;
464 	case S_IFDIR:
465 	    fclass = xstrdup("directory");
466 	    break;
467 	case S_IFIFO:
468 	    fclass = xstrdup("fifo (named pipe)");
469 	    break;
470 	case S_IFSOCK:
471 	    fclass = xstrdup("socket");
472 	    break;
473 	case S_IFLNK:
474 	    fclass = rstrscat(NULL, "symbolic link to `",
475 			      rpmfiFLink(fi), "'", NULL);
476 	    break;
477 	}
478     }
479 
480     return (fclass != NULL) ? fclass : xstrdup("");
481 }
482 
483 /**
484  * Retrieve/generate file classes.
485  * @param h		header
486  * @retval td		tag data container
487  * @param hgflags	header get flags
488  * @return		1 on success
489  */
fileclassTag(Header h,rpmtd td,headerGetFlags hgflags)490 static int fileclassTag(Header h, rpmtd td, headerGetFlags hgflags)
491 {
492     rpmfi fi = rpmfiNew(NULL, h, RPMTAG_BASENAMES, RPMFI_NOHEADER);
493     int numfiles = rpmfiFC(fi);
494 
495     if (numfiles > 0) {
496 	char **fclasses = xmalloc(numfiles * sizeof(*fclasses));
497 	int ix;
498 
499 	rpmfiInit(fi, 0);
500 	while ((ix = rpmfiNext(fi)) >= 0) {
501 	    fclasses[ix] = makeFClass(fi);
502 	}
503 
504 	td->data = fclasses;
505 	td->count = numfiles;
506 	td->flags = RPMTD_ALLOCED | RPMTD_PTR_ALLOCED;
507 	td->type = RPM_STRING_ARRAY_TYPE;
508     }
509 
510     rpmfiFree(fi);
511     return (numfiles > 0);
512 }
513 
514 /**
515  * Retrieve file provides.
516  * @param h		header
517  * @retval td		tag data container
518  * @param hgflags	header get flags
519  * @return		1 on success
520  */
fileprovideTag(Header h,rpmtd td,headerGetFlags hgflags)521 static int fileprovideTag(Header h, rpmtd td, headerGetFlags hgflags)
522 {
523     return filedepTag(h, RPMTAG_PROVIDENAME, td, hgflags);
524 }
525 
526 /**
527  * Retrieve file requires.
528  * @param h		header
529  * @retval td		tag data container
530  * @param hgflags	header get flags
531  * @return		1 on success
532  */
filerequireTag(Header h,rpmtd td,headerGetFlags hgflags)533 static int filerequireTag(Header h, rpmtd td, headerGetFlags hgflags)
534 {
535     return filedepTag(h, RPMTAG_REQUIRENAME, td, hgflags);
536 }
537 
538 /* I18N look aside diversions */
539 
540 #if defined(ENABLE_NLS)
541 extern int _nl_msg_cat_cntr;	/* XXX GNU gettext voodoo */
542 #endif
543 static const char * const language = "LANGUAGE";
544 
545 static const char * const _macro_i18ndomains = "%{?_i18ndomains}";
546 
547 /**
548  * Retrieve i18n text.
549  * @param h		header
550  * @param tag		tag
551  * @retval td		tag data container
552  * @param hgflags	header get flags
553  * @return		1 on success
554  */
i18nTag(Header h,rpmTag tag,rpmtd td,headerGetFlags hgflags)555 static int i18nTag(Header h, rpmTag tag, rpmtd td, headerGetFlags hgflags)
556 {
557     int rc;
558 #if defined(ENABLE_NLS)
559     char * dstring = rpmExpand(_macro_i18ndomains, NULL);
560 
561     td->type = RPM_STRING_TYPE;
562     td->data = NULL;
563     td->count = 0;
564 
565     if (dstring && *dstring) {
566 	char *domain, *de;
567 	const char * langval;
568 	char * msgkey;
569 	const char * msgid;
570 
571 	rasprintf(&msgkey, "%s(%s)", headerGetString(h, RPMTAG_NAME),
572 		  rpmTagGetName(tag));
573 
574 	/* change to en_US for msgkey -> msgid resolution */
575 	langval = getenv(language);
576 	(void) setenv(language, "en_US", 1);
577         ++_nl_msg_cat_cntr;
578 
579 	msgid = NULL;
580 	for (domain = dstring; domain != NULL; domain = de) {
581 	    de = strchr(domain, ':');
582 	    if (de) *de++ = '\0';
583 	    msgid = dgettext(domain, msgkey);
584 	    if (msgid != msgkey) break;
585 	}
586 
587 	/* restore previous environment for msgid -> msgstr resolution */
588 	if (langval)
589 	    (void) setenv(language, langval, 1);
590 	else
591 	    unsetenv(language);
592         ++_nl_msg_cat_cntr;
593 
594 	if (domain && msgid) {
595 	    td->data = dgettext(domain, msgid);
596 	    td->data = xstrdup(td->data); /* XXX xstrdup has side effects. */
597 	    td->count = 1;
598 	    td->flags = RPMTD_ALLOCED;
599 	}
600 	dstring = _free(dstring);
601 	free(msgkey);
602 	if (td->data)
603 	    return 1;
604     }
605 
606     free(dstring);
607 #endif
608 
609     rc = headerGet(h, tag, td, HEADERGET_ALLOC);
610     return rc;
611 }
612 
613 /**
614  * Retrieve summary text.
615  * @param h		header
616  * @retval td		tag data container
617  * @param hgflags	header get flags
618  * @return		1 on success
619  */
summaryTag(Header h,rpmtd td,headerGetFlags hgflags)620 static int summaryTag(Header h, rpmtd td, headerGetFlags hgflags)
621 {
622     return i18nTag(h, RPMTAG_SUMMARY, td, hgflags);
623 }
624 
625 /**
626  * Retrieve description text.
627  * @param h		header
628  * @retval td		tag data container
629  * @param hgflags	header get flags
630  * @return		1 on success
631  */
descriptionTag(Header h,rpmtd td,headerGetFlags hgflags)632 static int descriptionTag(Header h, rpmtd td, headerGetFlags hgflags)
633 {
634     return i18nTag(h, RPMTAG_DESCRIPTION, td, hgflags);
635 }
636 
637 /**
638  * Retrieve group text.
639  * @param h		header
640  * @retval td		tag data container
641  * @param hgflags	header get flags
642  * @return		1 on success
643  */
groupTag(Header h,rpmtd td,headerGetFlags hgflags)644 static int groupTag(Header h, rpmtd td, headerGetFlags hgflags)
645 {
646     return i18nTag(h, RPMTAG_GROUP, td, hgflags);
647 }
648 
649 /*
650  * Helper to convert 32bit tag to 64bit version.
651  * If header has new 64bit tag then just return the data,
652  * otherwise convert 32bit old tag data to 64bit values.
653  * For consistency, always return malloced data.
654  */
get64(Header h,rpmtd td,rpmTag newtag,rpmTag oldtag)655 static int get64(Header h, rpmtd td, rpmTag newtag, rpmTag oldtag)
656 {
657     int rc;
658 
659     if (headerIsEntry(h, newtag)) {
660 	rc = headerGet(h, newtag, td, HEADERGET_ALLOC);
661     } else {
662 	struct rpmtd_s olddata;
663 	uint32_t *d32 = NULL;
664 	uint64_t *d64 = NULL;
665 
666 	headerGet(h, oldtag, &olddata, HEADERGET_MINMEM);
667 	if (rpmtdType(&olddata) == RPM_INT32_TYPE) {
668 	    td->type = RPM_INT64_TYPE;
669 	    td->count = olddata.count;
670 	    td->flags = RPMTD_ALLOCED;
671 	    td->data = xmalloc(sizeof(*d64) * td->count);
672 	    d64 = td->data;
673 	    while ((d32 = rpmtdNextUint32(&olddata))) {
674 		*d64++ = *d32;
675 	    }
676 	}
677 	rpmtdFreeData(&olddata);
678 	rc = d64 ? 1 : 0;
679     }
680 
681     return rc;
682 }
683 
684 /**
685  * Retrieve file sizes as 64bit regardless of how they're stored.
686  * @param h		header
687  * @retval td		tag data container
688  * @param hgflags	header get flags
689  * @return		1 on success
690  */
longfilesizesTag(Header h,rpmtd td,headerGetFlags hgflags)691 static int longfilesizesTag(Header h, rpmtd td, headerGetFlags hgflags)
692 {
693     return get64(h, td, RPMTAG_LONGFILESIZES, RPMTAG_FILESIZES);
694 }
695 
longarchivesizeTag(Header h,rpmtd td,headerGetFlags hgflags)696 static int longarchivesizeTag(Header h, rpmtd td, headerGetFlags hgflags)
697 {
698     return get64(h, td, RPMTAG_LONGARCHIVESIZE, RPMTAG_ARCHIVESIZE);
699 }
700 
longsizeTag(Header h,rpmtd td,headerGetFlags hgflags)701 static int longsizeTag(Header h, rpmtd td, headerGetFlags hgflags)
702 {
703     return get64(h, td, RPMTAG_LONGSIZE, RPMTAG_SIZE);
704 }
705 
longsigsizeTag(Header h,rpmtd td,headerGetFlags hgflags)706 static int longsigsizeTag(Header h, rpmtd td, headerGetFlags hgflags)
707 {
708     return get64(h, td, RPMTAG_LONGSIGSIZE, RPMTAG_SIGSIZE);
709 }
710 
numberTag(rpmtd td,uint32_t val)711 static int numberTag(rpmtd td, uint32_t val)
712 {
713     uint32_t *tval = xmalloc(sizeof(*tval));
714 
715     tval[0] = val;
716     td->type = RPM_INT32_TYPE;
717     td->count = 1;
718     td->data = tval;
719     td->flags = RPMTD_ALLOCED;
720     return 1; /* this cannot fail */
721 }
722 
dbinstanceTag(Header h,rpmtd td,headerGetFlags hgflags)723 static int dbinstanceTag(Header h, rpmtd td, headerGetFlags hgflags)
724 {
725     return numberTag(td, headerGetInstance(h));
726 }
727 
headercolorTag(Header h,rpmtd td,headerGetFlags hgflags)728 static int headercolorTag(Header h, rpmtd td, headerGetFlags hgflags)
729 {
730     rpm_color_t *fcolor, hcolor = 0;
731     struct rpmtd_s fcolors;
732 
733     headerGet(h, RPMTAG_FILECOLORS, &fcolors, HEADERGET_MINMEM);
734     while ((fcolor = rpmtdNextUint32(&fcolors)) != NULL) {
735 	hcolor |= *fcolor;
736     }
737     rpmtdFreeData(&fcolors);
738     hcolor &= 0x0f;
739 
740     return numberTag(td, hcolor);
741 }
742 
743 enum nevraFlags_e {
744     NEVRA_NAME		= (1 << 0),
745     NEVRA_EPOCH		= (1 << 1),
746     NEVRA_VERSION	= (1 << 2),
747     NEVRA_RELEASE	= (1 << 3),
748     NEVRA_ARCH		= (1 << 4)
749 };
750 typedef rpmFlags nevraFlags;
751 
getNEVRA(Header h,rpmtd td,nevraFlags flags)752 static int getNEVRA(Header h, rpmtd td, nevraFlags flags)
753 {
754     const char *val = NULL;
755     char *res = xstrdup("");
756 
757     if ((flags & NEVRA_NAME)) {
758 	val = headerGetString(h, RPMTAG_NAME);
759 	if (val) rstrscat(&res, val, "-", NULL);
760     }
761     if ((flags & NEVRA_EPOCH)) {
762 	char *e = headerGetAsString(h, RPMTAG_EPOCH);
763 	if (e) rstrscat(&res, e, ":", NULL);
764 	free(e);
765     }
766     if ((flags & NEVRA_VERSION)) {
767 	val = headerGetString(h, RPMTAG_VERSION);
768 	if (val) rstrscat(&res, val, "-", NULL);
769     }
770     if ((flags & NEVRA_RELEASE)) {
771 	val = headerGetString(h, RPMTAG_RELEASE);
772 	if (val) rstrscat(&res, val, NULL);
773     }
774     if ((flags & NEVRA_ARCH)) {
775 	val = headerGetString(h, RPMTAG_ARCH);
776 	if (headerIsSource(h) && val == NULL) val = "src";
777 	if (val) rstrscat(&res, ".", val, NULL);
778     }
779 
780     td->type = RPM_STRING_TYPE;
781     td->data = res;
782     td->count = 1;
783     td->flags = RPMTD_ALLOCED;
784 
785     return 1;
786 }
787 
evrTag(Header h,rpmtd td,headerGetFlags hgflags)788 static int evrTag(Header h, rpmtd td, headerGetFlags hgflags)
789 {
790     return getNEVRA(h, td, NEVRA_EPOCH|NEVRA_VERSION|NEVRA_RELEASE);
791 }
792 
nvrTag(Header h,rpmtd td,headerGetFlags hgflags)793 static int nvrTag(Header h, rpmtd td, headerGetFlags hgflags)
794 {
795     return getNEVRA(h, td, NEVRA_NAME|NEVRA_VERSION|NEVRA_RELEASE);
796 }
797 
nvraTag(Header h,rpmtd td,headerGetFlags hgflags)798 static int nvraTag(Header h, rpmtd td, headerGetFlags hgflags)
799 {
800     return getNEVRA(h, td, NEVRA_NAME|NEVRA_VERSION|NEVRA_RELEASE|NEVRA_ARCH);
801 }
802 
nevrTag(Header h,rpmtd td,headerGetFlags hgflags)803 static int nevrTag(Header h, rpmtd td, headerGetFlags hgflags)
804 {
805     return getNEVRA(h, td, NEVRA_NAME|NEVRA_EPOCH|NEVRA_VERSION|NEVRA_RELEASE);
806 }
807 
nevraTag(Header h,rpmtd td,headerGetFlags hgflags)808 static int nevraTag(Header h, rpmtd td, headerGetFlags hgflags)
809 {
810     return getNEVRA(h, td, NEVRA_NAME|NEVRA_EPOCH|NEVRA_VERSION|NEVRA_RELEASE|NEVRA_ARCH);
811 }
812 
verboseTag(Header h,rpmtd td,headerGetFlags hgflags)813 static int verboseTag(Header h, rpmtd td, headerGetFlags hgflags)
814 {
815     if (rpmIsVerbose()) {
816 	td->type = RPM_INT32_TYPE;
817 	td->count = 1;
818 	td->data = &(td->count);
819 	td->flags = RPMTD_NONE;
820 	return 1;
821     } else {
822 	return 0;
823     }
824 }
825 
epochnumTag(Header h,rpmtd td,headerGetFlags hgflags)826 static int epochnumTag(Header h, rpmtd td, headerGetFlags hgflags)
827 {
828     /* For consistency, always return malloced data */
829     if (!headerGet(h, RPMTAG_EPOCH, td, HEADERGET_ALLOC)) {
830 	uint32_t *e = malloc(sizeof(*e));
831 	*e = 0;
832 	td->data = e;
833 	td->type = RPM_INT32_TYPE;
834 	td->count = 1;
835 	td->flags = RPMTD_ALLOCED;
836     }
837     td->tag = RPMTAG_EPOCHNUM;
838     return 1;
839 }
840 
depnevrsTag(Header h,rpmtd td,headerGetFlags hgflags,rpmTagVal tag)841 static int depnevrsTag(Header h, rpmtd td, headerGetFlags hgflags,
842 			rpmTagVal tag)
843 {
844     rpmds ds = rpmdsNew(h, tag, 0);
845     int ndeps = rpmdsCount(ds);
846 
847     if (ndeps > 0) {
848 	char **deps = xmalloc(sizeof(*deps) * ndeps);
849 	int i;
850 	while ((i = rpmdsNext(ds)) >= 0) {
851 	    deps[i] = rpmdsNewDNEVR(NULL, ds);
852 	}
853 	td->data = deps;
854 	td->type = RPM_STRING_ARRAY_TYPE;
855 	td->count = ndeps;
856 	td->flags |= (RPMTD_ALLOCED | RPMTD_PTR_ALLOCED);
857     }
858     rpmdsFree(ds);
859     return (ndeps > 0);
860 }
861 
862 #define RPMSENSE_STRONG (1 << 27)
863 
depnevrsTagFiltered(Header h,rpmtd td,headerGetFlags hgflags,rpmTagVal tag,int strong)864 static int depnevrsTagFiltered(Header h, rpmtd td, headerGetFlags hgflags,
865 			rpmTagVal tag, int strong)
866 {
867     rpmds ds = rpmdsNew(h, tag, 0);
868     int ndeps = rpmdsCount(ds);
869 
870     if (ndeps > 0) {
871 	char **deps = xmalloc(sizeof(*deps) * ndeps);
872 	ndeps = 0;
873 	while (rpmdsNext(ds) >= 0) {
874 	    if ((rpmdsFlags(ds) & RPMSENSE_STRONG) == (strong ? RPMSENSE_STRONG : 0))
875 		deps[ndeps++] = rpmdsNewDNEVR(NULL, ds);
876 	}
877 	if (ndeps) {
878 	    td->data = deps;
879 	    td->type = RPM_STRING_ARRAY_TYPE;
880 	    td->count = ndeps;
881 	    td->flags |= (RPMTD_ALLOCED | RPMTD_PTR_ALLOCED);
882 	} else {
883 	    _free(deps);
884         }
885     }
886     rpmdsFree(ds);
887     return (ndeps > 0);
888 }
889 
requirenevrsTag(Header h,rpmtd td,headerGetFlags hgflags)890 static int requirenevrsTag(Header h, rpmtd td, headerGetFlags hgflags)
891 {
892     return depnevrsTag(h, td, hgflags, RPMTAG_REQUIRENAME);
893 }
894 
recommendnevrsTag(Header h,rpmtd td,headerGetFlags hgflags)895 static int recommendnevrsTag(Header h, rpmtd td, headerGetFlags hgflags)
896 {
897     return depnevrsTag(h, td, hgflags, RPMTAG_RECOMMENDNAME) ||
898            depnevrsTagFiltered(h, td, hgflags, RPMTAG_OLDSUGGESTSNAME, 1);
899 }
900 
suggestnevrsTag(Header h,rpmtd td,headerGetFlags hgflags)901 static int suggestnevrsTag(Header h, rpmtd td, headerGetFlags hgflags)
902 {
903     return depnevrsTag(h, td, hgflags, RPMTAG_SUGGESTNAME) ||
904            depnevrsTagFiltered(h, td, hgflags, RPMTAG_OLDSUGGESTSNAME, 0);
905 }
906 
supplementnevrsTag(Header h,rpmtd td,headerGetFlags hgflags)907 static int supplementnevrsTag(Header h, rpmtd td, headerGetFlags hgflags)
908 {
909     return depnevrsTag(h, td, hgflags, RPMTAG_SUPPLEMENTNAME) ||
910            depnevrsTagFiltered(h, td, hgflags, RPMTAG_OLDENHANCESNAME, 1);
911 }
912 
enhancenevrsTag(Header h,rpmtd td,headerGetFlags hgflags)913 static int enhancenevrsTag(Header h, rpmtd td, headerGetFlags hgflags)
914 {
915     return depnevrsTag(h, td, hgflags, RPMTAG_ENHANCENAME) ||
916            depnevrsTagFiltered(h, td, hgflags, RPMTAG_OLDENHANCESNAME, 0);
917 }
918 
providenevrsTag(Header h,rpmtd td,headerGetFlags hgflags)919 static int providenevrsTag(Header h, rpmtd td, headerGetFlags hgflags)
920 {
921     return depnevrsTag(h, td, hgflags, RPMTAG_PROVIDENAME);
922 }
923 
obsoletenevrsTag(Header h,rpmtd td,headerGetFlags hgflags)924 static int obsoletenevrsTag(Header h, rpmtd td, headerGetFlags hgflags)
925 {
926     return depnevrsTag(h, td, hgflags, RPMTAG_OBSOLETENAME);
927 }
928 
conflictnevrsTag(Header h,rpmtd td,headerGetFlags hgflags)929 static int conflictnevrsTag(Header h, rpmtd td, headerGetFlags hgflags)
930 {
931     return depnevrsTag(h, td, hgflags, RPMTAG_CONFLICTNAME);
932 }
933 
filenlinksTag(Header h,rpmtd td,headerGetFlags hgflags)934 static int filenlinksTag(Header h, rpmtd td, headerGetFlags hgflags)
935 {
936     rpmfi fi = rpmfiNew(NULL, h, RPMTAG_BASENAMES, RPMFI_NOHEADER);
937     rpm_count_t fc = rpmfiFC(fi);
938 
939     if (fc > 0) {
940 	uint32_t *nlinks = xmalloc(fc * sizeof(*nlinks));
941 	int ix;
942 	while ((ix = rpmfiNext(fi)) >= 0) {
943 	    nlinks[ix] = rpmfiFNlink(fi);
944 	}
945 	td->data = nlinks;
946 	td->type = RPM_INT32_TYPE;
947 	td->count = fc;
948 	td->flags = RPMTD_ALLOCED;
949     }
950 
951     rpmfiFree(fi);
952     return (fc > 0);
953 }
954 
955 static const struct headerTagFunc_s rpmHeaderTagExtensions[] = {
956     { RPMTAG_GROUP,		groupTag },
957     { RPMTAG_DESCRIPTION,	descriptionTag },
958     { RPMTAG_SUMMARY,		summaryTag },
959     { RPMTAG_FILECLASS,		fileclassTag },
960     { RPMTAG_FILENAMES,		filenamesTag },
961     { RPMTAG_ORIGFILENAMES,	origfilenamesTag },
962     { RPMTAG_FILEPROVIDE,	fileprovideTag },
963     { RPMTAG_FILEREQUIRE,	filerequireTag },
964     { RPMTAG_TRIGGERCONDS,	triggercondsTag },
965     { RPMTAG_FILETRIGGERCONDS,	filetriggercondsTag },
966     { RPMTAG_TRANSFILETRIGGERCONDS,	transfiletriggercondsTag },
967     { RPMTAG_TRIGGERTYPE,	triggertypeTag },
968     { RPMTAG_FILETRIGGERTYPE,	filetriggertypeTag },
969     { RPMTAG_TRANSFILETRIGGERTYPE,	transfiletriggertypeTag },
970     { RPMTAG_LONGFILESIZES,	longfilesizesTag },
971     { RPMTAG_LONGARCHIVESIZE,	longarchivesizeTag },
972     { RPMTAG_LONGSIZE,		longsizeTag },
973     { RPMTAG_LONGSIGSIZE,	longsigsizeTag },
974     { RPMTAG_DBINSTANCE,	dbinstanceTag },
975     { RPMTAG_EVR,		evrTag },
976     { RPMTAG_NVR,		nvrTag },
977     { RPMTAG_NEVR,		nevrTag },
978     { RPMTAG_NVRA,		nvraTag },
979     { RPMTAG_NEVRA,		nevraTag },
980     { RPMTAG_HEADERCOLOR,	headercolorTag },
981     { RPMTAG_VERBOSE,		verboseTag },
982     { RPMTAG_EPOCHNUM,		epochnumTag },
983     { RPMTAG_INSTFILENAMES,	instfilenamesTag },
984     { RPMTAG_REQUIRENEVRS,	requirenevrsTag },
985     { RPMTAG_RECOMMENDNEVRS,	recommendnevrsTag},
986     { RPMTAG_SUGGESTNEVRS,	suggestnevrsTag},
987     { RPMTAG_SUPPLEMENTNEVRS,	supplementnevrsTag},
988     { RPMTAG_ENHANCENEVRS,	enhancenevrsTag},
989     { RPMTAG_PROVIDENEVRS,	providenevrsTag },
990     { RPMTAG_OBSOLETENEVRS,	obsoletenevrsTag },
991     { RPMTAG_CONFLICTNEVRS,	conflictnevrsTag },
992     { RPMTAG_FILENLINKS,	filenlinksTag },
993     { 0, 			NULL }
994 };
995 
rpmHeaderTagFunc(rpmTagVal tag)996 headerTagTagFunction rpmHeaderTagFunc(rpmTagVal tag)
997 {
998     const struct headerTagFunc_s * ext;
999     headerTagTagFunction func = NULL;
1000 
1001     for (ext = rpmHeaderTagExtensions; ext->func != NULL; ext++) {
1002 	if (ext->tag == tag) {
1003 	    func = ext->func;
1004 	    break;
1005 	}
1006     }
1007     return func;
1008 }
1009 
1010