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