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