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