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