Sanitize python object -> tag number exception handling
[platform/upstream/rpm.git] / lib / query.c
1 /** \ingroup rpmcli
2  * \file lib/query.c
3  * Display tag values from package metadata.
4  */
5
6 #include "system.h"
7
8 #include <inttypes.h>
9
10 #include <rpm/rpmcli.h>
11 #include <rpm/header.h>
12 #include <rpm/rpmdb.h>
13 #include <rpm/rpmfi.h>
14 #include <rpm/rpmgi.h>
15 #include <rpm/rpmts.h>
16 #include <rpm/rpmlog.h>
17 #include <rpm/rpmfileutil.h>    /* rpmCleanPath */
18
19 #include "lib/manifest.h"
20
21 #include "debug.h"
22
23
24 /**
25  */
26 static void printFileInfo(const char * name,
27                           rpm_loff_t size, unsigned short mode,
28                           unsigned int mtime,
29                           unsigned short rdev, unsigned int nlink,
30                           const char * owner, const char * group,
31                           const char * linkto)
32 {
33     char sizefield[21];
34     char ownerfield[8+1], groupfield[8+1];
35     char timefield[100];
36     time_t when = mtime;  /* important if sizeof(int32_t) ! sizeof(time_t) */
37     struct tm * tm;
38     static time_t now;
39     static struct tm nowtm;
40     char * perms = rpmPermsString(mode);
41     char *link = NULL;
42
43     /* On first call, grab snapshot of now */
44     if (now == 0) {
45         now = time(NULL);
46         tm = localtime(&now);
47         if (tm) nowtm = *tm;    /* structure assignment */
48     }
49
50     rstrlcpy(ownerfield, owner, sizeof(ownerfield));
51     rstrlcpy(groupfield, group, sizeof(groupfield));
52
53     /* this is normally right */
54     snprintf(sizefield, sizeof(sizefield), "%20" PRIu64, size);
55
56     /* this knows too much about dev_t */
57
58     if (S_ISLNK(mode)) {
59         rasprintf(&link, "%s -> %s", name, linkto);
60     } else if (S_ISCHR(mode)) {
61         perms[0] = 'c';
62         snprintf(sizefield, sizeof(sizefield), "%3u, %3u", ((unsigned)(rdev >> 8) & 0xff),
63                         ((unsigned)rdev & 0xff));
64     } else if (S_ISBLK(mode)) {
65         perms[0] = 'b';
66         snprintf(sizefield, sizeof(sizefield), "%3u, %3u", ((unsigned)(rdev >> 8) & 0xff),
67                         ((unsigned)rdev & 0xff));
68     }
69
70     /* Convert file mtime to display format */
71     tm = localtime(&when);
72     timefield[0] = '\0';
73     if (tm != NULL)
74     {   const char *fmt;
75         if (now > when + 6L * 30L * 24L * 60L * 60L ||  /* Old. */
76             now < when - 60L * 60L)                     /* In the future.  */
77         {
78         /* The file is fairly old or in the future.
79          * POSIX says the cutoff is 6 months old;
80          * approximate this by 6*30 days.
81          * Allow a 1 hour slop factor for what is considered "the future",
82          * to allow for NFS server/client clock disagreement.
83          * Show the year instead of the time of day.
84          */        
85             fmt = "%b %e  %Y";
86         } else {
87             fmt = "%b %e %H:%M";
88         }
89         (void)strftime(timefield, sizeof(timefield) - 1, fmt, tm);
90     }
91
92     rpmlog(RPMLOG_NOTICE, "%s %4d %-8s%-8s %10s %s %s\n", perms,
93         (int)nlink, ownerfield, groupfield, sizefield, timefield, 
94         link ? link : name);
95     free(perms);
96     free(link);
97 }
98
99 int showQueryPackage(QVA_t qva, rpmts ts, Header h)
100 {
101     rpmfi fi = NULL;
102     rpmfiFlags fiflags =  (RPMFI_NOHEADER | RPMFI_FLAGS_QUERY);
103     int rc = 0;         /* XXX FIXME: need real return code */
104
105     if (qva->qva_queryFormat != NULL) {
106         const char *errstr;
107         char *str = headerFormat(h, qva->qva_queryFormat, &errstr);
108
109         if ( str != NULL ) {
110             rpmlog(RPMLOG_NOTICE, "%s", str);
111             free(str);
112         } else {
113             rpmlog(RPMLOG_ERR, _("incorrect format: %s\n"), errstr);
114         }
115     }
116
117     if (!(qva->qva_flags & QUERY_FOR_LIST))
118         goto exit;
119
120     if (!(qva->qva_flags & QUERY_FOR_DUMPFILES))
121         fiflags |= RPMFI_NOFILEDIGESTS;
122
123     fi = rpmfiNew(ts, h, RPMTAG_BASENAMES, fiflags);
124     if (rpmfiFC(fi) <= 0) {
125         rpmlog(RPMLOG_NOTICE, _("(contains no files)\n"));
126         goto exit;
127     }
128
129     fi = rpmfiInit(fi, 0);
130     while (rpmfiNext(fi) >= 0) {
131         rpmfileAttrs fflags = rpmfiFFlags(fi);
132         rpm_mode_t fmode = rpmfiFMode(fi);
133         rpm_rdev_t frdev = rpmfiFRdev(fi);
134         rpm_time_t fmtime = rpmfiFMtime(fi);
135         rpmfileState fstate = rpmfiFState(fi);
136         rpm_loff_t fsize = rpmfiFSize(fi);
137         const char *fn = rpmfiFN(fi);
138         const char *fuser = rpmfiFUser(fi);
139         const char *fgroup = rpmfiFGroup(fi);
140         const char *flink = rpmfiFLink(fi);
141         uint32_t fnlink = rpmfiFNlink(fi);
142         char *buf = NULL;
143
144         /* If querying only docs, skip non-doc files. */
145         if ((qva->qva_flags & QUERY_FOR_DOCS) && !(fflags & RPMFILE_DOC))
146             continue;
147
148         /* If querying only configs, skip non-config files. */
149         if ((qva->qva_flags & QUERY_FOR_CONFIG) && !(fflags & RPMFILE_CONFIG))
150             continue;
151
152         /* If not querying %ghost, skip ghost files. */
153         if ((qva->qva_fflags & RPMFILE_GHOST) && (fflags & RPMFILE_GHOST))
154             continue;
155
156         if (qva->qva_flags & QUERY_FOR_STATE) {
157             switch (fstate) {
158             case RPMFILE_STATE_NORMAL:
159                 rstrcat(&buf, _("normal        "));
160                 break;
161             case RPMFILE_STATE_REPLACED:
162                 rstrcat(&buf, _("replaced      "));
163                 break;
164             case RPMFILE_STATE_NOTINSTALLED:
165                 rstrcat(&buf, _("not installed "));
166                 break;
167             case RPMFILE_STATE_NETSHARED:
168                 rstrcat(&buf, _("net shared    "));
169                 break;
170             case RPMFILE_STATE_WRONGCOLOR:
171                 rstrcat(&buf, _("wrong color   "));
172                 break;
173             case RPMFILE_STATE_MISSING:
174                 rstrcat(&buf, _("(no state)    "));
175                 break;
176             default:
177                 rasprintf(&buf, _("(unknown %3d) "), fstate);
178                 break;
179             }
180         }
181
182         if (qva->qva_flags & QUERY_FOR_DUMPFILES) {
183             char *add, *fdigest;
184             fdigest = rpmfiFDigestHex(fi, NULL);
185             rasprintf(&add, "%s %" PRIu64 " %d %s 0%o ", 
186                       fn, fsize, fmtime, fdigest ? fdigest : "", fmode);
187             rstrcat(&buf, add);
188             free(add);
189             free(fdigest);
190
191             if (fuser && fgroup) {
192                 rasprintf(&add, "%s %s", fuser, fgroup);
193                 rstrcat(&buf, add);
194                 free(add);
195             } else {
196                 rpmlog(RPMLOG_ERR,
197                         _("package has not file owner/group lists\n"));
198             }
199
200             rasprintf(&add, " %s %s %u %s",
201                                  fflags & RPMFILE_CONFIG ? "1" : "0",
202                                  fflags & RPMFILE_DOC ? "1" : "0",
203                                  frdev,
204                                  (flink && *flink ? flink : "X"));
205             rpmlog(RPMLOG_NOTICE, "%s%s\n", buf, add);
206             free(add);
207         } else
208         if (!rpmIsVerbose()) {
209             rpmlog(RPMLOG_NOTICE, "%s%s\n", buf ? buf : "", fn);
210         }
211         else {
212
213             /* XXX Adjust directory link count and size for display output. */
214             if (S_ISDIR(fmode)) {
215                 fnlink++;
216                 fsize = 0;
217             }
218
219             if (fuser && fgroup) {
220                 if (buf) {
221                     rpmlog(RPMLOG_NOTICE, "%s", buf);
222                 }
223                 printFileInfo(fn, fsize, fmode, fmtime, frdev, fnlink,
224                                         fuser, fgroup, flink);
225             } else {
226                 rpmlog(RPMLOG_ERR,
227                         _("package has neither file owner or id lists\n"));
228             }
229         }
230         free(buf);
231     }
232
233     rc = 0;
234
235 exit:
236     fi = rpmfiFree(fi);
237     return rc;
238 }
239
240 void rpmDisplayQueryTags(FILE * fp)
241 {
242     static const char * const tagTypeNames[] = {
243         "", "char", "int8", "int16", "int32", "int64",
244         "string", "blob", "argv", "i18nstring"
245     };
246     const char *tname, *sname;
247     rpmtd names = rpmtdNew();
248     (void) rpmTagGetNames(names, 1);
249
250     while ((tname = rpmtdNextString(names))) {
251         sname = tname + strlen("RPMTAG_");
252         if (rpmIsVerbose()) {
253             rpmTag tag = rpmTagGetValue(sname);
254             rpmTagType type = rpmTagGetType(tag) & RPM_MASK_TYPE;
255             fprintf(fp, "%-20s %6d", sname, tag);
256             if (type > RPM_NULL_TYPE && type <= RPM_MAX_TYPE)
257                 fprintf(fp, " %s", tagTypeNames[type]);
258         } else {
259             fprintf(fp, "%s", sname);
260         }
261         fprintf(fp, "\n");
262     }
263     rpmtdFreeData(names);
264     rpmtdFree(names);
265 }
266
267 static int rpmgiShowMatches(QVA_t qva, rpmts ts)
268 {
269     rpmgi gi = qva->qva_gi;
270     int ec = 0;
271
272     while (rpmgiNext(gi) == RPMRC_OK) {
273         Header h;
274         int rc;
275
276         rpmdbCheckSignals();
277         h = rpmgiHeader(gi);
278         if (h == NULL)          /* XXX perhaps stricter break instead? */
279             continue;
280         if ((rc = qva->qva_showPackage(qva, ts, h)) != 0)
281             ec = rc;
282     }
283     return ec + rpmgiNumErrors(gi);
284 }
285
286 int rpmcliShowMatches(QVA_t qva, rpmts ts)
287 {
288     Header h;
289     int ec = 0;
290
291     while ((h = rpmdbNextIterator(qva->qva_mi)) != NULL) {
292         int rc;
293         rpmdbCheckSignals();
294         if ((rc = qva->qva_showPackage(qva, ts, h)) != 0)
295             ec = rc;
296     }
297     qva->qva_mi = rpmdbFreeIterator(qva->qva_mi);
298     return ec;
299 }
300
301 int rpmQueryVerify(QVA_t qva, rpmts ts, const char * arg)
302 {
303     int res = 0;
304     const char * s;
305     int i;
306     int provides_checked = 0;
307
308     (void) rpmdbCheckSignals();
309
310     if (qva->qva_showPackage == NULL)
311         return 1;
312
313     switch (qva->qva_source) {
314     case RPMQV_RPM:
315     case RPMQV_ALL:
316     case RPMQV_HDLIST:
317     case RPMQV_FTSWALK:
318         res = rpmgiShowMatches(qva, ts);
319         break;
320
321     case RPMQV_SPECFILE:
322         res = ((qva->qva_specQuery != NULL)
323                 ? qva->qva_specQuery(ts, qva, arg) : 1);
324         break;
325
326     case RPMQV_GROUP:
327         qva->qva_mi = rpmtsInitIterator(ts, RPMTAG_GROUP, arg, 0);
328         if (qva->qva_mi == NULL) {
329             rpmlog(RPMLOG_NOTICE,
330                 _("group %s does not contain any packages\n"), arg);
331             res = 1;
332         } else
333             res = rpmcliShowMatches(qva, ts);
334         break;
335
336     case RPMQV_TRIGGEREDBY:
337         qva->qva_mi = rpmtsInitIterator(ts, RPMTAG_TRIGGERNAME, arg, 0);
338         if (qva->qva_mi == NULL) {
339             rpmlog(RPMLOG_NOTICE, _("no package triggers %s\n"), arg);
340             res = 1;
341         } else
342             res = rpmcliShowMatches(qva, ts);
343         break;
344
345     case RPMQV_PKGID:
346     {   unsigned char MD5[16];
347         unsigned char * t;
348
349         for (i = 0, s = arg; *s && isxdigit(*s); s++, i++)
350             {};
351         if (i != 32) {
352             rpmlog(RPMLOG_ERR, _("malformed %s: %s\n"), "pkgid", arg);
353             return 1;
354         }
355
356         MD5[0] = '\0';
357         for (i = 0, t = MD5, s = arg; i < 16; i++, t++, s += 2)
358             *t = (rnibble(s[0]) << 4) | rnibble(s[1]);
359         
360         qva->qva_mi = rpmtsInitIterator(ts, RPMTAG_SIGMD5, MD5, sizeof(MD5));
361         if (qva->qva_mi == NULL) {
362             rpmlog(RPMLOG_NOTICE, _("no package matches %s: %s\n"),
363                         "pkgid", arg);
364             res = 1;
365         } else
366             res = rpmcliShowMatches(qva, ts);
367     }   break;
368
369     case RPMQV_HDRID:
370         for (i = 0, s = arg; *s && isxdigit(*s); s++, i++)
371             {};
372         if (i != 40) {
373             rpmlog(RPMLOG_ERR, _("malformed %s: %s\n"), "hdrid", arg);
374             return 1;
375         }
376
377         qva->qva_mi = rpmtsInitIterator(ts, RPMTAG_SHA1HEADER, arg, 0);
378         if (qva->qva_mi == NULL) {
379             rpmlog(RPMLOG_NOTICE, _("no package matches %s: %s\n"),
380                         "hdrid", arg);
381             res = 1;
382         } else
383             res = rpmcliShowMatches(qva, ts);
384         break;
385
386     case RPMQV_FILEID:
387     {   unsigned char *digest, *t;
388         size_t diglen;
389
390         for (i = 0, s = arg; *s && isxdigit(*s); s++, i++)
391             {};
392         /* XXX dunno the algorithm yet, just check we're in the ballpark */
393         if (i % 32 != 0 || i < 32 || i > 512) {
394             rpmlog(RPMLOG_ERR, _("malformed %s: %s\n"), "fileid", arg);
395             return 1;
396         }
397
398         diglen = i / 2;
399         digest = t = xcalloc(diglen, sizeof(*digest));
400         for (i = 0, s = arg; i < diglen; i++, t++, s += 2)
401             *t = (rnibble(s[0]) << 4) | rnibble(s[1]);
402
403         qva->qva_mi = rpmtsInitIterator(ts, RPMTAG_FILEDIGESTS, digest, diglen);
404         if (qva->qva_mi == NULL) {
405             rpmlog(RPMLOG_NOTICE, _("no package matches %s: %s\n"),
406                         "fileid", arg);
407             res = 1;
408         } else
409             res = rpmcliShowMatches(qva, ts);
410
411         free(digest);
412     }   break;
413
414     case RPMQV_TID:
415     {   char * end = NULL;
416         rpm_tid_t iid = strtoul(arg, &end, 0);
417
418         if ((*end) || (end == arg) || (iid == UINT_MAX)) {
419             rpmlog(RPMLOG_ERR, _("malformed %s: %s\n"), "tid", arg);
420             return 1;
421         }
422         qva->qva_mi = rpmtsInitIterator(ts, RPMTAG_INSTALLTID, &iid, sizeof(iid));
423         if (qva->qva_mi == NULL) {
424             rpmlog(RPMLOG_NOTICE, _("no package matches %s: %s\n"),
425                         "tid", arg);
426             res = 1;
427         } else
428             res = rpmcliShowMatches(qva, ts);
429     }   break;
430
431     case RPMQV_WHATREQUIRES:
432         qva->qva_mi = rpmtsInitIterator(ts, RPMTAG_REQUIRENAME, arg, 0);
433         if (qva->qva_mi == NULL) {
434             rpmlog(RPMLOG_NOTICE, _("no package requires %s\n"), arg);
435             res = 1;
436         } else
437             res = rpmcliShowMatches(qva, ts);
438         break;
439
440     case RPMQV_WHATPROVIDES:
441         if (arg[0] != '/') {
442             provides_checked = 1;
443             qva->qva_mi = rpmtsInitIterator(ts, RPMTAG_PROVIDENAME, arg, 0);
444             if (qva->qva_mi == NULL) {
445                 rpmlog(RPMLOG_NOTICE, _("no package provides %s\n"), arg);
446                 res = 1;
447             } else
448                 res = rpmcliShowMatches(qva, ts);
449             break;
450         }
451     case RPMQV_PATH:
452     {   char * fn;
453
454         for (s = arg; *s != '\0'; s++)
455             if (!(*s == '.' || *s == '/'))
456                 break;
457
458         if (*s == '\0') {
459             char fnbuf[PATH_MAX];
460             fn = realpath(arg, fnbuf);
461             fn = xstrdup( (fn != NULL ? fn : arg) );
462         } else if (*arg != '/') {
463             char *curDir = rpmGetCwd();
464             fn = (char *) rpmGetPath(curDir, "/", arg, NULL);
465             curDir = _free(curDir);
466         } else
467             fn = xstrdup(arg);
468         (void) rpmCleanPath(fn);
469
470         qva->qva_mi = rpmtsInitIterator(ts, RPMTAG_BASENAMES, fn, 0);
471         if (qva->qva_mi == NULL && !provides_checked)
472             qva->qva_mi = rpmtsInitIterator(ts, RPMTAG_PROVIDENAME, fn, 0);
473
474         if (qva->qva_mi == NULL) {
475             struct stat sb;
476             if (lstat(fn, &sb) != 0)
477                 rpmlog(RPMLOG_ERR, _("file %s: %s\n"), fn, strerror(errno));
478             else
479                 rpmlog(RPMLOG_NOTICE,
480                         _("file %s is not owned by any package\n"), fn);
481             res = 1;
482         } else
483             res = rpmcliShowMatches(qva, ts);
484
485         fn = _free(fn);
486     }   break;
487
488     case RPMQV_DBOFFSET:
489     {   char * end = NULL;
490         unsigned int recOffset = strtoul(arg, &end, 0);
491
492         if ((*end) || (end == arg) || (recOffset == UINT_MAX)) {
493             rpmlog(RPMLOG_ERR, _("invalid package number: %s\n"), arg);
494             return 1;
495         }
496         rpmlog(RPMLOG_DEBUG, "package record number: %u\n", recOffset);
497         /* RPMDBI_PACKAGES */
498         qva->qva_mi = rpmtsInitIterator(ts, RPMDBI_PACKAGES, &recOffset, sizeof(recOffset));
499         if (qva->qva_mi == NULL) {
500             rpmlog(RPMLOG_ERR,
501                 _("record %u could not be read\n"), recOffset);
502             res = 1;
503         } else
504             res = rpmcliShowMatches(qva, ts);
505     }   break;
506
507     case RPMQV_PACKAGE:
508     {
509         int matches = 0;
510         rpmdbMatchIterator mi;
511         mi = rpmtsInitIterator(ts, RPMDBI_LABEL, arg, 0);
512         while (rpmdbNextIterator(mi) != NULL) {
513             matches++;
514         }
515         rpmdbFreeIterator(mi);
516         if (! matches) {
517             rpmlog(RPMLOG_NOTICE, _("package %s is not installed\n"), arg);
518             res = 1;
519         } else {
520             qva->qva_mi = rpmtsInitIterator(ts, RPMDBI_LABEL, arg, 0);
521             res = rpmcliShowMatches(qva, ts);
522         }
523         break;
524     }
525     
526     }
527    
528     return res;
529 }
530
531 static int rpmcliArgIterHelper(rpmts ts, QVA_t qva, rpmTag tag, ARGV_const_t argv, rpmgiFlags gFlgs)
532 {
533     rpmRC rpmrc = RPMRC_NOTFOUND;
534     int ec = 0;
535
536     qva->qva_gi = rpmgiNew(ts, tag, NULL, 0);
537     qva->qva_rc = rpmgiSetArgs(qva->qva_gi, argv, ftsOpts, gFlgs);
538     
539     if (qva->qva_gi != NULL && (rpmgiGetFlags(qva->qva_gi) & RPMGI_TSADD))      /* Load the ts with headers. */
540         while ((rpmrc = rpmgiNext(qva->qva_gi)) == RPMRC_OK)
541             {};
542     if (rpmrc != RPMRC_NOTFOUND) {
543         qva->qva_gi = rpmgiFree(qva->qva_gi);
544         return 1;       /* XXX should be no. of failures. */
545     }
546     /* FIX: argv can be NULL, cast to pass argv array */
547     ec = rpmQueryVerify(qva, ts, (tag == RPMDBI_PACKAGES)? (const char *) argv : NULL);
548     rpmtsEmpty(ts);
549     qva->qva_gi = rpmgiFree(qva->qva_gi);
550     return ec;
551 }
552
553 int rpmcliArgIter(rpmts ts, QVA_t qva, ARGV_const_t argv)
554 {
555     int ec = 0;
556
557     switch (qva->qva_source) {
558     case RPMQV_ALL:
559         ec = rpmcliArgIterHelper(ts, qva, RPMDBI_PACKAGES, argv, RPMGI_NONE);
560         break;
561     case RPMQV_RPM:
562         ec = rpmcliArgIterHelper(ts, qva, RPMDBI_ARGLIST, argv, giFlags);
563         break;
564     case RPMQV_HDLIST:
565         ec = rpmcliArgIterHelper(ts, qva, RPMDBI_HDLIST, argv, giFlags);
566         break;
567     case RPMQV_FTSWALK:
568         if (ftsOpts == 0)
569             ftsOpts = (RPMGI_COMFOLLOW | RPMGI_LOGICAL | RPMGI_NOSTAT);
570         ec = rpmcliArgIterHelper(ts, qva, RPMDBI_FTSWALK, argv, giFlags);
571         break;
572     default:
573         qva->qva_gi = rpmgiNew(ts, RPMDBI_ARGLIST, NULL, 0);
574         qva->qva_rc = rpmgiSetArgs(qva->qva_gi, argv, ftsOpts,
575                 (giFlags | (RPMGI_NOGLOB|RPMGI_NOHEADER)));
576         while (rpmgiNext(qva->qva_gi) == RPMRC_OK) {
577             ec += rpmQueryVerify(qva, ts, rpmgiHdrPath(qva->qva_gi));
578             rpmtsEmpty(ts);
579         }
580         qva->qva_gi = rpmgiFree(qva->qva_gi);
581         break;
582     }
583
584     return ec;
585 }
586
587 int rpmcliQuery(rpmts ts, QVA_t qva, char * const * argv)
588 {
589     rpmVSFlags vsflags, ovsflags;
590     int ec = 0;
591
592     if (qva->qva_showPackage == NULL)
593         qva->qva_showPackage = showQueryPackage;
594
595     /* If --queryformat unspecified, then set default now. */
596     if (!(qva->qva_flags & _QUERY_FOR_BITS) && qva->qva_queryFormat == NULL) {
597         char * fmt = rpmExpand("%{?_query_all_fmt}\n", NULL);
598         if (fmt == NULL || strlen(fmt) <= 1) {
599             fmt = _free(fmt);
600             fmt = xstrdup("%{nvra}\n");
601         }
602         qva->qva_queryFormat = fmt;
603     }
604
605     vsflags = rpmExpandNumeric("%{?_vsflags_query}");
606     if (qva->qva_flags & VERIFY_DIGEST)
607         vsflags |= _RPMVSF_NODIGESTS;
608     if (qva->qva_flags & VERIFY_SIGNATURE)
609         vsflags |= _RPMVSF_NOSIGNATURES;
610     if (qva->qva_flags & VERIFY_HDRCHK)
611         vsflags |= RPMVSF_NOHDRCHK;
612
613     ovsflags = rpmtsSetVSFlags(ts, vsflags);
614     ec = rpmcliArgIter(ts, qva, argv);
615     vsflags = rpmtsSetVSFlags(ts, ovsflags);
616
617     if (qva->qva_showPackage == showQueryPackage)
618         qva->qva_showPackage = NULL;
619
620     return ec;
621 }