- combine depends and provides handling into one PRCO() method in rpmhandler,
[apt.git] / apt-pkg / rpm / rpmhandler.cc
1
2 /*
3  ######################################################################
4
5  RPM database and hdlist related handling
6
7  ######################################################################
8  */
9
10 #include <config.h>
11
12 #ifdef HAVE_RPM
13
14 #include <fcntl.h>
15 #include <sys/types.h>
16 #include <sys/stat.h>
17 #include <utime.h>
18 #include <unistd.h>
19 #include <assert.h>
20 #include <libgen.h>
21
22 #include <apt-pkg/error.h>
23 #include <apt-pkg/configuration.h>
24 #include <apt-pkg/md5.h>
25 #include <apt-pkg/crc-16.h>
26
27 #include <apt-pkg/rpmhandler.h>
28 #include <apt-pkg/rpmpackagedata.h>
29
30 #ifdef WITH_REPOMD
31 #include <libxml/parser.h>
32 #include <libxml/tree.h>
33 #include <libxml/xmlreader.h>
34 #endif
35
36 #include <apti18n.h>
37
38 #if RPM_VERSION >= 0x040100
39 #include <rpm/rpmts.h>
40 #include <rpm/rpmdb.h>
41 #include <rpm/rpmds.h>
42 #include <rpm/rpmfi.h>
43 #define rpmxxInitIterator(a,b,c,d) rpmtsInitIterator(a,(rpmTag)b,c,d)
44 #else
45 #define rpmxxInitIterator(a,b,c,d) rpmdbInitIterator(a,b,c,d)
46 #endif
47
48 // An attempt to deal with false zero epochs from repomd. With older rpm's we
49 // can only blindly trust the repo admin created the repository with options
50 // suitable for those versions. For rpm >= 4.2.1 this is linked with
51 // promoteepoch behavior - if promoteepoch is used then epoch hiding must
52 // not happen.
53 bool HideZeroEpoch;
54
55 string RPMHandler::Epoch()
56 {
57    char str[512] = "";
58    int_32 count, type, *epoch;
59    void *val;
60    assert(HeaderP != NULL);
61    int rc = headerGetEntry(HeaderP, RPMTAG_EPOCH, &type, &val, &count);
62    epoch = (int_32*)val;
63    if (rc == 1 && count > 0) {
64       snprintf(str, sizeof(str), "%i", epoch[0]);
65    }
66    return string(str);
67 }
68
69 unsigned long RPMHandler::GetITag(rpmTag Tag)
70 {
71    int_32 count, type, *num;
72    void *val;
73    assert(HeaderP != NULL);
74    int rc = headerGetEntry(HeaderP, Tag,
75                            &type, (void**)&val, &count);
76    num = (int_32*)val;
77    return rc?num[0]:0;
78 }
79
80 string RPMHandler::GetSTag(rpmTag Tag)
81 {
82    char *str;
83    void *val;
84    int_32 count, type;
85    assert(HeaderP != NULL);
86    int rc = headerGetEntry(HeaderP, Tag,
87                            &type, (void**)&val, &count);
88    str = (char *)val;
89    return string(rc?str:"");
90 }
91
92 string RPMHandler::EVR()
93 {
94    string e = Epoch();
95    string v = Version();
96    string r = Release();
97    string evr = "";
98    if (e.empty() == true) {
99       evr = v + '-' + r;
100    } else if (HideZeroEpoch && e == "0") {
101       evr = v + '-' + r;
102    } else {
103       evr = e + ':' + v + '-' + r;
104    }
105    return evr;
106
107
108 unsigned int RPMHandler::DepOp(int_32 rpmflags)
109 {
110    unsigned int Op = 0;
111    int_32 flags = (rpmflags & RPMSENSE_SENSEMASK);
112    if (flags == RPMSENSE_ANY) {
113       Op = pkgCache::Dep::NoOp;
114    } else if (flags & RPMSENSE_LESS) {
115       if (flags & RPMSENSE_EQUAL)
116           Op = pkgCache::Dep::LessEq;
117       else
118           Op = pkgCache::Dep::Less;
119    } else if (flags & RPMSENSE_GREATER) {
120       if (flags & RPMSENSE_EQUAL)
121           Op = pkgCache::Dep::GreaterEq;
122       else
123           Op = pkgCache::Dep::Greater;
124    } else if (flags & RPMSENSE_EQUAL) {
125       Op = pkgCache::Dep::Equals;
126    } else {
127       /* can't happen, right? */
128       _error->Error(_("Impossible flags %d in %s"), rpmflags, Name().c_str());
129    }
130       
131    return Op;
132 }
133
134 bool RPMHandler::HasFile(const char *File)
135 {
136    if (*File == '\0')
137       return false;
138    
139    vector<string> Files;
140    FileList(Files);
141    for (vector<string>::iterator I = Files.begin(); I != Files.end(); I++) {
142       if (string(File) == (*I)) {
143          return true;
144       }
145    }
146    return false;
147 }
148
149 bool RPMHandler::InternalDep(const char *name, const char *ver, int_32 flag) 
150 {
151    if (strncmp(name, "rpmlib(", strlen("rpmlib(")) == 0) {
152 #if RPM_VERSION >= 0x040404
153      rpmds rpmlibProv = NULL;
154      rpmds ds = rpmdsSingle(RPMTAG_PROVIDENAME,
155                             name, ver?ver:NULL, flag);
156      rpmdsRpmlib(&rpmlibProv, NULL);
157      int res = rpmdsSearch(rpmlibProv, ds) >= 0;
158      rpmdsFree(ds);
159      rpmdsFree(rpmlibProv);
160 #elif RPM_VERSION >= 0x040100
161       rpmds ds = rpmdsSingle(RPMTAG_PROVIDENAME,
162                              name, ver?ver:NULL, flag);
163       int res = rpmCheckRpmlibProvides(ds);
164       rpmdsFree(ds);
165 #else
166       int res = rpmCheckRpmlibProvides(name, ver?ver:NULL,
167                                        flag);
168 #endif
169       if (res) 
170          return true;
171    }
172
173 #if RPM_VERSION >= 0x040404
174    // uhhuh, any of these changing would require full cache rebuild...
175    if (strncmp(name, "getconf(", strlen("getconf(")) == 0)
176    {
177      rpmds getconfProv = NULL;
178      rpmds ds = rpmdsSingle(RPMTAG_PROVIDENAME,
179                             name, ver?ver:NULL, flag);
180      rpmdsGetconf(&getconfProv, NULL);
181      int res = rpmdsSearch(getconfProv, ds);
182      rpmdsFree(ds);
183      rpmdsFree(getconfProv);
184      if (res) 
185          return true;
186    }
187
188    if (strncmp(name, "cpuinfo(", strlen("cpuinfo(")) == 0)
189    {
190      rpmds cpuinfoProv = NULL;
191      rpmds ds = rpmdsSingle(RPMTAG_PROVIDENAME,
192                             name, ver?ver:NULL, flag);
193      rpmdsCpuinfo(&cpuinfoProv, NULL);
194      int res = rpmdsSearch(cpuinfoProv, ds);
195      rpmdsFree(ds);
196      rpmdsFree(cpuinfoProv);
197      if (res) 
198          return true;
199    }
200
201    if (strncmp(name, "sysinfo(", strlen("sysinfo(")) == 0)
202    {
203      rpmds sysinfoProv = NULL;
204      rpmds ds = rpmdsSingle(RPMTAG_PROVIDENAME,
205                             name, ver?ver:NULL, flag);
206      rpmdsCpuinfo(&sysinfoProv, NULL);
207      int res = rpmdsSearch(sysinfoProv, ds);
208      rpmdsFree(ds);
209      rpmdsFree(sysinfoProv);
210      if (res)
211          return true;
212    }
213
214    if (strncmp(name, "uname(", strlen("uname(")) == 0)
215    {
216      rpmds unameProv = NULL;
217      rpmds ds = rpmdsSingle(RPMTAG_PROVIDENAME,
218                             name, ver?ver:NULL, flag);
219      rpmdsUname(&unameProv, NULL);
220      int res = rpmdsSearch(unameProv, ds);
221      rpmdsFree(ds);
222      rpmdsFree(unameProv);
223      if (res)
224          return true;
225    }
226
227    if (strlen(name) > 5 && name[strlen(name)-1] == ')' &&
228        ((strchr("Rr_", name[0]) != NULL &&
229          strchr("Ww_", name[1]) != NULL &&
230          strchr("Xx_", name[2]) != NULL &&
231          name[3] == '(') ||
232          strncmp(name, "exists(", strlen("exists(")) == 0 ||
233          strncmp(name, "executable(", strlen("executable(")) == 0 ||
234          strncmp(name, "readable(", strlen("readable(")) == 0 ||
235          strncmp(name, "writable(", strlen("writable("))== 0 ))
236    {
237       int res = rpmioAccess(name, NULL, X_OK);
238       if (res == 0)
239          return true;
240    }
241
242    /* TODO
243     * - /etc/rpm/sysinfo provides
244     * - macro probe provides 
245     * - actually implement soname() and access() dependencies
246     */
247    if (strncmp(name, "soname(", strlen("soname(")) == 0)
248    {
249       cout << "FIXME, ignoring soname() dependency: " << name << endl;
250       return true;
251    }
252 #endif
253    return false; 
254 }
255
256 bool RPMHandler::PutDep(const char *name, const char *ver, int_32 flags, 
257                         unsigned int Type, vector<Dependency*> &Deps)
258 {
259    if (InternalDep(name, ver, flags) == true) {
260       return true;
261    }
262
263    if (Type == pkgCache::Dep::Depends) {
264       if (flags & RPMSENSE_PREREQ)
265          Type = pkgCache::Dep::PreDepends;
266 #if RPM_VERSION >= 0x040403
267       else if (flags & RPMSENSE_MISSINGOK)
268          Type = pkgCache::Dep::Suggests;
269 #endif
270       else
271          Type = pkgCache::Dep::Depends;
272    }
273
274    Dependency *Dep = new Dependency;
275    Dep->Name = name;
276    Dep->Version = ver;
277
278    if (HideZeroEpoch && Dep->Version.substr(0, 2) == "0:") {
279       Dep->Version = Dep->Version.substr(2);
280    }
281
282    Dep->Op = DepOp(flags);
283    Dep->Type = Type;
284    Deps.push_back(Dep);
285    return true;
286 }
287
288
289 bool RPMHandler::PRCO(unsigned int Type, vector<Dependency*> &Deps)
290 #if RPM_VERSION >= 0x040100
291 {
292    rpmTag deptype = RPMTAG_REQUIRENAME;
293    switch (Type) {
294       case pkgCache::Dep::Depends:
295          deptype = RPMTAG_REQUIRENAME;
296          break;
297       case pkgCache::Dep::Obsoletes:
298          deptype = RPMTAG_OBSOLETENAME;
299          break;
300       case pkgCache::Dep::Conflicts:
301          deptype = RPMTAG_CONFLICTNAME;
302          break;
303       case pkgCache::Dep::Provides:
304          deptype = RPMTAG_PROVIDENAME;
305          break;
306 #if RPM_VERSION >= 0x040403
307       case pkgCache::Dep::Suggests:
308          deptype = RPMTAG_SUGGESTNAME;
309          break;
310 #if 0 // Enhances dep type is not even known to apt, sigh..
311       case pkgCache::Dep::Enhances:
312          deptype = RPMTAG_ENHANCES;
313          break;
314 #endif
315 #endif
316       default:
317          /* can't happen... right? */
318          return false;
319          break;
320    }
321    rpmds ds = NULL;
322    ds = rpmdsNew(HeaderP, deptype, 0);
323    if (ds != NULL) {
324       while (rpmdsNext(ds) >= 0) {
325          bool r = PutDep(rpmdsN(ds), rpmdsEVR(ds), rpmdsFlags(ds), Type, Deps);
326       }
327    }
328    rpmdsFree(ds);
329    return true;
330 }
331 #else
332 bool RPMHandler::PRCO(unsigned int Type, vector<Dependency*> &Deps)
333 {
334    char **namel = NULL;
335    char **verl = NULL;
336    int *flagl = NULL;
337    int res, type, count;
338    int_32 deptag, depver, depflags;
339    void *nameval = NULL;
340    void *verval = NULL;
341    void *flagval = NULL;
342
343    switch (Type) {
344       case pkgCache::Dep::Depends:
345          deptag = RPMTAG_REQUIRENAME;
346          depver = RPMTAG_REQUIREVERSION;
347          depflags = RPMTAG_REQUIREFLAGS;
348          break;
349       case pkgCache::Dep::Obsoletes:
350          deptag = RPMTAG_OBSOLETENAME;
351          depver = RPMTAG_OBSOLETEVERSION;
352          depflags = RPMTAG_OBSOLETEFLAGS;
353          break;
354       case pkgCache::Dep::Conflicts:
355          deptag = RPMTAG_CONFLICTNAME;
356          depver = RPMTAG_CONFLICTVERSION;
357          depflags = RPMTAG_CONFLICTFLAGS;
358          break;
359       case pkgCache::Dep::Provides:
360          deptag = RPMTAG_PROVIDENAME;
361          depver = RPMTAG_PROVIDEVERSION;
362          depflags = RPMTAG_PROVIDEFLAGS;
363          break;
364       default:
365          /* can't happen... right? */
366          return false;
367          break;
368    }
369    res = headerGetEntry(HeaderP, deptag, &type, (void **)&nameval, &count);
370    if (res != 1)
371       return true;
372    res = headerGetEntry(HeaderP, depver, &type, (void **)&verval, &count);
373    res = headerGetEntry(HeaderP, depflags, &type, (void **)&flagval, &count);
374
375    namel = (char**)nameval;
376    verl = (char**)verval;
377    flagl = (int*)flagval;
378
379    for (int i = 0; i < count; i++) {
380
381       bool res = PutDep(namel[i], verl[i], flagl[i], Type, Deps);
382    }
383    free(namel);
384    free(verl);
385    return true;
386       
387 }
388 #endif
389
390 // XXX rpmfi originates from somewhere around 2001 but what's the version?
391 #if RPM_VERSION >= 0x040100
392 bool RPMHandler::FileList(vector<string> &FileList)
393 {
394    rpmfi fi = NULL;
395    fi = rpmfiNew(NULL, HeaderP, RPMTAG_BASENAMES, 0);
396    if (fi != NULL) {
397       while (rpmfiNext(fi) >= 0) {
398         FileList.push_back(rpmfiFN(fi));
399       }
400    }
401    fi = rpmfiFree(fi);
402    return true;
403 }
404 #else
405 bool RPMHandler::FileList(vector<string> &FileList)
406 {
407    const char **names = NULL;
408    void *val = NULL;
409    int_32 count = 0;
410    bool ret = true;
411    rpmHeaderGetEntry(HeaderP, RPMTAG_OLDFILENAMES,
412                      NULL, (void **) &val, &count);
413    names = (const char **)val;
414    while (count--) {
415       FileList.push_back(names[count]);
416    }
417    free(names);
418    return ret;
419
420 }
421 #endif
422
423 RPMFileHandler::RPMFileHandler(string File)
424 {
425    ID = File;
426    FD = Fopen(File.c_str(), "r");
427    if (FD == NULL)
428    {
429       /*
430       _error->Error(_("could not open RPM package list file %s: %s"),
431                     File.c_str(), rpmErrorString());
432       */
433       return;
434    }
435    iSize = fdSize(FD);
436 }
437
438 RPMFileHandler::RPMFileHandler(FileFd *File)
439 {
440    FD = fdDup(File->Fd());
441    if (FD == NULL)
442    {
443       /*
444       _error->Error(_("could not create RPM file descriptor: %s"),
445                     rpmErrorString());
446       */
447       return;
448    }
449    iSize = fdSize(FD);
450 }
451
452 RPMFileHandler::~RPMFileHandler()
453 {
454    if (HeaderP != NULL)
455       headerFree(HeaderP);
456    if (FD != NULL)
457       Fclose(FD);
458 }
459
460 bool RPMFileHandler::Skip()
461 {
462    if (FD == NULL)
463       return false;
464    iOffset = lseek(Fileno(FD),0,SEEK_CUR);
465    if (HeaderP != NULL)
466        headerFree(HeaderP);
467    HeaderP = headerRead(FD, HEADER_MAGIC_YES);
468    return (HeaderP != NULL);
469 }
470
471 bool RPMFileHandler::Jump(off_t Offset)
472 {
473    if (FD == NULL)
474       return false;
475    if (lseek(Fileno(FD),Offset,SEEK_SET) != Offset)
476       return false;
477    return Skip();
478 }
479
480 void RPMFileHandler::Rewind()
481 {
482    if (FD == NULL)
483       return;
484    iOffset = lseek(Fileno(FD),0,SEEK_SET);
485    if (iOffset != 0)
486       _error->Error(_("could not rewind RPMFileHandler"));
487 }
488
489 string RPMFileHandler::FileName()
490 {
491    return GetSTag(CRPMTAG_FILENAME);
492 }
493
494 string RPMFileHandler::Directory()
495 {
496    return GetSTag(CRPMTAG_DIRECTORY);
497 }
498
499 unsigned long RPMFileHandler::FileSize()
500 {
501    return GetITag(CRPMTAG_FILESIZE);
502 }
503
504 string RPMFileHandler::MD5Sum()
505 {
506    return GetSTag(CRPMTAG_MD5);
507 }
508
509 bool RPMSingleFileHandler::Skip()
510 {
511    if (FD == NULL)
512       return false;
513    if (HeaderP != NULL) {
514       headerFree(HeaderP);
515       HeaderP = NULL;
516       return false;
517    }
518 #if RPM_VERSION >= 0x040100
519    rpmts TS = rpmtsCreate();
520    rpmtsSetVSFlags(TS, (rpmVSFlags_e)-1);
521    int rc = rpmReadPackageFile(TS, FD, sFilePath.c_str(), &HeaderP);
522    if (rc != RPMRC_OK && rc != RPMRC_NOTTRUSTED && rc != RPMRC_NOKEY) {
523       _error->Error(_("Failed reading file %s"), sFilePath.c_str());
524       HeaderP = NULL;
525    }
526    rpmtsFree(TS);
527 #else
528    int rc = rpmReadPackageHeader(FD, &HeaderP, 0, NULL, NULL);
529    if (rc) {
530       _error->Error(_("Failed reading file %s"), sFilePath.c_str());
531       HeaderP = NULL;
532    }
533 #endif
534    return (HeaderP != NULL);
535 }
536
537 bool RPMSingleFileHandler::Jump(off_t Offset)
538 {
539    assert(Offset == 0);
540    Rewind();
541    return RPMFileHandler::Jump(Offset);
542 }
543
544 void RPMSingleFileHandler::Rewind()
545 {
546    if (FD == NULL)
547       return;
548    if (HeaderP != NULL) {
549       HeaderP = NULL;
550       headerFree(HeaderP);
551    }
552    lseek(Fileno(FD),0,SEEK_SET);
553 }
554
555 unsigned long RPMSingleFileHandler::FileSize()
556 {
557    struct stat S;
558    if (stat(sFilePath.c_str(),&S) != 0)
559       return 0;
560    return S.st_size;
561 }
562
563 string RPMSingleFileHandler::MD5Sum()
564 {
565    MD5Summation MD5;
566    FileFd File(sFilePath, FileFd::ReadOnly);
567    MD5.AddFD(File.Fd(), File.Size());
568    File.Close();
569    return MD5.Result().Value();
570 }
571
572 RPMDirHandler::RPMDirHandler(string DirName)
573    : sDirName(DirName)
574 {
575    ID = DirName;
576 #if RPM_VERSION >= 0x040100
577    TS = NULL;
578 #endif
579    Dir = opendir(sDirName.c_str());
580    if (Dir == NULL)
581       return;
582    iSize = 0;
583    while (nextFileName() != NULL)
584       iSize += 1;
585    rewinddir(Dir);
586 #if RPM_VERSION >= 0x040100
587    TS = rpmtsCreate();
588    rpmtsSetVSFlags(TS, (rpmVSFlags_e)-1);
589 #endif
590 }
591
592 const char *RPMDirHandler::nextFileName()
593 {
594    for (struct dirent *Ent = readdir(Dir); Ent != 0; Ent = readdir(Dir))
595    {
596       const char *name = Ent->d_name;
597
598       if (name[0] == '.')
599          continue;
600
601       if (flExtension(name) != "rpm")
602          continue;
603
604       // Make sure it is a file and not something else
605       sFilePath = flCombine(sDirName,name);
606       struct stat St;
607       if (stat(sFilePath.c_str(),&St) != 0 || S_ISREG(St.st_mode) == 0)
608          continue;
609
610       sFileName = name;
611       
612       return name;
613    } 
614    return NULL;
615 }
616
617 RPMDirHandler::~RPMDirHandler()
618 {
619    if (HeaderP != NULL)
620       headerFree(HeaderP);
621 #if RPM_VERSION >= 0x040100
622    if (TS != NULL)
623       rpmtsFree(TS);
624 #endif
625    if (Dir != NULL)
626       closedir(Dir);
627 }
628
629 bool RPMDirHandler::Skip()
630 {
631    if (Dir == NULL)
632       return false;
633    if (HeaderP != NULL) {
634       headerFree(HeaderP);
635       HeaderP = NULL;
636    }
637    const char *fname = nextFileName();
638    bool Res = false;
639    for (; fname != NULL; fname = nextFileName()) {
640       iOffset++;
641       if (fname == NULL)
642          break;
643       FD_t FD = Fopen(sFilePath.c_str(), "r");
644       if (FD == NULL)
645          continue;
646 #if RPM_VERSION >= 0x040100
647       int rc = rpmReadPackageFile(TS, FD, fname, &HeaderP);
648       Fclose(FD);
649       if (rc != RPMRC_OK
650           && rc != RPMRC_NOTTRUSTED
651           && rc != RPMRC_NOKEY)
652          continue;
653 #else
654       int isSource;
655       int rc = rpmReadPackageHeader(FD, &HeaderP, &isSource, NULL, NULL);
656       Fclose(FD);
657       if (rc != 0)
658          continue;
659 #endif
660       Res = true;
661       break;
662    }
663    return Res;
664 }
665
666 bool RPMDirHandler::Jump(off_t Offset)
667 {
668    if (Dir == NULL)
669       return false;
670    rewinddir(Dir);
671    iOffset = 0;
672    while (1) {
673       if (iOffset+1 == Offset)
674          return Skip();
675       if (nextFileName() == NULL)
676          break;
677       iOffset++;
678    }
679    return false;
680 }
681
682 void RPMDirHandler::Rewind()
683 {
684    rewinddir(Dir);
685    iOffset = 0;
686 }
687
688 unsigned long RPMDirHandler::FileSize()
689 {
690    if (Dir == NULL)
691       return 0;
692    struct stat St;
693    if (stat(sFilePath.c_str(),&St) != 0) {
694       _error->Errno("stat",_("Unable to determine the file size"));
695       return 0;
696    }
697    return St.st_size;
698 }
699
700 string RPMDirHandler::MD5Sum()
701 {
702    if (Dir == NULL)
703       return "";
704    MD5Summation MD5;
705    FileFd File(sFilePath, FileFd::ReadOnly);
706    MD5.AddFD(File.Fd(), File.Size());
707    File.Close();
708    return MD5.Result().Value();
709 }
710
711
712 RPMDBHandler::RPMDBHandler(bool WriteLock)
713    : Handler(0), WriteLock(WriteLock)
714 {
715 #if RPM_VERSION >= 0x040000
716    RpmIter = NULL;
717 #endif
718    string Dir = _config->Find("RPM::RootDir");
719    
720    rpmReadConfigFiles(NULL, NULL);
721    ID = DataPath(false);
722
723    RPMPackageData::Singleton()->InitMinArchScore();
724
725    // Everytime we open a database for writing, it has its
726    // mtime changed, and kills our cache validity. As we never
727    // change any information in the database directly, we will
728    // restore the mtime and save our cache.
729    struct stat St;
730    stat(DataPath(false).c_str(), &St);
731    DbFileMtime = St.st_mtime;
732
733 #if RPM_VERSION >= 0x040100
734    Handler = rpmtsCreate();
735    rpmtsSetVSFlags(Handler, (rpmVSFlags_e)-1);
736    rpmtsSetRootDir(Handler, Dir.c_str());
737 #else
738    const char *RootDir = NULL;
739    if (!Dir.empty())
740       RootDir = Dir.c_str();
741    if (rpmdbOpen(RootDir, &Handler, O_RDONLY, 0644) != 0)
742    {
743       _error->Error(_("could not open RPM database"));
744       return;
745    }
746 #endif
747 #if RPM_VERSION >= 0x040000
748    RpmIter = rpmxxInitIterator(Handler, RPMDBI_PACKAGES, NULL, 0);
749    if (RpmIter == NULL) {
750       _error->Error(_("could not create RPM database iterator"));
751       return;
752    }
753    // iSize = rpmdbGetIteratorCount(RpmIter);
754    // This doesn't seem to work right now. Code in rpm (4.0.4, at least)
755    // returns a 0 from rpmdbGetIteratorCount() if rpmxxInitIterator() is
756    // called with RPMDBI_PACKAGES or with keyp == NULL. The algorithm
757    // below will be used until there's support for it.
758    iSize = 0;
759    rpmdbMatchIterator countIt;
760    countIt = rpmxxInitIterator(Handler, RPMDBI_PACKAGES, NULL, 0);
761    while (rpmdbNextIterator(countIt) != NULL)
762       iSize++;
763    rpmdbFreeIterator(countIt);
764 #else
765    iSize = St.st_size;
766
767 #endif
768
769
770    // Restore just after opening the database, and just after closing.
771    if (WriteLock) {
772       struct utimbuf Ut;
773       Ut.actime = DbFileMtime;
774       Ut.modtime = DbFileMtime;
775       utime(DataPath(false).c_str(), &Ut);
776    }
777 }
778
779 RPMDBHandler::~RPMDBHandler()
780 {
781 #if RPM_VERSION >= 0x040000
782    if (RpmIter != NULL)
783       rpmdbFreeIterator(RpmIter);
784 #else
785    if (HeaderP != NULL)
786        headerFree(HeaderP);
787 #endif
788
789    if (Handler != NULL) {
790 #if RPM_VERSION >= 0x040100
791       rpmtsFree(Handler);
792 #else
793       rpmdbClose(Handler);
794 #endif
795    }
796
797    // Restore just after opening the database, and just after closing.
798    if (WriteLock) {
799       struct utimbuf Ut;
800       Ut.actime = DbFileMtime;
801       Ut.modtime = DbFileMtime;
802       utime(DataPath(false).c_str(), &Ut);
803    }
804 }
805
806 string RPMDBHandler::DataPath(bool DirectoryOnly)
807 {
808    string File = "packages.rpm";
809    char *tmp = (char *) rpmExpand("%{_dbpath}", NULL);
810    string DBPath(_config->Find("RPM::RootDir")+tmp);
811    free(tmp);
812
813 #if RPM_VERSION >= 0x040000
814    if (rpmExpandNumeric("%{_dbapi}") >= 3)
815       File = "Packages";       
816 #endif
817    if (DirectoryOnly == true)
818        return DBPath;
819    else
820        return DBPath+"/"+File;
821 }
822
823 bool RPMDBHandler::Skip()
824 {
825 #if RPM_VERSION >= 0x040000
826    if (RpmIter == NULL)
827        return false;
828    HeaderP = rpmdbNextIterator(RpmIter);
829    iOffset = rpmdbGetIteratorOffset(RpmIter);
830    if (HeaderP == NULL)
831       return false;
832 #else
833    if (iOffset == 0)
834       iOffset = rpmdbFirstRecNum(Handler);
835    else
836       iOffset = rpmdbNextRecNum(Handler, iOffset);
837    if (HeaderP != NULL)
838    {
839       headerFree(HeaderP);
840       HeaderP = NULL;
841    }
842    if (iOffset == 0)
843        return false;
844    HeaderP = rpmdbGetRecord(Handler, iOffset);
845 #endif
846    return true;
847 }
848
849 bool RPMDBHandler::Jump(off_t Offset)
850 {
851    iOffset = Offset;
852 #if RPM_VERSION >= 0x040000
853    // rpmdb indexes are hardcoded uint32_t, the size must match here
854    uint_32 rpmOffset = iOffset;
855    if (RpmIter == NULL)
856       return false;
857    rpmdbFreeIterator(RpmIter);
858    if (iOffset == 0)
859       RpmIter = rpmxxInitIterator(Handler, RPMDBI_PACKAGES, NULL, 0);
860    else {
861       RpmIter = rpmxxInitIterator(Handler, RPMDBI_PACKAGES,
862                                   &rpmOffset, sizeof(rpmOffset));
863       iOffset = rpmOffset;
864    }
865    HeaderP = rpmdbNextIterator(RpmIter);
866 #else
867    HeaderP = rpmdbGetRecord(Handler, iOffset);
868 #endif
869    return true;
870 }
871
872 bool RPMDBHandler::JumpByName(string PkgName)
873 {
874    if (RpmIter == NULL) return false;
875    rpmdbFreeIterator(RpmIter);
876 #if RPM_VERSION >= 0x040100
877    RpmIter = rpmtsInitIterator(Handler, (rpmTag)RPMDBI_LABEL, PkgName.c_str(), 0);
878 #else
879    RpmIter = rpmdbInitIterator(Handler, RPMDBI_LABEL, PkgName.c_str(), 0);
880 #endif
881
882    HeaderP = rpmdbNextIterator(RpmIter);
883    return (HeaderP != NULL);
884 }
885
886 void RPMDBHandler::Rewind()
887 {
888 #if RPM_VERSION >= 0x040000
889    if (RpmIter == NULL)
890       return;
891    rpmdbFreeIterator(RpmIter);   
892    RpmIter = rpmxxInitIterator(Handler, RPMDBI_PACKAGES, NULL, 0);
893 #else
894    if (HeaderP != NULL)
895    {
896       headerFree(HeaderP);
897       HeaderP = NULL;
898    }
899 #endif
900    iOffset = 0;
901 }
902 #endif
903
904 #ifdef WITH_REPOMD
905 RPMRepomdHandler::RPMRepomdHandler(string File)
906 {
907    PrimaryFile = File;
908
909    ID = File;
910    Root = NULL;
911    Primary = NULL;
912    xmlChar *packages = NULL;
913    off_t pkgcount = 0;
914    
915
916    Primary = xmlReadFile(File.c_str(), NULL, XML_PARSE_NONET|XML_PARSE_NOBLANKS);
917    if ((Root = xmlDocGetRootElement(Primary)) == NULL) {
918       _error->Error(_("Failed to open package index %s"), PrimaryFile.c_str());
919       goto error;
920    }
921    if (xmlStrncmp(Root->name, (xmlChar*)"metadata", strlen("metadata")) != 0) {
922       _error->Error(_("Corrupted package index %s"), PrimaryFile.c_str());
923       goto error;
924    }
925
926    packages = xmlGetProp(Root, (xmlChar*)"packages");
927    iSize = atoi((char*)packages);
928    xmlFree(packages);
929    for (xmlNode *n = Root->children; n; n = n->next) {
930       if (n->type != XML_ELEMENT_NODE ||
931           xmlStrcmp(n->name, (xmlChar*)"package") != 0)
932          continue;
933       Pkgs.push_back(n);
934       pkgcount++;
935    }
936    PkgIter = Pkgs.begin();
937
938    // There seem to be broken version(s) of createrepo around which report
939    // to have one more package than is in the repository. Warn and work around.
940    if (iSize != pkgcount) {
941       _error->Warning(_("Inconsistent metadata, package count doesn't match in %s"), File.c_str());
942       iSize = pkgcount;
943    }
944
945    return;
946
947 error:
948    if (Primary) {
949       xmlFreeDoc(Primary);
950    }
951 }
952
953 bool RPMRepomdHandler::Skip()
954 {
955    if (PkgIter == Pkgs.end()) {
956       return false;
957    }
958    NodeP = *PkgIter;
959    iOffset = PkgIter - Pkgs.begin();
960
961    PkgIter++;
962    return true;
963 }
964
965 bool RPMRepomdHandler::Jump(off_t Offset)
966 {
967    if (Offset >= iSize) {
968       return false;
969    }
970    iOffset = Offset;
971    NodeP = Pkgs[Offset];
972    // This isn't strictly necessary as Skip() and Jump() aren't mixed
973    // in practise but doesn't hurt either...
974    PkgIter = Pkgs.begin() + Offset + 1;
975    return true;
976
977 }
978
979 void RPMRepomdHandler::Rewind()
980 {
981    iOffset = 0;
982    PkgIter = Pkgs.begin();
983 }
984
985 xmlNode *RPMRepomdHandler::FindNode(const string Name)
986 {
987    for (xmlNode *n = NodeP->children; n; n = n->next) {
988       if (xmlStrcmp(n->name, (xmlChar*)Name.c_str()) == 0) {
989          return n;
990       }
991    }
992    return NULL;
993 }
994
995 xmlNode *RPMRepomdHandler::FindNode(xmlNode *Node, const string Name)
996 {
997    for (xmlNode *n = Node->children; n; n = n->next) {
998       if (xmlStrcmp(n->name, (xmlChar*)Name.c_str()) == 0) {
999          return n;
1000       }
1001    }
1002    return NULL;
1003 }
1004
1005 string RPMRepomdHandler::FindTag(xmlNode *Node, string Tag)
1006 {
1007    xmlNode *n = FindNode(Node, Tag);
1008    string str = "";
1009    if (n) {
1010       xmlChar *content = xmlNodeGetContent(n);
1011       if (content) {
1012          str = (char*)content;
1013          xmlFree(content);
1014       }
1015    }
1016    return str;
1017 }
1018
1019 string RPMRepomdHandler::GetProp(xmlNode *Node, char *Prop)
1020 {
1021    string str = "";
1022    if (Node) {
1023       xmlChar *prop = xmlGetProp(Node, (xmlChar*)Prop);
1024       if (prop) {
1025          str = (char*)prop;
1026          xmlFree(prop);
1027       }
1028    }
1029    return str;
1030 }
1031
1032 string RPMRepomdHandler::Group()
1033 {
1034    xmlNode *n = FindNode("format");
1035    return FindTag(n, "group");
1036 }
1037
1038 string RPMRepomdHandler::Vendor()
1039 {
1040    xmlNode *n = FindNode("format");
1041    return FindTag(n, "vendor");
1042 }
1043
1044 string RPMRepomdHandler::Release()
1045 {
1046    xmlNode *n = FindNode("version");
1047    return GetProp(n, "rel");
1048 }
1049
1050 string RPMRepomdHandler::Version()
1051 {
1052    xmlNode *n = FindNode("version");
1053    return GetProp(n, "ver");
1054 }
1055
1056 string RPMRepomdHandler::Epoch()
1057 {
1058    string epoch;
1059    xmlNode *n = FindNode("version");
1060    epoch = GetProp(n, "epoch");
1061    // XXX createrepo stomps epoch zero on packages without epoch, hide
1062    // them. Rpm treats zero and empty equally anyway so it doesn't matter.
1063    if (epoch == "0")
1064       epoch = "";
1065    return epoch;
1066 }
1067
1068 string RPMRepomdHandler::FileName()
1069 {
1070    xmlNode *n;
1071    string str = "";
1072    if ((n = FindNode("location"))) {
1073       xmlChar *prop = xmlGetProp(n, (xmlChar*)"href");
1074       str = basename((char*)prop);
1075       xmlFree(prop);
1076    }
1077    return str;
1078 }
1079
1080 string RPMRepomdHandler::Directory()
1081 {
1082    xmlNode *n;
1083    string str = "";
1084    if ((n = FindNode("location"))) {
1085       xmlChar *prop = xmlGetProp(n, (xmlChar*)"href");
1086       if (prop) {
1087          str = dirname((char*)prop);
1088          xmlFree(prop);
1089       }
1090    }
1091    return str;
1092 }
1093
1094 string RPMRepomdHandler::MD5Sum()
1095 {
1096    // XXX FIXME the method should be an abstract Checksum type using
1097    // md5 / sha1 appropriately, for now relying on hacks elsewhere..
1098    return SHA1Sum();
1099 }
1100
1101 string RPMRepomdHandler::SHA1Sum()
1102 {
1103    xmlNode *n;
1104    string str = "";
1105    if ((n = FindNode("checksum"))) {
1106       xmlChar *content = xmlNodeGetContent(n);
1107       str = (char*)content;
1108       xmlFree(content);
1109    }
1110    return str;
1111 }
1112 unsigned long RPMRepomdHandler::FileSize()
1113 {
1114    xmlNode *n;
1115    unsigned long size = 0;
1116    if ((n = FindNode("size"))) {
1117       xmlChar *prop = xmlGetProp(n, (xmlChar*)"package");
1118       size = atol((char*)prop);
1119       xmlFree(prop);
1120    } 
1121    return size;
1122 }
1123
1124 unsigned long RPMRepomdHandler::InstalledSize()
1125 {
1126    xmlNode *n;
1127    unsigned long size = 0;
1128    if ((n = FindNode("size"))) {
1129       xmlChar *prop = xmlGetProp(n, (xmlChar*)"installed");
1130       size = atol((char*)prop);
1131       xmlFree(prop);
1132    } 
1133    return size;
1134 }
1135
1136 string RPMRepomdHandler::SourceRpm()
1137 {
1138    xmlNode *n = FindNode("format");
1139    return FindTag(n, "sourcerpm");
1140 }
1141
1142 bool RPMRepomdHandler::PRCO(unsigned int Type, vector<Dependency*> &Deps)
1143 {
1144    xmlNode *format = FindNode("format");
1145    xmlNode *dco = NULL;
1146
1147    switch (Type) {
1148       case pkgCache::Dep::Depends:
1149          dco = FindNode(format, "requires");
1150          break;
1151       case pkgCache::Dep::Conflicts:
1152          dco = FindNode(format, "conflicts");
1153          break;
1154       case pkgCache::Dep::Obsoletes:
1155          dco = FindNode(format, "obsoletes");
1156          break;
1157       case pkgCache::Dep::Provides:
1158          dco = FindNode(format, "provides");
1159          break;
1160    }
1161
1162    if (! dco) {
1163       return true;
1164    }
1165    for (xmlNode *n = dco->children; n; n = n->next) {
1166       int_32 RpmOp = 0;
1167       string deptype, depver;
1168       xmlChar *depname, *flags;
1169       if ((depname = xmlGetProp(n, (xmlChar*)"name")) == NULL) continue;
1170
1171       if ((flags = xmlGetProp(n, (xmlChar*)"flags"))) {
1172          deptype = string((char*)flags);
1173          xmlFree(flags);
1174
1175          xmlChar *epoch = xmlGetProp(n, (xmlChar*)"epoch");
1176          if (epoch) {
1177             depver += string((char*)epoch) + ":";
1178             xmlFree(epoch);
1179          }
1180          xmlChar *ver = xmlGetProp(n, (xmlChar*)"ver");
1181          if (ver) {
1182             depver += string((char*)ver);
1183             xmlFree(ver);
1184          }
1185          xmlChar *rel = xmlGetProp(n, (xmlChar*)"rel");
1186          if (rel) {
1187             depver += "-" + string((char*)rel);
1188             xmlFree(rel);
1189          }
1190
1191
1192          if (deptype == "EQ") {
1193             RpmOp = RPMSENSE_EQUAL;
1194          } else if (deptype == "GE") {
1195             RpmOp = RPMSENSE_GREATER | RPMSENSE_EQUAL;
1196          } else if (deptype == "GT") {
1197             RpmOp = RPMSENSE_GREATER;
1198          } else if (deptype == "LE") {
1199             RpmOp = RPMSENSE_LESS | RPMSENSE_EQUAL;
1200          } else if (deptype == "LT") {
1201             RpmOp = RPMSENSE_LESS;
1202          } else {
1203             // erm, unknown dependency type?
1204             return false;
1205          }
1206       } else {
1207          RpmOp = RPMSENSE_ANY;
1208       }
1209
1210       if (Type == pkgCache::Dep::Depends) {
1211          xmlChar *pre = xmlGetProp(n, (xmlChar*)"pre"); 
1212          if (pre) {
1213             RpmOp |= RPMSENSE_PREREQ;
1214             xmlFree(pre);
1215          }
1216       }
1217       bool res = PutDep((char*)depname, depver.c_str(), RpmOp, Type, Deps);
1218    }
1219    return true;
1220 }
1221
1222 bool RPMRepomdHandler::FileList(vector<string> &FileList)
1223 {
1224    xmlNode *format = FindNode("format");
1225    for (xmlNode *n = format->children; n; n = n->next) {
1226       if (xmlStrcmp(n->name, (xmlChar*)"file") != 0)  continue;
1227       xmlChar *Filename = xmlNodeGetContent(n);
1228       FileList.push_back(string((char*)Filename));
1229       xmlFree(Filename);
1230    }
1231    return true;
1232 }
1233
1234 RPMRepomdHandler::~RPMRepomdHandler()
1235 {
1236    xmlFreeDoc(Primary);
1237 }
1238
1239 RPMRepomdFLHandler::RPMRepomdFLHandler(string File) : RPMHandler()
1240 {
1241    FilelistFile = File.substr(0, File.size() - 11) + "filelists.xml";
1242
1243    ID = File;
1244    Filelist = NULL;
1245    NodeP = NULL;
1246    iOffset = -1;
1247
1248    if (FileExists(FilelistFile)) {
1249       Filelist = xmlReaderForFile(FilelistFile.c_str(), NULL,
1250                                   XML_PARSE_NONET|XML_PARSE_NOBLANKS);
1251       if (Filelist == NULL) {
1252         xmlFreeTextReader(Filelist);
1253         _error->Error(_("Failed to open filelist index %s"), FilelistFile.c_str());
1254         goto error;
1255       }
1256
1257       // seek into first package in filelists.xml
1258       int ret = xmlTextReaderRead(Filelist);
1259       if (ret == 1) {
1260         xmlChar *pkgs = xmlTextReaderGetAttribute(Filelist, (xmlChar*)"packages");
1261         iSize = atoi((char*)pkgs);
1262         xmlFree(pkgs);
1263       }
1264       while (ret == 1) {
1265         if (xmlStrcmp(xmlTextReaderConstName(Filelist),
1266                      (xmlChar*)"package") == 0) {
1267            break;
1268         }
1269         ret = xmlTextReaderRead(Filelist);
1270       }
1271    }
1272    return;
1273
1274 error:
1275    if (Filelist) {
1276        xmlFreeTextReader(Filelist);
1277    }
1278 }
1279
1280 bool RPMRepomdFLHandler::Jump(off_t Offset)
1281 {
1282    //cerr << "RepomdFLHandler::Jump() called but not implemented!" << endl;
1283    return false;
1284 }
1285
1286 void RPMRepomdFLHandler::Rewind()
1287 {
1288    //cerr << "RepomdFLHandler::Rewind() called but not implemented!" << endl;
1289 }
1290
1291 bool RPMRepomdFLHandler::Skip()
1292 {
1293    if (iOffset +1 >= iSize) {
1294       return false;
1295    }
1296    if (iOffset >= 0) {
1297       xmlTextReaderNext(Filelist);
1298    }
1299    NodeP = xmlTextReaderExpand(Filelist);
1300    iOffset++;
1301
1302    return true;
1303 }
1304
1305 bool RPMRepomdFLHandler::FileList(vector<string> &FileList)
1306 {
1307    for (xmlNode *n = NodeP->children; n; n = n->next) {
1308       if (xmlStrcmp(n->name, (xmlChar*)"file") != 0)  continue;
1309       xmlChar *Filename = xmlNodeGetContent(n);
1310       FileList.push_back(string((char*)Filename));
1311       xmlFree(Filename);
1312    }
1313    return true;
1314 }
1315
1316 string RPMRepomdFLHandler::FindTag(char *Tag)
1317 {
1318      string str = "";
1319      if (NodeP) {
1320          xmlChar *attr = xmlGetProp(NodeP, (xmlChar*)Tag);
1321          if (attr) {
1322             str = (char*)attr;
1323             xmlFree(attr);
1324          }
1325      }
1326      return str;
1327 };
1328
1329 RPMRepomdFLHandler::~RPMRepomdFLHandler()
1330 {
1331    xmlFreeTextReader(Filelist);
1332 }
1333 #endif /* WITH_REPOMD */
1334
1335
1336 // vim:sts=3:sw=3