- In rpm < 4.2.1 zero epoch isn't the same as non-existent epoch, we can't
[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       // XXX FIXME: Ugh.. "initialize" filelist to correct position
933       xmlTextReaderRead(Filelist);
934       xmlTextReaderRead(Filelist);
935    }     
936
937    packages = xmlGetProp(Root, (xmlChar*)"packages");
938    iSize = atoi((char*)packages);
939    xmlFree(packages);
940    for (xmlNode *n = Root->children; n; n = n->next) {
941       if (n->type != XML_ELEMENT_NODE ||
942           xmlStrcmp(n->name, (xmlChar*)"package") != 0)
943          continue;
944       Pkgs.push_back(n);
945       pkgcount++;
946    }
947    PkgIter = Pkgs.begin();
948
949    // There seem to be broken version(s) of createrepo around which report
950    // to have one more package than is in the repository. Warn and work around.
951    if (iSize != pkgcount) {
952       _error->Warning(_("Inconsistent metadata, package count doesn't match in %s"), File.c_str());
953       iSize = pkgcount;
954    }
955
956    return;
957
958 error:
959    if (Primary) {
960       xmlFreeDoc(Primary);
961    }
962    if (Filelist) {
963       xmlFreeTextReader(Filelist);
964    }
965 }
966
967 bool RPMRepomdHandler::Skip()
968 {
969    if (PkgIter == Pkgs.end()) {
970       return false;
971    }
972    NodeP = *PkgIter;
973    iOffset = PkgIter - Pkgs.begin();
974    if (WithFilelist) {
975       xmlTextReaderNext(Filelist);
976    }
977
978    PkgIter++;
979    return true;
980 }
981
982 bool RPMRepomdHandler::Jump(unsigned int Offset)
983 {
984    if (Offset >= iSize) {
985       return false;
986    }
987    iOffset = Offset;
988    NodeP = Pkgs[Offset];
989    // This isn't strictly necessary as Skip() and Jump() aren't mixed
990    // in practise but doesn't hurt either...
991    PkgIter = Pkgs.begin() + Offset + 1;
992    return true;
993
994 }
995
996 void RPMRepomdHandler::Rewind()
997 {
998    iOffset = 0;
999    PkgIter = Pkgs.begin();
1000 }
1001
1002 xmlNode *RPMRepomdHandler::FindNode(const string Name)
1003 {
1004    for (xmlNode *n = NodeP->children; n; n = n->next) {
1005       if (xmlStrcmp(n->name, (xmlChar*)Name.c_str()) == 0) {
1006          return n;
1007       }
1008    }
1009    return NULL;
1010 }
1011
1012 xmlNode *RPMRepomdHandler::FindNode(xmlNode *Node, const string Name)
1013 {
1014    for (xmlNode *n = Node->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 string RPMRepomdHandler::FindTag(xmlNode *Node, string Tag)
1023 {
1024    xmlNode *n = FindNode(Node, Tag);
1025    string str = "";
1026    if (n) {
1027       xmlChar *content = xmlNodeGetContent(n);
1028       str = (char*)content;
1029       xmlFree(content);
1030    }
1031    return str;
1032 }
1033
1034 string RPMRepomdHandler::GetProp(xmlNode *Node, char *Prop)
1035 {
1036    string str = "";
1037    if (Node) {
1038       xmlChar *prop = xmlGetProp(Node, (xmlChar*)Prop);
1039       str = (char*)prop;
1040       xmlFree(prop);
1041    }
1042    return str;
1043 }
1044
1045 string RPMRepomdHandler::Group()
1046 {
1047    xmlNode *n = FindNode("format");
1048    return FindTag(n, "group");
1049 }
1050
1051 string RPMRepomdHandler::Vendor()
1052 {
1053    xmlNode *n = FindNode("format");
1054    return FindTag(n, "vendor");
1055 }
1056
1057 string RPMRepomdHandler::Release()
1058 {
1059    xmlNode *n = FindNode("version");
1060    return GetProp(n, "rel");
1061 }
1062
1063 string RPMRepomdHandler::Version()
1064 {
1065    xmlNode *n = FindNode("version");
1066    return GetProp(n, "ver");
1067 }
1068
1069 string RPMRepomdHandler::Epoch()
1070 {
1071    string epoch;
1072    xmlNode *n = FindNode("version");
1073    epoch = GetProp(n, "epoch");
1074    // XXX createrepo stomps epoch zero on packages without epoch, hide
1075    // them. Rpm treats zero and empty equally anyway so it doesn't matter.
1076    if (epoch == "0")
1077       epoch = "";
1078    return epoch;
1079 }
1080
1081 string RPMRepomdHandler::FileName()
1082 {
1083    xmlNode *n;
1084    string str = "";
1085    if ((n = FindNode("location"))) {
1086       xmlChar *prop = xmlGetProp(n, (xmlChar*)"href");
1087       str = basename((char*)prop);
1088       xmlFree(prop);
1089    }
1090    return str;
1091 }
1092
1093 string RPMRepomdHandler::Directory()
1094 {
1095    xmlNode *n;
1096    string str = "";
1097    if ((n = FindNode("location"))) {
1098       xmlChar *prop = xmlGetProp(n, (xmlChar*)"href");
1099       str = dirname((char*)prop);
1100       xmlFree(prop);
1101    }
1102    return str;
1103 }
1104
1105 string RPMRepomdHandler::MD5Sum()
1106 {
1107    // XXX FIXME the method should be an abstract Checksum type using
1108    // md5 / sha1 appropriately, for now relying on hacks elsewhere..
1109    return SHA1Sum();
1110 }
1111
1112 string RPMRepomdHandler::SHA1Sum()
1113 {
1114    xmlNode *n;
1115    string str = "";
1116    if ((n = FindNode("checksum"))) {
1117       xmlChar *content = xmlNodeGetContent(n);
1118       str = (char*)content;
1119       xmlFree(content);
1120    }
1121    return str;
1122 }
1123 unsigned long RPMRepomdHandler::FileSize()
1124 {
1125    xmlNode *n;
1126    unsigned long size = 0;
1127    if ((n = FindNode("size"))) {
1128       xmlChar *prop = xmlGetProp(n, (xmlChar*)"package");
1129       size = atol((char*)prop);
1130       xmlFree(prop);
1131    } 
1132    return size;
1133 }
1134
1135 unsigned long RPMRepomdHandler::InstalledSize()
1136 {
1137    xmlNode *n;
1138    unsigned long size = 0;
1139    if ((n = FindNode("size"))) {
1140       xmlChar *prop = xmlGetProp(n, (xmlChar*)"installed");
1141       size = atol((char*)prop);
1142       xmlFree(prop);
1143    } 
1144    return size;
1145 }
1146
1147 string RPMRepomdHandler::SourceRpm()
1148 {
1149    xmlNode *n = FindNode("format");
1150    return FindTag(n, "sourcerpm");
1151 }
1152
1153 bool RPMRepomdHandler::HasFile(const char *File)
1154 {
1155    if (*File == '\0')
1156       return false;
1157
1158    bool found = false;
1159
1160    xmlNode *format = FindNode("format");
1161    for (xmlNode *n = format->children; n; n = n->next) {
1162       if (xmlStrcmp(n->name, (xmlChar*)"file") != 0) 
1163          continue;
1164       if (xmlStrcmp(xmlNodeGetContent(n), (xmlChar*)File) == 0) {
1165          found = true;
1166          break;
1167       }
1168    }
1169
1170 #if 0
1171    // look through filelists.xml for the file if not in primary.xml
1172    if (! found) {
1173       for (xmlNode *n = FlNodeP->children; n; n = n->next) {
1174          if (strcmp((char*)n->name, "file") != 0) 
1175             continue;
1176          if (strcmp(File, (char*)xmlNodeGetContent(n)) == 0) {
1177             found = true;
1178             break;
1179          }
1180       }
1181    }
1182 #endif
1183    return found;
1184
1185 }
1186
1187 bool RPMRepomdHandler::Depends(unsigned int Type, vector<Dependency*> &Deps)
1188 {
1189    xmlNode *format = FindNode("format");
1190    xmlNode *dco = NULL;
1191
1192    switch (Type) {
1193       case pkgCache::Dep::Depends:
1194          dco = FindNode(format, "requires");
1195          break;
1196       case pkgCache::Dep::Conflicts:
1197          dco = FindNode(format, "conflicts");
1198          break;
1199       case pkgCache::Dep::Obsoletes:
1200          dco = FindNode(format, "obsoletes");
1201          break;
1202    }
1203
1204    if (! dco) {
1205       return true;
1206    }
1207    for (xmlNode *n = dco->children; n; n = n->next) {
1208       unsigned int Op = 0;
1209       int_32 RpmOp = 0;
1210       string deptype, depver;
1211       xmlChar *depname, *flags;
1212       if ((depname = xmlGetProp(n, (xmlChar*)"name")) == NULL) continue;
1213
1214       if ((flags = xmlGetProp(n, (xmlChar*)"flags"))) {
1215          deptype = string((char*)flags);
1216          xmlFree(flags);
1217
1218          xmlChar *epoch = xmlGetProp(n, (xmlChar*)"epoch");
1219          if (epoch) {
1220             depver += string((char*)epoch) + ":";
1221             xmlFree(epoch);
1222          }
1223          xmlChar *ver = xmlGetProp(n, (xmlChar*)"ver");
1224          if (ver) {
1225             depver += string((char*)ver);
1226             xmlFree(ver);
1227          }
1228          xmlChar *rel = xmlGetProp(n, (xmlChar*)"rel");
1229          if (rel) {
1230             depver += "-" + string((char*)rel);
1231             xmlFree(rel);
1232          }
1233
1234
1235          if (deptype == "EQ") {
1236             Op = pkgCache::Dep::Equals;
1237             RpmOp = RPMSENSE_EQUAL;
1238          } else if (deptype == "GE") {
1239             Op = pkgCache::Dep::GreaterEq;
1240             RpmOp = RPMSENSE_GREATER | RPMSENSE_EQUAL;
1241          } else if (deptype == "GT") {
1242             Op = pkgCache::Dep::Greater;
1243             RpmOp = RPMSENSE_GREATER;
1244          } else if (deptype == "LE") {
1245             Op = pkgCache::Dep::LessEq;
1246             RpmOp = RPMSENSE_LESS | RPMSENSE_EQUAL;
1247          } else if (deptype == "LT") {
1248             Op = pkgCache::Dep::Less;
1249             RpmOp = RPMSENSE_LESS;
1250          } else {
1251             // erm, unknown dependency type?
1252          }
1253       } else {
1254          Op = pkgCache::Dep::NoOp;
1255          RpmOp = RPMSENSE_ANY;
1256       }
1257
1258       if (InternalDep((char*)depname, depver.c_str(), RpmOp) == true) {
1259          continue;
1260       }
1261       if (Type == pkgCache::Dep::Depends) {
1262          xmlChar *pre = xmlGetProp(n, (xmlChar*)"pre"); 
1263          if (pre) {
1264             Type = pkgCache::Dep::PreDepends;
1265             xmlFree(pre);
1266          }
1267       }
1268       Dependency *Dep = new Dependency;
1269       Dep->Name = string((char*)depname);
1270       xmlFree(depname);
1271       Dep->Version = depver;
1272       if (HideZeroEpoch && Dep->Version.substr(0, 2) == "0:") {
1273          Dep->Version = Dep->Version.substr(2);
1274       }
1275
1276       
1277       Dep->Op = Op;
1278       Dep->Type = Type;
1279       Deps.push_back(Dep);
1280    }
1281    return true;
1282 }
1283
1284 bool RPMRepomdHandler::Provides(vector<Dependency*> &Provs)
1285 {
1286    xmlNode *format = FindNode("format");
1287    xmlNode *provides = FindNode(format, "provides");
1288
1289    if (! provides)
1290       return true;
1291
1292    for (xmlNode *n = provides->children; n; n = n->next) {
1293       string depver = "";
1294       xmlChar *depname, *flags;
1295
1296       if (xmlStrcmp(n->name, (xmlChar*)"entry") != 0)  continue;
1297
1298       Dependency *Dep = new Dependency;
1299       if ((depname = xmlGetProp(n, (xmlChar*)"name")) == NULL) continue;
1300       Dep->Name = string((char*)depname);
1301       xmlFree(depname);
1302
1303       if ((flags = xmlGetProp(n, (xmlChar*)"flags"))) {
1304          xmlFree(flags);
1305
1306          xmlChar *epoch = xmlGetProp(n, (xmlChar*)"epoch");
1307          if (epoch) {
1308             depver += string((char*)epoch) + ":";
1309             xmlFree(epoch);
1310          }
1311          xmlChar *ver = xmlGetProp(n, (xmlChar*)"ver");
1312          if (ver) {
1313             depver += string((char*)ver);
1314             xmlFree(ver);
1315          }
1316          xmlChar *rel = xmlGetProp(n, (xmlChar*)"rel");
1317          if (rel) {
1318             depver += "-" + string((char*)rel);
1319             xmlFree(rel);
1320          }
1321
1322       }
1323       Dep->Version = depver;
1324       if (HideZeroEpoch && Dep->Version.substr(0, 2) == "0:") {
1325          Dep->Version = Dep->Version.substr(2);
1326       }
1327
1328       if (depver.empty() == false)
1329          Dep->Op = pkgCache::Dep::Equals;
1330       Provs.push_back(Dep);
1331    }
1332    return true;
1333 }
1334
1335 bool RPMRepomdHandler::FileProvides(vector<string> &FileProvs)
1336 {
1337    // XXX maybe this should be made runtime configurable?
1338    if ( !WithFilelist ) {
1339       xmlNode *format = FindNode("format");
1340       for (xmlNode *n = format->children; n; n = n->next) {
1341          if (xmlStrcmp(n->name, (xmlChar*)"file") != 0)  continue;
1342          xmlChar *Filename = xmlNodeGetContent(n);
1343          FileProvs.push_back(string((char*)Filename));
1344          xmlFree(Filename);
1345       }
1346    } else {
1347       xmlNode *FlP = xmlTextReaderExpand(Filelist);
1348       for (xmlNode *n = FlP->children; n; n = n->next) {
1349          if (xmlStrcmp(n->name, (xmlChar*)"file") != 0) 
1350             continue;
1351          xmlChar *Filename = xmlNodeGetContent(n);
1352          FileProvs.push_back(string((char*)Filename));
1353          xmlFree(Filename);
1354       }
1355    }
1356    return true;
1357 }
1358
1359 RPMRepomdHandler::~RPMRepomdHandler()
1360 {
1361    xmlFreeDoc(Primary);
1362    if (WithFilelist)
1363       xmlFreeTextReader(Filelist);
1364 }
1365
1366 // vim:sts=3:sw=3