/** \ingroup header * \file lib/formats.c */ #include "system.h" #include #include #include #include #include #include #include #include #include "rpmio/digest.h" #include "lib/manifest.h" #include "lib/misc.h" #include "lib/signature.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 * @return formatted string */ static char * stringFormat(rpmtd td) { char *val = NULL; switch (rpmtdClass(td)) { case RPM_NUMERIC_CLASS: rasprintf(&val, "%" PRIu64, rpmtdGetNumber(td)); break; case RPM_STRING_CLASS: val = xstrdup(rpmtdGetString(td)); break; case RPM_BINARY_CLASS: val = pgpHexStr(td->data, td->count); break; default: val = xstrdup("(unknown type)"); break; } return val; } static char * numFormat(rpmtd td, const char *format) { char * val = NULL; if (rpmtdClass(td) != RPM_NUMERIC_CLASS) { val = xstrdup(_("(not a number)")); } else { rasprintf(&val, format, rpmtdGetNumber(td)); } return val; } /** * octalFormat. * @param td tag data container * @return formatted string */ static char * octalFormat(rpmtd td) { return numFormat(td, "%o"); } /** * hexFormat. * @param td tag data container * @return formatted string */ static char * hexFormat(rpmtd td) { return numFormat(td, "%x"); } /** * @param td tag data container * @return formatted string */ static char * realDateFormat(rpmtd td, 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); /* 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; } /** * Format a date. * @param td tag data container * @return formatted string */ static char * dateFormat(rpmtd td) { return realDateFormat(td, _("%c")); } /** * Format a day. * @param td tag data container * @return formatted string */ static char * dayFormat(rpmtd td) { return realDateFormat(td, _("%a %b %d %Y")); } /** * Return shell escape formatted data. * @param td tag data container * @return formatted string */ static char * shescapeFormat(rpmtd td) { char * result = NULL, * dst, * src; if (rpmtdClass(td) == RPM_NUMERIC_CLASS) { rasprintf(&result, "%" PRIu64, rpmtdGetNumber(td)); } else { 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); } return result; } /** * Identify type of trigger. * @param td tag data container * @return formatted string */ static char * triggertypeFormat(rpmtd td) { 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 * @return formatted string */ static char * deptypeFormat(rpmtd td) { 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 (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; } /** * Format file permissions for display. * @param td tag data container * @return formatted string */ static char * permsFormat(rpmtd td) { char * val = NULL; if (rpmtdClass(td) != RPM_NUMERIC_CLASS) { val = xstrdup(_("(not a number)")); } else { val = rpmPermsString(rpmtdGetNumber(td)); } return val; } /** * Format file flags for display. * @param td tag data container * @return formatted string */ static char * fflagsFormat(rpmtd td) { char * val = NULL; if (rpmtdClass(td) != RPM_NUMERIC_CLASS) { val = xstrdup(_("(not a number)")); } else { val = rpmFFlagsString(rpmtdGetNumber(td), ""); } return val; } /** * Wrap a pubkey in ascii armor for display. * @todo Permit selectable display formats (i.e. binary). * @param td tag data container * @return formatted string */ static char * armorFormat(rpmtd td) { 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 (rpmBase64Decode(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 * @return formatted string */ static char * base64Format(rpmtd td) { char * val = NULL; if (rpmtdType(td) != RPM_BIN_TYPE) { val = xstrdup(_("(not a blob)")); } else { val = rpmBase64Encode(td->data, td->count, -1); if (val == NULL) val = xstrdup(""); } return val; } /** * Wrap tag data in simple header xml markup. * @param td tag data container * @return formatted string */ static char * xmlFormat(rpmtd td) { 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': 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, "", NULL); free(new_s); } free(s); return val; } /** * Display signature fingerprint and time. * @param td tag data container * @return formatted string */ static char * pgpsigFormat(rpmtd td) { char * val = NULL; if (rpmtdType(td) != RPM_BIN_TYPE) { val = xstrdup(_("(not a blob)")); } else { pgpDigParams sigp = NULL; if (pgpPrtParams(td->data, td->count, PGPTAG_SIGNATURE, &sigp)) { val = 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)) { snprintf(dbuf, sizeof(dbuf), _("Invalid date %u"), dateint); dbuf[sizeof(dbuf)-1] = '\0'; } 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; } /** * Format dependency flags for display. * @param td tag data container * @return formatted string */ static char * depflagsFormat(rpmtd td) { char * val = NULL; if (rpmtdClass(td) != RPM_NUMERIC_CLASS) { val = xstrdup(_("(not a number)")); } else { 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; } static char * depflag_strongFormat(rpmtd td) { char * val = NULL; if (rpmtdClass(td) != RPM_NUMERIC_CLASS) { val = xstrdup(_("(not a number)")); } else { uint64_t anint = rpmtdGetNumber(td); val = xstrdup(anint & RPMSENSE_STRONG ? "strong" : ""); } return val; } /** * Return tag container array size. * @param td tag data container * @return formatted string */ static char * arraysizeFormat(rpmtd td) { char *val = NULL; rasprintf(&val, "%u", rpmtdCount(td)); return val; } static char * fstateFormat(rpmtd td) { 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; } val = xstrdup(str); } return val; } static char * verifyFlags(rpmtd td, const char *pad) { char * val = NULL; if (rpmtdClass(td) != RPM_NUMERIC_CLASS) { val = xstrdup(_("(not a number)")); } else { val = rpmVerifyString(rpmtdGetNumber(td), pad); } return val; } static char * vflagsFormat(rpmtd td) { return verifyFlags(td, ""); } static char * fstatusFormat(rpmtd td) { return verifyFlags(td, "."); } static char * expandFormat(rpmtd td) { char *val = NULL; if (rpmtdClass(td) != RPM_STRING_CLASS) { val = xstrdup(_("(not a string)")); } else { val = rpmExpand(td->data, NULL); } 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 }, { RPMTD_FORMAT_DEPFLAG_STRONG, "depflag_strong", depflag_strongFormat }, { -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; }