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