Sanitize python object -> tag number exception handling
[platform/upstream/rpm.git] / lib / formats.c
1 /** \ingroup header
2  * \file lib/formats.c
3  */
4
5 #include "system.h"
6
7 #include <inttypes.h>
8
9 #include <rpm/rpmtypes.h>
10 #include <rpm/rpmtd.h>
11 #include <rpm/rpmds.h>
12 #include <rpm/rpmfi.h>
13 #include <rpm/rpmstring.h>
14
15 #include "rpmio/digest.h"
16 #include "lib/manifest.h"
17
18 #include "debug.h"
19
20 /** \ingroup header
21  * Define header tag output formats.
22  */
23
24 struct headerFormatFunc_s {
25     rpmtdFormats fmt;   /*!< Value of extension */
26     const char *name;   /*!< Name of extension. */
27     void *func;         /*!< Pointer to formatter function. */  
28 };
29
30 /* forward declarations */
31 static const struct headerFormatFunc_s rpmHeaderFormats[];
32
33 void *rpmHeaderFormatFuncByName(const char *fmt);
34 void *rpmHeaderFormatFuncByValue(rpmtdFormats fmt);
35
36 /**
37  * barebones string representation with no extra formatting
38  * @param td            tag data container
39  * @param formatPrefix  sprintf format string
40  * @return              formatted string
41  */
42 static char * stringFormat(rpmtd td, char *formatPrefix)
43 {
44     const char *str = NULL;
45     char *val = NULL, *buf = NULL;
46
47     switch (rpmtdClass(td)) {
48         case RPM_NUMERIC_CLASS:
49             strcat(formatPrefix, PRIu64);
50             rasprintf(&val, formatPrefix, rpmtdGetNumber(td));
51             break;
52         case RPM_STRING_CLASS:
53             str = rpmtdGetString(td);
54             strcat(formatPrefix, "s");
55             rasprintf(&val, formatPrefix, str);
56             break;
57         case RPM_BINARY_CLASS:
58             buf = pgpHexStr(td->data, td->count);
59             strcat(formatPrefix, "s");
60             rasprintf(&val, formatPrefix, buf);
61             free(buf);
62             break;
63         default:
64             val = xstrdup("(unknown type)");
65             break;
66     }
67     return val;
68 }
69
70 static char * numFormat(rpmtd td, char * formatPrefix, char *format)
71 {
72     char * val = NULL;
73
74     if (rpmtdClass(td) != RPM_NUMERIC_CLASS) {
75         val = xstrdup(_("(not a number)"));
76     } else {
77         strcat(formatPrefix, format);
78         rasprintf(&val, formatPrefix, rpmtdGetNumber(td));
79     }
80
81     return val;
82 }
83 /**
84  * octalFormat.
85  * @param td            tag data container
86  * @param formatPrefix  sprintf format string
87  * @return              formatted string
88  */
89 static char * octalFormat(rpmtd td, char * formatPrefix)
90 {
91     return numFormat(td, formatPrefix, "o");
92 }
93
94 /**
95  * hexFormat.
96  * @param td            tag data container
97  * @param formatPrefix  sprintf format string
98  * @return              formatted string
99  */
100 static char * hexFormat(rpmtd td, char * formatPrefix)
101 {
102     return numFormat(td, formatPrefix, "x");
103 }
104
105 /**
106  * @param td            tag data container
107  * @param formatPrefix  sprintf format string
108  * @return              formatted string
109  */
110 static char * realDateFormat(rpmtd td, char * formatPrefix,
111                 const char * strftimeFormat)
112 {
113     char * val = NULL;
114
115     if (rpmtdClass(td) != RPM_NUMERIC_CLASS) {
116         val = xstrdup(_("(not a number)"));
117     } else {
118         struct tm * tstruct;
119         char buf[50];
120         time_t dateint = rpmtdGetNumber(td);
121         tstruct = localtime(&dateint);
122
123         strcat(formatPrefix, "s");
124
125         buf[0] = '\0';
126         if (tstruct)
127             (void) strftime(buf, sizeof(buf) - 1, strftimeFormat, tstruct);
128         rasprintf(&val, formatPrefix, buf);
129     }
130
131     return val;
132 }
133
134 /**
135  * Format a date.
136  * @param td            tag data container
137  * @param formatPrefix  sprintf format string
138  * @return              formatted string
139  */
140 static char * dateFormat(rpmtd td, char * formatPrefix)
141 {
142     return realDateFormat(td, formatPrefix, _("%c"));
143 }
144
145 /**
146  * Format a day.
147  * @param td            tag data container
148  * @param formatPrefix  sprintf format string
149  * @return              formatted string
150  */
151 static char * dayFormat(rpmtd td, char * formatPrefix)
152 {
153     return realDateFormat(td, formatPrefix, _("%a %b %d %Y"));
154 }
155
156 /**
157  * Return shell escape formatted data.
158  * @param td            tag data container
159  * @param formatPrefix  sprintf format string
160  * @return              formatted string
161  */
162 static char * shescapeFormat(rpmtd td, char * formatPrefix)
163 {
164     char * result = NULL, * dst, * src;
165
166     if (rpmtdClass(td) == RPM_NUMERIC_CLASS) {
167         strcat(formatPrefix, PRIu64);
168         rasprintf(&result, formatPrefix, rpmtdGetNumber(td));
169     } else {
170         char *buf = NULL;
171         strcat(formatPrefix, "s");
172         rasprintf(&buf, formatPrefix, rpmtdGetString(td));
173
174         result = dst = xmalloc(strlen(buf) * 4 + 3);
175         *dst++ = '\'';
176         for (src = buf; *src != '\0'; src++) {
177             if (*src == '\'') {
178                 *dst++ = '\'';
179                 *dst++ = '\\';
180                 *dst++ = '\'';
181                 *dst++ = '\'';
182             } else {
183                 *dst++ = *src;
184             }
185         }
186         *dst++ = '\'';
187         *dst = '\0';
188         free(buf);
189     }
190
191     return result;
192 }
193
194
195 /**
196  * Identify type of trigger.
197  * @param td            tag data container
198  * @param formatPrefix  sprintf format string
199  * @return              formatted string
200  */
201 static char * triggertypeFormat(rpmtd td, char * formatPrefix)
202 {
203     char * val;
204
205     if (rpmtdClass(td) != RPM_NUMERIC_CLASS) {
206         val = xstrdup(_("(not a number)"));
207     } else {
208         uint64_t item = rpmtdGetNumber(td);
209         if (item & RPMSENSE_TRIGGERPREIN)
210             val = xstrdup("prein");
211         else if (item & RPMSENSE_TRIGGERIN)
212             val = xstrdup("in");
213         else if (item & RPMSENSE_TRIGGERUN)
214             val = xstrdup("un");
215         else if (item & RPMSENSE_TRIGGERPOSTUN)
216             val = xstrdup("postun");
217         else
218             val = xstrdup("");
219     }
220     return val;
221 }
222
223 /**
224  * Identify type of dependency.
225  * @param td            tag data container
226  * @param formatPrefix  sprintf format string
227  * @return              formatted string
228  */
229 static char * deptypeFormat(rpmtd td, char * formatPrefix)
230 {
231     char *val = NULL;
232     if (rpmtdClass(td) != RPM_NUMERIC_CLASS) {
233         val = xstrdup(_("(not a number)"));
234     } else {
235         ARGV_t sdeps = NULL;
236         uint64_t item = rpmtdGetNumber(td);
237
238         if (item & RPMSENSE_SCRIPT_PRE)
239             argvAdd(&sdeps, "pre");
240         if (item & RPMSENSE_SCRIPT_POST)
241             argvAdd(&sdeps, "post");
242         if (item & RPMSENSE_SCRIPT_PREUN)
243             argvAdd(&sdeps, "preun");
244         if (item & RPMSENSE_SCRIPT_POSTUN)
245             argvAdd(&sdeps, "postun");
246         if (item & RPMSENSE_SCRIPT_VERIFY)
247             argvAdd(&sdeps, "verify");
248
249         if (sdeps) {
250             val = argvJoin(sdeps, ",");
251             argvFree(sdeps);
252         } else {
253             if (item & RPMSENSE_RPMLIB)
254                 val = xstrdup("rpmlib");
255             else if (item & RPMSENSE_INTERP)
256                 val = xstrdup("interp");
257             else if ((item & RPMSENSE_FIND_REQUIRES) || 
258                      (item & RPMSENSE_FIND_PROVIDES))
259                 val = xstrdup("auto");
260             else if (item & RPMSENSE_PREREQ)
261                 val = xstrdup("prereq");
262             else
263                 val = xstrdup("manual");
264         }
265     }
266     return val;
267 }
268
269 /**
270  * Format file permissions for display.
271  * @param td            tag data container
272  * @param formatPrefix  sprintf format string
273  * @return              formatted string
274  */
275 static char * permsFormat(rpmtd td, char * formatPrefix)
276 {
277     char * val = NULL;
278     char * buf;
279
280     if (rpmtdClass(td) != RPM_NUMERIC_CLASS) {
281         val = xstrdup(_("(not a number)"));
282     } else {
283         strcat(formatPrefix, "s");
284         buf = rpmPermsString(rpmtdGetNumber(td));
285         rasprintf(&val, formatPrefix, buf);
286         buf = _free(buf);
287     }
288
289     return val;
290 }
291
292 /**
293  * Format file flags for display.
294  * @param td            tag data container
295  * @param formatPrefix  sprintf format string
296  * @return              formatted string
297  */
298 static char * fflagsFormat(rpmtd td, char * formatPrefix)
299 {
300     char * val = NULL;
301     char buf[15];
302
303     if (rpmtdClass(td) != RPM_NUMERIC_CLASS) {
304         val = xstrdup(_("(not a number)"));
305     } else {
306         uint64_t anint = rpmtdGetNumber(td);
307         buf[0] = '\0';
308         if (anint & RPMFILE_DOC)
309             strcat(buf, "d");
310         if (anint & RPMFILE_CONFIG)
311             strcat(buf, "c");
312         if (anint & RPMFILE_SPECFILE)
313             strcat(buf, "s");
314         if (anint & RPMFILE_MISSINGOK)
315             strcat(buf, "m");
316         if (anint & RPMFILE_NOREPLACE)
317             strcat(buf, "n");
318         if (anint & RPMFILE_GHOST)
319             strcat(buf, "g");
320         if (anint & RPMFILE_LICENSE)
321             strcat(buf, "l");
322         if (anint & RPMFILE_README)
323             strcat(buf, "r");
324
325         strcat(formatPrefix, "s");
326         rasprintf(&val, formatPrefix, buf);
327     }
328
329     return val;
330 }
331
332 /**
333  * Wrap a pubkey in ascii armor for display.
334  * @todo Permit selectable display formats (i.e. binary).
335  * @param td            tag data container
336  * @param formatPrefix  sprintf format string
337  * @return              formatted string
338  */
339 static char * armorFormat(rpmtd td, char * formatPrefix)
340 {
341     const char * enc;
342     const unsigned char * s;
343     unsigned char * bs = NULL;
344     char *val;
345     size_t ns;
346     int atype;
347
348     switch (rpmtdType(td)) {
349     case RPM_BIN_TYPE:
350         s = td->data;
351         /* XXX HACK ALERT: element field abused as no. bytes of binary data. */
352         ns = td->count;
353         atype = PGPARMOR_SIGNATURE;     /* XXX check pkt for signature */
354         break;
355     case RPM_STRING_TYPE:
356     case RPM_STRING_ARRAY_TYPE:
357         enc = rpmtdGetString(td);
358         if (b64decode(enc, (void **)&bs, &ns))
359             return xstrdup(_("(not base64)"));
360         s = bs;
361         atype = PGPARMOR_PUBKEY;        /* XXX check pkt for pubkey */
362         break;
363     case RPM_NULL_TYPE:
364     case RPM_CHAR_TYPE:
365     case RPM_INT8_TYPE:
366     case RPM_INT16_TYPE:
367     case RPM_INT32_TYPE:
368     case RPM_INT64_TYPE:
369     case RPM_I18NSTRING_TYPE:
370     default:
371         return xstrdup(_("(invalid type)"));
372         break;
373     }
374
375     /* XXX this doesn't use padding directly, assumes enough slop in retval. */
376     val = pgpArmorWrap(atype, s, ns);
377     if (atype == PGPARMOR_PUBKEY) {
378         free(bs);
379     }
380     return val;
381 }
382
383 /**
384  * Encode binary data in base64 for display.
385  * @todo Permit selectable display formats (i.e. binary).
386  * @param td            tag data container
387  * @param formatPrefix  sprintf format string
388  * @return              formatted string
389  */
390 static char * base64Format(rpmtd td, char * formatPrefix)
391 {
392     char * val = NULL;
393
394     if (rpmtdType(td) != RPM_BIN_TYPE) {
395         val = xstrdup(_("(not a blob)"));
396     } else {
397         char * enc;
398         if ((enc = b64encode(td->data, td->count, -1)) != NULL) {
399             strcat(formatPrefix, "s");
400             rasprintf(&val, formatPrefix, enc ? enc : "");
401             free(enc);
402         }
403     }
404
405     return val;
406 }
407
408 /**
409  * Wrap tag data in simple header xml markup.
410  * @param td            tag data container
411  * @param formatPrefix  sprintf format string
412  * @return              formatted string
413  */
414 static char * xmlFormat(rpmtd td, char * formatPrefix)
415 {
416     const char *xtag = NULL;
417     char *val = NULL;
418     char *s = NULL;
419     rpmtdFormats fmt = RPMTD_FORMAT_STRING;
420
421     switch (rpmtdClass(td)) {
422     case RPM_STRING_CLASS:
423         xtag = "string";
424         break;
425     case RPM_BINARY_CLASS:
426         fmt = RPMTD_FORMAT_BASE64;
427         xtag = "base64";
428         break;
429     case RPM_NUMERIC_CLASS:
430         xtag = "integer";
431         break;
432     case RPM_NULL_TYPE:
433     default:
434         return xstrdup(_("(invalid xml type)"));
435         break;
436     }
437
438     /* XXX TODO: handle errors */
439     s = rpmtdFormat(td, fmt, NULL);
440
441     if (s[0] == '\0') {
442         val = rstrscat(NULL, "\t<", xtag, "/>", NULL);
443     } else {
444         char *new_s = NULL;
445         size_t i, s_size = strlen(s);
446         
447         for (i=0; i<s_size; i++) {
448             switch (s[i]) {
449                 case '<':       rstrcat(&new_s, "&lt;");        break;
450                 case '>':       rstrcat(&new_s, "&gt;");        break;
451                 case '&':       rstrcat(&new_s, "&amp;");       break;
452                 default: {
453                     char c[2] = " ";
454                     *c = s[i];
455                     rstrcat(&new_s, c);
456                     break;
457                 }
458             }
459         }
460
461         val = rstrscat(NULL, "\t<", xtag, ">", new_s, "</", xtag, ">", NULL);
462         free(new_s);
463     }
464     free(s);
465
466     strcat(formatPrefix, "s");
467     return val;
468 }
469
470 /**
471  * Display signature fingerprint and time.
472  * @param td            tag data container
473  * @param formatPrefix  sprintf format string
474  * @return              formatted string
475  */
476 static char * pgpsigFormat(rpmtd td, char * formatPrefix)
477 {
478     char * val, * t;
479
480     if (rpmtdType(td) != RPM_BIN_TYPE) {
481         val = xstrdup(_("(not a blob)"));
482     } else {
483         const uint8_t * pkt = td->data;
484         size_t pktlen = 0;
485         unsigned int v = *pkt;
486         pgpTag tag = 0;
487         size_t plen;
488         size_t hlen = 0;
489
490         if (v & 0x80) {
491             if (v & 0x40) {
492                 tag = (v & 0x3f);
493                 plen = pgpLen(pkt+1, &hlen);
494             } else {
495                 tag = (v >> 2) & 0xf;
496                 plen = (1 << (v & 0x3));
497                 hlen = pgpGrab(pkt+1, plen);
498             }
499         
500             pktlen = 1 + plen + hlen;
501         }
502
503         if (pktlen == 0 || tag != PGPTAG_SIGNATURE) {
504             val = xstrdup(_("(not an OpenPGP signature)"));
505         } else {
506             pgpDig dig = pgpNewDig();
507             pgpDigParams sigp = &dig->signature;
508             size_t nb = 0;
509             char *tempstr = NULL;
510
511             (void) pgpPrtPkts(pkt, pktlen, dig, 0);
512
513             val = NULL;
514         again:
515             nb += 100;
516             val = t = xrealloc(val, nb + 1);
517
518             switch (sigp->pubkey_algo) {
519             case PGPPUBKEYALGO_DSA:
520                 t = stpcpy(t, "DSA");
521                 break;
522             case PGPPUBKEYALGO_RSA:
523                 t = stpcpy(t, "RSA");
524                 break;
525             default:
526                 (void) snprintf(t, nb - (t - val), "%d", sigp->pubkey_algo);
527                 t += strlen(t);
528                 break;
529             }
530             if (t + 5 >= val + nb)
531                 goto again;
532             *t++ = '/';
533             switch (sigp->hash_algo) {
534             case PGPHASHALGO_MD5:
535                 t = stpcpy(t, "MD5");
536                 break;
537             case PGPHASHALGO_SHA1:
538                 t = stpcpy(t, "SHA1");
539                 break;
540             default:
541                 (void) snprintf(t, nb - (t - val), "%d", sigp->hash_algo);
542                 t += strlen(t);
543                 break;
544             }
545             if (t + strlen (", ") + 1 >= val + nb)
546                 goto again;
547
548             t = stpcpy(t, ", ");
549
550             /* this is important if sizeof(int32_t) ! sizeof(time_t) */
551             {   time_t dateint = pgpGrab(sigp->time, sizeof(sigp->time));
552                 struct tm * tstruct = localtime(&dateint);
553                 if (tstruct)
554                     (void) strftime(t, (nb - (t - val)), "%c", tstruct);
555             }
556             t += strlen(t);
557             if (t + strlen (", Key ID ") + 1 >= val + nb)
558                 goto again;
559             t = stpcpy(t, ", Key ID ");
560             tempstr = pgpHexStr(sigp->signid, sizeof(sigp->signid));
561             if (t + strlen (tempstr) > val + nb)
562                 goto again;
563             t = stpcpy(t, tempstr);
564             free(tempstr);
565
566             dig = pgpFreeDig(dig);
567         }
568     }
569
570     return val;
571 }
572
573 /**
574  * Format dependency flags for display.
575  * @param td            tag data container
576  * @param formatPrefix  sprintf format string
577  * @return              formatted string
578  */
579 static char * depflagsFormat(rpmtd td, char * formatPrefix)
580 {
581     char * val = NULL;
582
583     if (rpmtdClass(td) != RPM_NUMERIC_CLASS) {
584         val = xstrdup(_("(not a number)"));
585     } else {
586         uint64_t anint = rpmtdGetNumber(td);
587         char buf[10];
588         buf[0] = '\0';
589
590         if (anint & RPMSENSE_LESS) 
591             strcat(buf, "<");
592         if (anint & RPMSENSE_GREATER)
593             strcat(buf, ">");
594         if (anint & RPMSENSE_EQUAL)
595             strcat(buf, "=");
596
597         strcat(formatPrefix, "s");
598         rasprintf(&val, formatPrefix, buf);
599     }
600
601     return val;
602 }
603
604 /**
605  * Return tag container array size.
606  * @param td            tag data container
607  * @param formatPrefix  sprintf format string
608  * @return              formatted string
609  */
610 static char * arraysizeFormat(rpmtd td, char * formatPrefix)
611 {
612     char *val = NULL;
613     strcat(formatPrefix, "u");
614     rasprintf(&val, formatPrefix, rpmtdCount(td));
615     return val;
616 }
617
618 void *rpmHeaderFormatFuncByName(const char *fmt)
619 {
620     const struct headerFormatFunc_s * ext;
621     void *func = NULL;
622
623     for (ext = rpmHeaderFormats; ext->name != NULL; ext++) {
624         if (rstreq(ext->name, fmt)) {
625             func = ext->func;
626             break;
627         }
628     }
629     return func;
630 }
631
632 void *rpmHeaderFormatFuncByValue(rpmtdFormats fmt)
633 {
634     const struct headerFormatFunc_s * ext;
635     void *func = NULL;
636
637     for (ext = rpmHeaderFormats; ext->name != NULL; ext++) {
638         if (fmt == ext->fmt) {
639             func = ext->func;
640             break;
641         }
642     }
643     return func;
644 }
645
646 static const struct headerFormatFunc_s rpmHeaderFormats[] = {
647     { RPMTD_FORMAT_STRING,      "string",       stringFormat },
648     { RPMTD_FORMAT_ARMOR,       "armor",        armorFormat },
649     { RPMTD_FORMAT_BASE64,      "base64",       base64Format },
650     { RPMTD_FORMAT_PGPSIG,      "pgpsig",       pgpsigFormat },
651     { RPMTD_FORMAT_DEPFLAGS,    "depflags",     depflagsFormat },
652     { RPMTD_FORMAT_DEPTYPE,     "deptype",      deptypeFormat },
653     { RPMTD_FORMAT_FFLAGS,      "fflags",       fflagsFormat },
654     { RPMTD_FORMAT_PERMS,       "perms",        permsFormat },
655     { RPMTD_FORMAT_PERMS,       "permissions",  permsFormat },
656     { RPMTD_FORMAT_TRIGGERTYPE, "triggertype",  triggertypeFormat },
657     { RPMTD_FORMAT_XML,         "xml",          xmlFormat },
658     { RPMTD_FORMAT_OCTAL,       "octal",        octalFormat },
659     { RPMTD_FORMAT_HEX,         "hex",          hexFormat },
660     { RPMTD_FORMAT_DATE,        "date",         dateFormat },
661     { RPMTD_FORMAT_DAY,         "day",          dayFormat },
662     { RPMTD_FORMAT_SHESCAPE,    "shescape",     shescapeFormat },
663     { RPMTD_FORMAT_ARRAYSIZE,   "arraysize",    arraysizeFormat },
664     { -1,                       NULL,           NULL }
665 };