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