summaryrefslogtreecommitdiff
path: root/lib/formats.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/formats.c')
-rw-r--r--lib/formats.c705
1 files changed, 705 insertions, 0 deletions
diff --git a/lib/formats.c b/lib/formats.c
new file mode 100644
index 0000000..7ce4608
--- /dev/null
+++ b/lib/formats.c
@@ -0,0 +1,705 @@
+/** \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 "rpmio/digest.h"
+#include "lib/manifest.h"
+#include "lib/misc.h"
+
+#include "debug.h"
+
+/** \ingroup header
+ * Define header tag output formats.
+ */
+
+struct headerFormatFunc_s {
+ rpmtdFormats fmt; /*!< Value of extension */
+ const char *name; /*!< Name of extension. */
+ headerTagFormatFunction func; /*!< Pointer to formatter function. */
+};
+
+/**
+ * barebones string representation with no extra formatting
+ * @param td tag data container
+ * @param formatPrefix sprintf format string
+ * @return formatted string
+ */
+static char * stringFormat(rpmtd td, char *formatPrefix)
+{
+ const char *str = NULL;
+ char *val = NULL, *buf = NULL;
+
+ switch (rpmtdClass(td)) {
+ case RPM_NUMERIC_CLASS:
+ strcat(formatPrefix, PRIu64);
+ rasprintf(&val, formatPrefix, rpmtdGetNumber(td));
+ break;
+ case RPM_STRING_CLASS:
+ str = rpmtdGetString(td);
+ strcat(formatPrefix, "s");
+ rasprintf(&val, formatPrefix, str);
+ break;
+ case RPM_BINARY_CLASS:
+ buf = pgpHexStr(td->data, td->count);
+ strcat(formatPrefix, "s");
+ rasprintf(&val, formatPrefix, buf);
+ free(buf);
+ break;
+ default:
+ val = xstrdup("(unknown type)");
+ break;
+ }
+ return val;
+}
+
+static char * numFormat(rpmtd td, char * formatPrefix, char *format)
+{
+ char * val = NULL;
+
+ if (rpmtdClass(td) != RPM_NUMERIC_CLASS) {
+ val = xstrdup(_("(not a number)"));
+ } else {
+ strcat(formatPrefix, format);
+ rasprintf(&val, formatPrefix, rpmtdGetNumber(td));
+ }
+
+ return val;
+}
+/**
+ * octalFormat.
+ * @param td tag data container
+ * @param formatPrefix sprintf format string
+ * @return formatted string
+ */
+static char * octalFormat(rpmtd td, char * formatPrefix)
+{
+ return numFormat(td, formatPrefix, "o");
+}
+
+/**
+ * hexFormat.
+ * @param td tag data container
+ * @param formatPrefix sprintf format string
+ * @return formatted string
+ */
+static char * hexFormat(rpmtd td, char * formatPrefix)
+{
+ return numFormat(td, formatPrefix, "x");
+}
+
+/**
+ * @param td tag data container
+ * @param formatPrefix sprintf format string
+ * @return formatted string
+ */
+static char * realDateFormat(rpmtd td, char * formatPrefix,
+ const char * strftimeFormat)
+{
+ char * val = NULL;
+
+ if (rpmtdClass(td) != RPM_NUMERIC_CLASS) {
+ val = xstrdup(_("(not a number)"));
+ } else {
+ struct tm * tstruct;
+ char buf[50];
+ time_t dateint = rpmtdGetNumber(td);
+ tstruct = localtime(&dateint);
+
+ strcat(formatPrefix, "s");
+
+ buf[0] = '\0';
+ if (tstruct)
+ (void) strftime(buf, sizeof(buf) - 1, strftimeFormat, tstruct);
+ rasprintf(&val, formatPrefix, buf);
+ }
+
+ return val;
+}
+
+/**
+ * Format a date.
+ * @param td tag data container
+ * @param formatPrefix sprintf format string
+ * @return formatted string
+ */
+static char * dateFormat(rpmtd td, char * formatPrefix)
+{
+ return realDateFormat(td, formatPrefix, _("%c"));
+}
+
+/**
+ * Format a day.
+ * @param td tag data container
+ * @param formatPrefix sprintf format string
+ * @return formatted string
+ */
+static char * dayFormat(rpmtd td, char * formatPrefix)
+{
+ return realDateFormat(td, formatPrefix, _("%a %b %d %Y"));
+}
+
+/**
+ * Return shell escape formatted data.
+ * @param td tag data container
+ * @param formatPrefix sprintf format string
+ * @return formatted string
+ */
+static char * shescapeFormat(rpmtd td, char * formatPrefix)
+{
+ char * result = NULL, * dst, * src;
+
+ if (rpmtdClass(td) == RPM_NUMERIC_CLASS) {
+ strcat(formatPrefix, PRIu64);
+ rasprintf(&result, formatPrefix, rpmtdGetNumber(td));
+ } else {
+ char *buf = NULL;
+ strcat(formatPrefix, "s");
+ rasprintf(&buf, formatPrefix, 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);
+ }
+
+ return result;
+}
+
+
+/**
+ * Identify type of trigger.
+ * @param td tag data container
+ * @param formatPrefix sprintf format string
+ * @return formatted string
+ */
+static char * triggertypeFormat(rpmtd td, char * formatPrefix)
+{
+ char * val;
+
+ if (rpmtdClass(td) != RPM_NUMERIC_CLASS) {
+ val = xstrdup(_("(not a number)"));
+ } else {
+ 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;
+}
+
+/**
+ * Identify type of dependency.
+ * @param td tag data container
+ * @param formatPrefix sprintf format string
+ * @return formatted string
+ */
+static char * deptypeFormat(rpmtd td, char * formatPrefix)
+{
+ char *val = NULL;
+ if (rpmtdClass(td) != RPM_NUMERIC_CLASS) {
+ val = xstrdup(_("(not a number)"));
+ } else {
+ 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 (sdeps) {
+ val = argvJoin(sdeps, ",");
+ } else {
+ val = xstrdup("manual");
+ }
+
+ argvFree(sdeps);
+ }
+ return val;
+}
+
+/**
+ * Format file permissions for display.
+ * @param td tag data container
+ * @param formatPrefix sprintf format string
+ * @return formatted string
+ */
+static char * permsFormat(rpmtd td, char * formatPrefix)
+{
+ char * val = NULL;
+ char * buf;
+
+ if (rpmtdClass(td) != RPM_NUMERIC_CLASS) {
+ val = xstrdup(_("(not a number)"));
+ } else {
+ strcat(formatPrefix, "s");
+ buf = rpmPermsString(rpmtdGetNumber(td));
+ rasprintf(&val, formatPrefix, buf);
+ buf = _free(buf);
+ }
+
+ return val;
+}
+
+/**
+ * Format file flags for display.
+ * @param td tag data container
+ * @param formatPrefix sprintf format string
+ * @return formatted string
+ */
+static char * fflagsFormat(rpmtd td, char * formatPrefix)
+{
+ char * val = NULL;
+
+ if (rpmtdClass(td) != RPM_NUMERIC_CLASS) {
+ val = xstrdup(_("(not a number)"));
+ } else {
+ char *buf = rpmFFlagsString(rpmtdGetNumber(td), "");
+ strcat(formatPrefix, "s");
+ rasprintf(&val, formatPrefix, buf);
+ free(buf);
+ }
+
+ return val;
+}
+
+/**
+ * Wrap a pubkey in ascii armor for display.
+ * @todo Permit selectable display formats (i.e. binary).
+ * @param td tag data container
+ * @param formatPrefix sprintf format string
+ * @return formatted string
+ */
+static char * armorFormat(rpmtd td, char * formatPrefix)
+{
+ const char * enc;
+ const unsigned char * s;
+ unsigned char * bs = NULL;
+ char *val;
+ 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 (b64decode(enc, (void **)&bs, &ns))
+ return xstrdup(_("(not base64)"));
+ 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:
+ return xstrdup(_("(invalid type)"));
+ break;
+ }
+
+ /* XXX this doesn't use padding directly, assumes enough slop in retval. */
+ val = pgpArmorWrap(atype, s, ns);
+ if (atype == PGPARMOR_PUBKEY) {
+ free(bs);
+ }
+ return val;
+}
+
+/**
+ * Encode binary data in base64 for display.
+ * @todo Permit selectable display formats (i.e. binary).
+ * @param td tag data container
+ * @param formatPrefix sprintf format string
+ * @return formatted string
+ */
+static char * base64Format(rpmtd td, char * formatPrefix)
+{
+ char * val = NULL;
+
+ if (rpmtdType(td) != RPM_BIN_TYPE) {
+ val = xstrdup(_("(not a blob)"));
+ } else {
+ char * enc;
+ if ((enc = b64encode(td->data, td->count, -1)) != NULL) {
+ strcat(formatPrefix, "s");
+ rasprintf(&val, formatPrefix, enc ? enc : "");
+ free(enc);
+ }
+ }
+
+ return val;
+}
+
+/**
+ * Wrap tag data in simple header xml markup.
+ * @param td tag data container
+ * @param formatPrefix sprintf format string
+ * @return formatted string
+ */
+static char * xmlFormat(rpmtd td, char * formatPrefix)
+{
+ const char *xtag = NULL;
+ char *val = NULL;
+ char *s = NULL;
+ rpmtdFormats fmt = RPMTD_FORMAT_STRING;
+
+ switch (rpmtdClass(td)) {
+ case RPM_STRING_CLASS:
+ xtag = "string";
+ break;
+ case RPM_BINARY_CLASS:
+ fmt = RPMTD_FORMAT_BASE64;
+ xtag = "base64";
+ break;
+ case RPM_NUMERIC_CLASS:
+ xtag = "integer";
+ break;
+ case RPM_NULL_TYPE:
+ default:
+ return xstrdup(_("(invalid xml type)"));
+ break;
+ }
+
+ /* XXX TODO: handle errors */
+ s = rpmtdFormat(td, fmt, NULL);
+
+ 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, "&lt;"); break;
+ case '>': rstrcat(&new_s, "&gt;"); break;
+ case '&': rstrcat(&new_s, "&amp;"); 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);
+
+ strcat(formatPrefix, "s");
+ return val;
+}
+
+/**
+ * Display signature fingerprint and time.
+ * @param td tag data container
+ * @param formatPrefix sprintf format string
+ * @return formatted string
+ */
+static char * pgpsigFormat(rpmtd td, char * formatPrefix)
+{
+ char * val, * t;
+
+ if (rpmtdType(td) != RPM_BIN_TYPE) {
+ val = xstrdup(_("(not a blob)"));
+ } else {
+ const uint8_t * pkt = td->data;
+ size_t pktlen = 0;
+ unsigned int v = *pkt;
+ pgpTag tag = 0;
+ size_t plen;
+ size_t hlen = 0;
+
+ if (v & 0x80) {
+ if (v & 0x40) {
+ tag = (v & 0x3f);
+ plen = pgpLen(pkt+1, &hlen);
+ } else {
+ tag = (v >> 2) & 0xf;
+ plen = (1 << (v & 0x3));
+ hlen = pgpGrab(pkt+1, plen);
+ }
+
+ pktlen = 1 + plen + hlen;
+ }
+
+ if (pktlen == 0 || tag != PGPTAG_SIGNATURE) {
+ val = xstrdup(_("(not an OpenPGP signature)"));
+ } else {
+ pgpDig dig = pgpNewDig();
+ pgpDigParams sigp = &dig->signature;
+ size_t nb = 0;
+ char *tempstr = NULL;
+
+ (void) pgpPrtPkts(pkt, pktlen, dig, 0);
+
+ val = NULL;
+ again:
+ nb += 100;
+ val = t = xrealloc(val, nb + 1);
+
+ t = stpcpy(t, pgpValString(PGPVAL_PUBKEYALGO, sigp->pubkey_algo));
+ if (t + 5 >= val + nb)
+ goto again;
+ *t++ = '/';
+ t = stpcpy(t, pgpValString(PGPVAL_HASHALGO, sigp->hash_algo));
+ if (t + strlen (", ") + 1 >= val + nb)
+ goto again;
+
+ t = stpcpy(t, ", ");
+
+ /* this is important if sizeof(int32_t) ! sizeof(time_t) */
+ { time_t dateint = pgpGrab(sigp->time, sizeof(sigp->time));
+ struct tm * tstruct = localtime(&dateint);
+ if (tstruct)
+ (void) strftime(t, (nb - (t - val)), "%c", tstruct);
+ }
+ t += strlen(t);
+ if (t + strlen (", Key ID ") + 1 >= val + nb)
+ goto again;
+ t = stpcpy(t, ", Key ID ");
+ tempstr = pgpHexStr(sigp->signid, sizeof(sigp->signid));
+ if (t + strlen (tempstr) > val + nb)
+ goto again;
+ t = stpcpy(t, tempstr);
+ free(tempstr);
+
+ dig = pgpFreeDig(dig);
+ }
+ }
+
+ return val;
+}
+
+/**
+ * Format dependency flags for display.
+ * @param td tag data container
+ * @param formatPrefix sprintf format string
+ * @return formatted string
+ */
+static char * depflagsFormat(rpmtd td, char * formatPrefix)
+{
+ char * val = NULL;
+
+ if (rpmtdClass(td) != RPM_NUMERIC_CLASS) {
+ val = xstrdup(_("(not a number)"));
+ } else {
+ uint64_t anint = rpmtdGetNumber(td);
+ char buf[10];
+ buf[0] = '\0';
+
+ if (anint & RPMSENSE_LESS)
+ strcat(buf, "<");
+ if (anint & RPMSENSE_GREATER)
+ strcat(buf, ">");
+ if (anint & RPMSENSE_EQUAL)
+ strcat(buf, "=");
+
+ strcat(formatPrefix, "s");
+ rasprintf(&val, formatPrefix, buf);
+ }
+
+ return val;
+}
+
+/**
+ * Return tag container array size.
+ * @param td tag data container
+ * @param formatPrefix sprintf format string
+ * @return formatted string
+ */
+static char * arraysizeFormat(rpmtd td, char * formatPrefix)
+{
+ char *val = NULL;
+ strcat(formatPrefix, "u");
+ rasprintf(&val, formatPrefix, rpmtdCount(td));
+ return val;
+}
+
+static char * fstateFormat(rpmtd td, char * formatPrefix)
+{
+ char * val = NULL;
+
+ if (rpmtdClass(td) != RPM_NUMERIC_CLASS) {
+ val = xstrdup(_("(not a number)"));
+ } else {
+ 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;
+ }
+
+ strcat(formatPrefix, "s");
+ rasprintf(&val, formatPrefix, str);
+ }
+ return val;
+}
+
+static char * verifyFlags(rpmtd td, char * formatPrefix, const char *pad)
+{
+ char * val = NULL;
+
+ if (rpmtdClass(td) != RPM_NUMERIC_CLASS) {
+ val = xstrdup(_("(not a number)"));
+ } else {
+ strcat(formatPrefix, "s");
+ char *buf = rpmVerifyString(rpmtdGetNumber(td), pad);
+ rasprintf(&val, formatPrefix, buf);
+ buf = _free(buf);
+ }
+ return val;
+}
+
+static char * vflagsFormat(rpmtd td, char * formatPrefix)
+{
+ return verifyFlags(td, formatPrefix, "");
+}
+
+static char * fstatusFormat(rpmtd td, char * formatPrefix)
+{
+ return verifyFlags(td, formatPrefix, ".");
+}
+
+static char * expandFormat(rpmtd td, char * formatPrefix)
+{
+ char *val = NULL;
+ if (rpmtdClass(td) != RPM_STRING_CLASS) {
+ val = xstrdup(_("(not a string)"));
+ } else {
+ val = rpmExpand(td->data, NULL);
+ }
+ strcat(formatPrefix, "s");
+ return val;
+}
+
+static const struct headerFormatFunc_s rpmHeaderFormats[] = {
+ { RPMTD_FORMAT_STRING, "string", stringFormat },
+ { RPMTD_FORMAT_ARMOR, "armor", armorFormat },
+ { RPMTD_FORMAT_BASE64, "base64", base64Format },
+ { RPMTD_FORMAT_PGPSIG, "pgpsig", pgpsigFormat },
+ { RPMTD_FORMAT_DEPFLAGS, "depflags", depflagsFormat },
+ { RPMTD_FORMAT_DEPTYPE, "deptype", deptypeFormat },
+ { RPMTD_FORMAT_FFLAGS, "fflags", fflagsFormat },
+ { RPMTD_FORMAT_PERMS, "perms", permsFormat },
+ { RPMTD_FORMAT_PERMS, "permissions", permsFormat },
+ { RPMTD_FORMAT_TRIGGERTYPE, "triggertype", triggertypeFormat },
+ { RPMTD_FORMAT_XML, "xml", xmlFormat },
+ { RPMTD_FORMAT_OCTAL, "octal", octalFormat },
+ { RPMTD_FORMAT_HEX, "hex", hexFormat },
+ { RPMTD_FORMAT_DATE, "date", dateFormat },
+ { RPMTD_FORMAT_DAY, "day", dayFormat },
+ { RPMTD_FORMAT_SHESCAPE, "shescape", shescapeFormat },
+ { RPMTD_FORMAT_ARRAYSIZE, "arraysize", arraysizeFormat },
+ { RPMTD_FORMAT_FSTATE, "fstate", fstateFormat },
+ { RPMTD_FORMAT_VFLAGS, "vflags", vflagsFormat },
+ { RPMTD_FORMAT_EXPAND, "expand", expandFormat },
+ { RPMTD_FORMAT_FSTATUS, "fstatus", fstatusFormat },
+ { -1, NULL, NULL }
+};
+
+headerTagFormatFunction rpmHeaderFormatFuncByName(const char *fmt)
+{
+ const struct headerFormatFunc_s * ext;
+ headerTagFormatFunction func = NULL;
+
+ for (ext = rpmHeaderFormats; ext->name != NULL; ext++) {
+ if (rstreq(ext->name, fmt)) {
+ func = ext->func;
+ break;
+ }
+ }
+ return func;
+}
+
+headerTagFormatFunction rpmHeaderFormatFuncByValue(rpmtdFormats fmt)
+{
+ const struct headerFormatFunc_s * ext;
+ headerTagFormatFunction func = NULL;
+
+ for (ext = rpmHeaderFormats; ext->name != NULL; ext++) {
+ if (fmt == ext->fmt) {
+ func = ext->func;
+ break;
+ }
+ }
+ return func;
+}
+