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