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