549 lines
13 KiB
C
549 lines
13 KiB
C
/** \ingroup header
|
|
* \file lib/formats.c
|
|
*/
|
|
|
|
#include "system.h"
|
|
|
|
#include <inttypes.h>
|
|
|
|
#include <rpm/rpmtypes.h>
|
|
#include <rpm/rpmtd.h>
|
|
#include <rpm/rpmds.h>
|
|
#include <rpm/rpmfi.h>
|
|
#include <rpm/rpmstring.h>
|
|
#include <rpm/rpmmacro.h>
|
|
#include <rpm/rpmbase64.h>
|
|
|
|
#include "rpmio/digest.h"
|
|
#include "lib/manifest.h"
|
|
#include "lib/misc.h"
|
|
#include "lib/signature.h"
|
|
|
|
#include "debug.h"
|
|
|
|
#define RPM_ANY_CLASS 255
|
|
|
|
typedef char * (*headerTagFormatFunction) (rpmtd td, char **emsg);
|
|
|
|
/** \ingroup header
|
|
* Define header tag output formats.
|
|
*/
|
|
|
|
struct headerFmt_s {
|
|
rpmtdFormats fmt; /*!< Value of extension */
|
|
const char *name; /*!< Name of extension. */
|
|
rpmTagClass class; /*!< Class of source data (RPM_ANY_CLASS for any) */
|
|
headerTagFormatFunction func; /*!< Pointer to formatter function. */
|
|
};
|
|
|
|
static const char *classEr(rpmTagClass class)
|
|
{
|
|
switch (class) {
|
|
case RPM_BINARY_CLASS: return _("(not a blob)");
|
|
case RPM_NUMERIC_CLASS: return _("(not a number)");
|
|
case RPM_STRING_CLASS: return _("(not a string)");
|
|
default: break;
|
|
}
|
|
return _("(invalid type)");
|
|
}
|
|
|
|
/* barebones string representation with no extra formatting */
|
|
static char * stringFormat(rpmtd td, char **emsg)
|
|
{
|
|
char *val = NULL;
|
|
|
|
switch (rpmtdClass(td)) {
|
|
case RPM_NUMERIC_CLASS:
|
|
rasprintf(&val, "%" PRIu64, rpmtdGetNumber(td));
|
|
break;
|
|
case RPM_STRING_CLASS: {
|
|
const char *str = rpmtdGetString(td);
|
|
if (str)
|
|
val = xstrdup(str);
|
|
break;
|
|
}
|
|
case RPM_BINARY_CLASS:
|
|
val = pgpHexStr(td->data, td->count);
|
|
break;
|
|
default:
|
|
*emsg = xstrdup("(unknown type)");
|
|
break;
|
|
}
|
|
return val;
|
|
}
|
|
|
|
/* arbitrary number format as per format arg */
|
|
static char * numFormat(rpmtd td, const char *format)
|
|
{
|
|
char * val = NULL;
|
|
rasprintf(&val, format, rpmtdGetNumber(td));
|
|
return val;
|
|
}
|
|
|
|
/* octal number formatting */
|
|
static char * octalFormat(rpmtd td, char **emsg)
|
|
{
|
|
return numFormat(td, "%o");
|
|
}
|
|
|
|
/* hexadecimal format */
|
|
static char * hexFormat(rpmtd td, char **emsg)
|
|
{
|
|
return numFormat(td, "%x");
|
|
}
|
|
|
|
/* arbitrary date formatting as per strftimeFormat arg */
|
|
static char * realDateFormat(rpmtd td, const char * strftimeFormat)
|
|
{
|
|
char * val = NULL;
|
|
struct tm * tstruct;
|
|
char buf[50];
|
|
time_t dateint = rpmtdGetNumber(td);
|
|
tstruct = localtime(&dateint);
|
|
|
|
/* XXX TODO: deal with non-fitting date string correctly */
|
|
buf[0] = '\0';
|
|
if (tstruct)
|
|
(void) strftime(buf, sizeof(buf) - 1, strftimeFormat, tstruct);
|
|
val = xstrdup(buf);
|
|
|
|
return val;
|
|
}
|
|
|
|
/* date formatting */
|
|
static char * dateFormat(rpmtd td, char **emsg)
|
|
{
|
|
return realDateFormat(td, _("%c"));
|
|
}
|
|
|
|
/* day formatting */
|
|
static char * dayFormat(rpmtd td, char **emsg)
|
|
{
|
|
return realDateFormat(td, _("%a %b %d %Y"));
|
|
}
|
|
|
|
/* shell escape formatting */
|
|
static char * shescapeFormat(rpmtd td, char **emsg)
|
|
{
|
|
char * result = NULL, * dst, * src;
|
|
|
|
if (rpmtdClass(td) == RPM_NUMERIC_CLASS) {
|
|
rasprintf(&result, "%" PRIu64, rpmtdGetNumber(td));
|
|
} else if (rpmtdClass(td) == RPM_STRING_CLASS) {
|
|
char *buf = xstrdup(rpmtdGetString(td));;
|
|
|
|
result = dst = xmalloc(strlen(buf) * 4 + 3);
|
|
*dst++ = '\'';
|
|
for (src = buf; *src != '\0'; src++) {
|
|
if (*src == '\'') {
|
|
*dst++ = '\'';
|
|
*dst++ = '\\';
|
|
*dst++ = '\'';
|
|
*dst++ = '\'';
|
|
} else {
|
|
*dst++ = *src;
|
|
}
|
|
}
|
|
*dst++ = '\'';
|
|
*dst = '\0';
|
|
free(buf);
|
|
} else {
|
|
*emsg = xstrdup(_("(invalid type)"));
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
/* trigger type formatting (from rpmsense flags) */
|
|
static char * triggertypeFormat(rpmtd td, char **emsg)
|
|
{
|
|
char * val;
|
|
uint64_t item = rpmtdGetNumber(td);
|
|
if (item & RPMSENSE_TRIGGERPREIN)
|
|
val = xstrdup("prein");
|
|
else if (item & RPMSENSE_TRIGGERIN)
|
|
val = xstrdup("in");
|
|
else if (item & RPMSENSE_TRIGGERUN)
|
|
val = xstrdup("un");
|
|
else if (item & RPMSENSE_TRIGGERPOSTUN)
|
|
val = xstrdup("postun");
|
|
else
|
|
val = xstrdup("");
|
|
return val;
|
|
}
|
|
|
|
/* dependency type formatting (from rpmsense flags) */
|
|
static char * deptypeFormat(rpmtd td, char **emsg)
|
|
{
|
|
char *val = NULL;
|
|
ARGV_t sdeps = NULL;
|
|
uint64_t item = rpmtdGetNumber(td);
|
|
|
|
if (item & RPMSENSE_SCRIPT_PRE)
|
|
argvAdd(&sdeps, "pre");
|
|
if (item & RPMSENSE_SCRIPT_POST)
|
|
argvAdd(&sdeps, "post");
|
|
if (item & RPMSENSE_SCRIPT_PREUN)
|
|
argvAdd(&sdeps, "preun");
|
|
if (item & RPMSENSE_SCRIPT_POSTUN)
|
|
argvAdd(&sdeps, "postun");
|
|
if (item & RPMSENSE_SCRIPT_VERIFY)
|
|
argvAdd(&sdeps, "verify");
|
|
if (item & RPMSENSE_INTERP)
|
|
argvAdd(&sdeps, "interp");
|
|
if (item & RPMSENSE_RPMLIB)
|
|
argvAdd(&sdeps, "rpmlib");
|
|
if ((item & RPMSENSE_FIND_REQUIRES) || (item & RPMSENSE_FIND_PROVIDES))
|
|
argvAdd(&sdeps, "auto");
|
|
if (item & RPMSENSE_PREREQ)
|
|
argvAdd(&sdeps, "prereq");
|
|
if (item & RPMSENSE_PRETRANS)
|
|
argvAdd(&sdeps, "pretrans");
|
|
if (item & RPMSENSE_POSTTRANS)
|
|
argvAdd(&sdeps, "posttrans");
|
|
if (item & RPMSENSE_CONFIG)
|
|
argvAdd(&sdeps, "config");
|
|
if (item & RPMSENSE_MISSINGOK)
|
|
argvAdd(&sdeps, "missingok");
|
|
|
|
if (sdeps) {
|
|
val = argvJoin(sdeps, ",");
|
|
} else {
|
|
val = xstrdup("manual");
|
|
}
|
|
|
|
argvFree(sdeps);
|
|
return val;
|
|
}
|
|
|
|
/* file permissions formatting */
|
|
static char * permsFormat(rpmtd td, char **emsg)
|
|
{
|
|
return rpmPermsString(rpmtdGetNumber(td));
|
|
}
|
|
|
|
/* file flags formatting */
|
|
static char * fflagsFormat(rpmtd td, char **emsg)
|
|
{
|
|
return rpmFFlagsString(rpmtdGetNumber(td), "");
|
|
}
|
|
|
|
/* pubkey ascii armor formatting */
|
|
static char * armorFormat(rpmtd td, char **emsg)
|
|
{
|
|
const char * enc;
|
|
const unsigned char * s;
|
|
unsigned char * bs = NULL;
|
|
char *val = NULL;
|
|
size_t ns;
|
|
int atype;
|
|
|
|
switch (rpmtdType(td)) {
|
|
case RPM_BIN_TYPE:
|
|
s = td->data;
|
|
/* XXX HACK ALERT: element field abused as no. bytes of binary data. */
|
|
ns = td->count;
|
|
atype = PGPARMOR_SIGNATURE; /* XXX check pkt for signature */
|
|
break;
|
|
case RPM_STRING_TYPE:
|
|
case RPM_STRING_ARRAY_TYPE:
|
|
enc = rpmtdGetString(td);
|
|
if (rpmBase64Decode(enc, (void **)&bs, &ns)) {
|
|
*emsg = xstrdup(_("(not base64)"));
|
|
goto exit;
|
|
}
|
|
s = bs;
|
|
atype = PGPARMOR_PUBKEY; /* XXX check pkt for pubkey */
|
|
break;
|
|
case RPM_NULL_TYPE:
|
|
case RPM_CHAR_TYPE:
|
|
case RPM_INT8_TYPE:
|
|
case RPM_INT16_TYPE:
|
|
case RPM_INT32_TYPE:
|
|
case RPM_INT64_TYPE:
|
|
case RPM_I18NSTRING_TYPE:
|
|
default:
|
|
*emsg = xstrdup(_("(invalid type)"));
|
|
goto exit;
|
|
break;
|
|
}
|
|
|
|
/* XXX this doesn't use padding directly, assumes enough slop in retval. */
|
|
val = pgpArmorWrap(atype, s, ns);
|
|
if (atype == PGPARMOR_PUBKEY) {
|
|
free(bs);
|
|
}
|
|
|
|
exit:
|
|
return val;
|
|
}
|
|
|
|
/* base64 encoding formatting */
|
|
static char * base64Format(rpmtd td, char **emsg)
|
|
{
|
|
char * val = rpmBase64Encode(td->data, td->count, -1);
|
|
if (val == NULL)
|
|
val = xstrdup("");
|
|
|
|
return val;
|
|
}
|
|
|
|
/* xml formatting */
|
|
static char * xmlFormat(rpmtd td, char **emsg)
|
|
{
|
|
const char *xtag = NULL;
|
|
char *val = NULL;
|
|
char *s = NULL;
|
|
headerTagFormatFunction fmt = stringFormat;
|
|
|
|
switch (rpmtdClass(td)) {
|
|
case RPM_STRING_CLASS:
|
|
xtag = "string";
|
|
break;
|
|
case RPM_BINARY_CLASS:
|
|
fmt = base64Format;
|
|
xtag = "base64";
|
|
break;
|
|
case RPM_NUMERIC_CLASS:
|
|
xtag = "integer";
|
|
break;
|
|
case RPM_NULL_TYPE:
|
|
default:
|
|
*emsg = xstrdup(_("(invalid xml type)"));
|
|
goto exit;
|
|
break;
|
|
}
|
|
|
|
s = fmt(td, emsg);
|
|
if (s == NULL)
|
|
goto exit;
|
|
|
|
if (s[0] == '\0') {
|
|
val = rstrscat(NULL, "\t<", xtag, "/>", NULL);
|
|
} else {
|
|
char *new_s = NULL;
|
|
size_t i, s_size = strlen(s);
|
|
|
|
for (i=0; i<s_size; i++) {
|
|
switch (s[i]) {
|
|
case '<': rstrcat(&new_s, "<"); break;
|
|
case '>': rstrcat(&new_s, ">"); break;
|
|
case '&': rstrcat(&new_s, "&"); break;
|
|
default: {
|
|
char c[2] = " ";
|
|
*c = s[i];
|
|
rstrcat(&new_s, c);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
val = rstrscat(NULL, "\t<", xtag, ">", new_s, "</", xtag, ">", NULL);
|
|
free(new_s);
|
|
}
|
|
free(s);
|
|
|
|
exit:
|
|
return val;
|
|
}
|
|
|
|
/* signature fingerprint and time formatting */
|
|
static char * pgpsigFormat(rpmtd td, char **emsg)
|
|
{
|
|
char * val = NULL;
|
|
pgpDigParams sigp = NULL;
|
|
|
|
if (pgpPrtParams(td->data, td->count, PGPTAG_SIGNATURE, &sigp)) {
|
|
*emsg = xstrdup(_("(not an OpenPGP signature)"));
|
|
} else {
|
|
char dbuf[BUFSIZ];
|
|
char *keyid = pgpHexStr(sigp->signid, sizeof(sigp->signid));
|
|
unsigned int dateint = pgpGrab(sigp->time, sizeof(sigp->time));
|
|
time_t date = dateint;
|
|
struct tm * tms = localtime(&date);
|
|
unsigned int key_algo = pgpDigParamsAlgo(sigp, PGPVAL_PUBKEYALGO);
|
|
unsigned int hash_algo = pgpDigParamsAlgo(sigp, PGPVAL_HASHALGO);
|
|
|
|
if (!(tms && strftime(dbuf, sizeof(dbuf), "%c", tms) > 0)) {
|
|
rasprintf(emsg, _("Invalid date %u"), dateint);
|
|
} else {
|
|
rasprintf(&val, "%s/%s, %s, Key ID %s",
|
|
pgpValString(PGPVAL_PUBKEYALGO, key_algo),
|
|
pgpValString(PGPVAL_HASHALGO, hash_algo),
|
|
dbuf, keyid);
|
|
}
|
|
|
|
free(keyid);
|
|
pgpDigParamsFree(sigp);
|
|
}
|
|
|
|
return val;
|
|
}
|
|
|
|
/* dependency flags formatting */
|
|
static char * depflagsFormat(rpmtd td, char **emsg)
|
|
{
|
|
char * val = NULL;
|
|
uint64_t anint = rpmtdGetNumber(td);
|
|
val = xcalloc(4, 1);
|
|
|
|
if (anint & RPMSENSE_LESS)
|
|
strcat(val, "<");
|
|
if (anint & RPMSENSE_GREATER)
|
|
strcat(val, ">");
|
|
if (anint & RPMSENSE_EQUAL)
|
|
strcat(val, "=");
|
|
|
|
return val;
|
|
}
|
|
|
|
/* tag container array size */
|
|
static char * arraysizeFormat(rpmtd td, char **emsg)
|
|
{
|
|
char *val = NULL;
|
|
rasprintf(&val, "%u", rpmtdCount(td));
|
|
return val;
|
|
}
|
|
|
|
/* file state formatting */
|
|
static char * fstateFormat(rpmtd td, char **emsg)
|
|
{
|
|
char * val = NULL;
|
|
const char * str;
|
|
rpmfileState fstate = rpmtdGetNumber(td);
|
|
switch (fstate) {
|
|
case RPMFILE_STATE_NORMAL:
|
|
str = _("normal");
|
|
break;
|
|
case RPMFILE_STATE_REPLACED:
|
|
str = _("replaced");
|
|
break;
|
|
case RPMFILE_STATE_NOTINSTALLED:
|
|
str = _("not installed");
|
|
break;
|
|
case RPMFILE_STATE_NETSHARED:
|
|
str = _("net shared");
|
|
break;
|
|
case RPMFILE_STATE_WRONGCOLOR:
|
|
str = _("wrong color");
|
|
break;
|
|
case RPMFILE_STATE_MISSING:
|
|
str = _("missing");
|
|
break;
|
|
default:
|
|
str = _("(unknown)");
|
|
break;
|
|
}
|
|
|
|
val = xstrdup(str);
|
|
return val;
|
|
}
|
|
|
|
/* file verification flags formatting with optional padding */
|
|
static char * verifyFlags(rpmtd td, const char *pad)
|
|
{
|
|
return rpmVerifyString(rpmtdGetNumber(td), pad);
|
|
}
|
|
|
|
static char * vflagsFormat(rpmtd td, char **emsg)
|
|
{
|
|
return verifyFlags(td, "");
|
|
}
|
|
|
|
static char * fstatusFormat(rpmtd td, char **emsg)
|
|
{
|
|
return verifyFlags(td, ".");
|
|
}
|
|
|
|
/* macro expansion formatting */
|
|
static char * expandFormat(rpmtd td, char **emsg)
|
|
{
|
|
return rpmExpand(rpmtdGetString(td), NULL);
|
|
}
|
|
|
|
static const struct headerFmt_s rpmHeaderFormats[] = {
|
|
{ RPMTD_FORMAT_STRING, "string",
|
|
RPM_ANY_CLASS, stringFormat },
|
|
{ RPMTD_FORMAT_ARMOR, "armor",
|
|
RPM_ANY_CLASS, armorFormat },
|
|
{ RPMTD_FORMAT_BASE64, "base64",
|
|
RPM_BINARY_CLASS, base64Format },
|
|
{ RPMTD_FORMAT_PGPSIG, "pgpsig",
|
|
RPM_BINARY_CLASS, pgpsigFormat },
|
|
{ RPMTD_FORMAT_DEPFLAGS, "depflags",
|
|
RPM_NUMERIC_CLASS, depflagsFormat },
|
|
{ RPMTD_FORMAT_DEPTYPE, "deptype",
|
|
RPM_NUMERIC_CLASS, deptypeFormat },
|
|
{ RPMTD_FORMAT_FFLAGS, "fflags",
|
|
RPM_NUMERIC_CLASS, fflagsFormat },
|
|
{ RPMTD_FORMAT_PERMS, "perms",
|
|
RPM_NUMERIC_CLASS, permsFormat },
|
|
{ RPMTD_FORMAT_PERMS, "permissions",
|
|
RPM_NUMERIC_CLASS, permsFormat },
|
|
{ RPMTD_FORMAT_TRIGGERTYPE, "triggertype",
|
|
RPM_NUMERIC_CLASS, triggertypeFormat },
|
|
{ RPMTD_FORMAT_XML, "xml",
|
|
RPM_ANY_CLASS, xmlFormat },
|
|
{ RPMTD_FORMAT_OCTAL, "octal",
|
|
RPM_NUMERIC_CLASS, octalFormat },
|
|
{ RPMTD_FORMAT_HEX, "hex",
|
|
RPM_NUMERIC_CLASS, hexFormat },
|
|
{ RPMTD_FORMAT_DATE, "date",
|
|
RPM_NUMERIC_CLASS, dateFormat },
|
|
{ RPMTD_FORMAT_DAY, "day",
|
|
RPM_NUMERIC_CLASS, dayFormat },
|
|
{ RPMTD_FORMAT_SHESCAPE, "shescape",
|
|
RPM_ANY_CLASS, shescapeFormat },
|
|
{ RPMTD_FORMAT_ARRAYSIZE, "arraysize",
|
|
RPM_ANY_CLASS, arraysizeFormat },
|
|
{ RPMTD_FORMAT_FSTATE, "fstate",
|
|
RPM_NUMERIC_CLASS, fstateFormat },
|
|
{ RPMTD_FORMAT_VFLAGS, "vflags",
|
|
RPM_NUMERIC_CLASS, vflagsFormat },
|
|
{ RPMTD_FORMAT_EXPAND, "expand",
|
|
RPM_STRING_CLASS, expandFormat },
|
|
{ RPMTD_FORMAT_FSTATUS, "fstatus",
|
|
RPM_NUMERIC_CLASS, fstatusFormat },
|
|
{ -1, NULL, 0, NULL }
|
|
};
|
|
|
|
headerFmt rpmHeaderFormatByName(const char *fmt)
|
|
{
|
|
const struct headerFmt_s * ext;
|
|
|
|
for (ext = rpmHeaderFormats; ext->name != NULL; ext++) {
|
|
if (rstreq(ext->name, fmt))
|
|
return ext;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
headerFmt rpmHeaderFormatByValue(rpmtdFormats fmt)
|
|
{
|
|
const struct headerFmt_s * ext;
|
|
|
|
for (ext = rpmHeaderFormats; ext->name != NULL; ext++) {
|
|
if (fmt == ext->fmt)
|
|
return ext;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
char *rpmHeaderFormatCall(headerFmt fmt, rpmtd td)
|
|
{
|
|
char *ret = NULL;
|
|
char *err = NULL;
|
|
|
|
if (fmt->class != RPM_ANY_CLASS && rpmtdClass(td) != fmt->class)
|
|
err = xstrdup(classEr(fmt->class));
|
|
else
|
|
ret = fmt->func(td, &err);
|
|
|
|
if (err) {
|
|
free(ret);
|
|
ret = err;
|
|
}
|
|
return ret;
|
|
}
|