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