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