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