- oops, fix versioned rpm internal dependencies with repomd
[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
32 #include <apti18n.h>
33
34 #if RPM_VERSION >= 0x040100
35 #include <rpm/rpmts.h>
36 #include <rpm/rpmdb.h>
37 #include <rpm/rpmds.h>
38 #define rpmxxInitIterator(a,b,c,d) rpmtsInitIterator(a,(rpmTag)b,c,d)
39 #else
40 #define rpmxxInitIterator(a,b,c,d) rpmdbInitIterator(a,b,c,d)
41 #endif
42
43 string RPMHandler::Epoch()
44 {
45    char str[512];
46    int_32 count, type, *epoch;
47    assert(HeaderP != NULL);
48    int rc = headerGetEntry(HeaderP, RPMTAG_EPOCH,
49                            &type, (void**)&epoch, &count);
50    if (rc == 1 && count > 0) {
51       snprintf(str, sizeof(str), "%i", epoch[0]);
52       return str;
53    } else { 
54       return string(rc?str:"");
55    }
56 }
57
58 unsigned long RPMHandler::GetITag(rpmTag Tag)
59 {
60    char *str;
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    : WriteLock(WriteLock), Handler(0)
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)
892 {
893    //cout << "Repomd handler constr. " << File << endl;
894    ID = File;
895    Root = NULL;
896
897    Primary = xmlReadFile(File.c_str(), NULL, XML_PARSE_NONET);
898    if ((Root = xmlDocGetRootElement(Primary)) == NULL) {
899       xmlFreeDoc(Primary);
900       cout << "getting root element failed" << endl;
901    }
902    NodeP = Root->children;
903    iSize = atoi((char*)xmlGetProp(Root, (xmlChar*)"packages"));
904
905    if (NodeP == NULL)
906       cout << "NodeP is null, ugh..." << endl;
907 }
908
909 bool RPMRepomdHandler::Skip()
910 {
911    //iOffset++;
912    //cout << "Repomd handler skip, offset " << iOffset << endl;
913    for (NodeP = NodeP->next; NodeP; NodeP = NodeP->next) {
914       //cout << "skip() current  " << NodeP << endl;
915       if (NodeP->type != XML_ELEMENT_NODE || strcmp((char*)NodeP->name, "package") != 0) {
916          continue;
917       } else {
918          iOffset++;
919          //cout << "in node " << xmlNodePGetContent(node) << endl;
920          return true;
921       }
922    } 
923    return false;
924    
925 }
926
927 bool RPMRepomdHandler::Jump(unsigned int Offset)
928 {
929    NodeP = Root->children;
930    iOffset = 0;
931    for (NodeP = NodeP->next; NodeP; NodeP = NodeP->next) {
932       if (iOffset+1 == Offset) {
933          return Skip();
934       }
935       if (NodeP->type != XML_ELEMENT_NODE || 
936           strcmp((char*)NodeP->name, "package") != 0) {
937          continue;
938       } else {
939          iOffset++;
940          //cout << "at offset " << iOffset << " of " << Offset << endl;
941       }
942    }
943    return false;
944 }
945
946 void RPMRepomdHandler::Rewind()
947 {
948    //cout << "Repomd handler rewind" << endl;
949    iOffset = 0;
950    NodeP = Root->children;
951 }
952
953 xmlNode *RPMRepomdHandler::FindNode(const string Name)
954 {
955    for (xmlNode *n = NodeP->children; n; n = n->next) {
956       if (strcmp((char*)n->name, Name.c_str()) == 0) {
957          return n;
958       }
959    }
960    return NULL;
961 }
962
963 xmlNode *RPMRepomdHandler::FindNode(xmlNode *Node, const string Name)
964 {
965    for (xmlNode *n = Node->children; n; n = n->next) {
966       if (strcmp((char*)n->name, Name.c_str()) == 0) {
967          return n;
968       }
969    }
970    return NULL;
971 }
972
973 string RPMRepomdHandler::FindTag(xmlNode *Node, string Tag)
974 {
975    xmlNode *n = FindNode(Node, Tag);
976    if (n) {
977       return (char*)xmlNodeGetContent(n);
978    } else {
979       return "";
980    }
981 }
982
983 string RPMRepomdHandler::GetContent(xmlNode *Node, string Tag)
984 {
985    if (Node) {
986       return (char*)xmlNodeGetContent(Node);
987    } else {
988       return "";
989    }
990 }
991
992 string RPMRepomdHandler::GetProp(xmlNode *Node, char *Prop)
993 {
994    if (Node) {
995       return (char*)xmlGetProp(Node, (xmlChar*)Prop);
996    } else {
997       return "";
998    }
999 }
1000
1001 string RPMRepomdHandler::Group()
1002 {
1003    xmlNode *n = FindNode("format");
1004    return FindTag(n, "group");
1005 }
1006
1007 string RPMRepomdHandler::Vendor()
1008 {
1009    xmlNode *n = FindNode("format");
1010    return FindTag(n, "vendor");
1011 }
1012
1013 string RPMRepomdHandler::Release()
1014 {
1015    xmlNode *n = FindNode("version");
1016    return GetProp(n, "rel");
1017 }
1018
1019 string RPMRepomdHandler::Version()
1020 {
1021    xmlNode *n = FindNode("version");
1022    return GetProp(n, "ver");
1023 }
1024
1025 string RPMRepomdHandler::Epoch()
1026 {
1027    xmlNode *n = FindNode("version");
1028    return GetProp(n, "epoch");
1029 }
1030
1031 string RPMRepomdHandler::FileName()
1032 {
1033    xmlNode *n;
1034    if ((n = FindNode("location"))) {
1035       return basename((char*)xmlGetProp(n, (xmlChar*)"href"));
1036    } else {
1037       return "";
1038    }
1039 }
1040
1041 string RPMRepomdHandler::Directory()
1042 {
1043    xmlNode *n;
1044    char *dir = "";
1045    if ((n = FindNode("location"))) {
1046       dir = dirname((char*)xmlGetProp(n, (xmlChar*)"href"));
1047    }
1048    return dir;
1049 }
1050
1051 string RPMRepomdHandler::MD5Sum()
1052 {
1053    // XXX FIXME the method should be an abstract Checksum type using
1054    // md5 / sha1 appropriately, for now relying on hacks elsewhere..
1055    return SHA1Sum();
1056 }
1057
1058 string RPMRepomdHandler::SHA1Sum()
1059 {
1060    xmlNode *n;
1061    if ((n = FindNode("checksum"))) {
1062       return (char*)xmlNodeGetContent(n);
1063    } else {
1064       return "";
1065    }
1066 }
1067 unsigned long RPMRepomdHandler::FileSize()
1068 {
1069    xmlNode *n;
1070    if ((n = FindNode("size"))) {
1071       return atol((char*)xmlGetProp(n, (xmlChar*)"package"));
1072    } else {
1073       return 0;
1074    }
1075 }
1076
1077 unsigned long RPMRepomdHandler::InstalledSize()
1078 {
1079    xmlNode *n;
1080    if ((n = FindNode("size"))) {
1081       return atol((char*)xmlGetProp(n, (xmlChar*)"installed"));
1082    } else {
1083       return 0;
1084    }
1085 }
1086
1087 bool RPMRepomdHandler::HasFile(const char *File)
1088 {
1089    if (*File == '\0')
1090       return false;
1091
1092    bool inprimary = false;
1093    bool found = false;
1094
1095    xmlNode *format = FindNode("format");
1096    for (xmlNode *n = format->children; n; n = n->next) {
1097       if (strcmp((char*)n->name, "file") != 0) 
1098          continue;
1099       if (strcmp(File, (char*)xmlNodeGetContent(n)) == 0) {
1100          //cout << "file in primary: " << File << endl;
1101          inprimary = true;
1102          break;
1103       }
1104    }
1105    // XXX FIXME: should plunge into filelist.xml if not found
1106    // in primary.xml
1107    found = inprimary;
1108    return found;
1109
1110 }
1111
1112 bool RPMRepomdHandler::Depends(unsigned int Type, vector<Dependency*> &Deps)
1113 {
1114    xmlNode *format = FindNode("format");
1115    xmlNode *dco;
1116
1117    xmlNode *n;
1118
1119    switch (Type) {
1120       case pkgCache::Dep::Depends:
1121          dco = FindNode(format, "requires");
1122          break;
1123       case pkgCache::Dep::Conflicts:
1124          dco = FindNode(format, "conflicts");
1125          break;
1126       case pkgCache::Dep::Obsoletes:
1127          dco = FindNode(format, "obsoletes");
1128          break;
1129    }
1130
1131    if (! dco) {
1132       return true;
1133    }
1134    for (n = dco->children; n; n = n->next) {
1135       unsigned int Op;
1136       int_32 RpmOp;
1137       string deptype, depver;
1138       char *ver, *rel, *epoch, *depname, *flags, *pre;
1139       if ((depname = (char*)xmlGetProp(n, (xmlChar*)"name")) == NULL) continue;
1140
1141       if ((flags = (char*)xmlGetProp(n, (xmlChar*)"flags"))) {
1142          ver = (char*)xmlGetProp(n, (xmlChar*)"ver");
1143          rel = (char*)xmlGetProp(n, (xmlChar*)"rel");
1144          epoch = (char*)xmlGetProp(n, (xmlChar*)"epoch");
1145          if (epoch)
1146             depver += string(epoch) + ":";
1147          ver = (char*)xmlGetProp(n, (xmlChar*)"ver");
1148          if (ver)
1149             depver += string(ver);
1150          rel = (char*)xmlGetProp(n, (xmlChar*)"rel");
1151          if (rel)
1152             depver += "-" + string(rel);
1153
1154          deptype = flags;
1155
1156          if (deptype == "EQ") {
1157             Op = pkgCache::Dep::Equals;
1158             RpmOp = RPMSENSE_EQUAL;
1159          } else if (deptype == "GE") {
1160             Op = pkgCache::Dep::GreaterEq;
1161             RpmOp = RPMSENSE_GREATER | RPMSENSE_EQUAL;
1162          } else if (deptype == "GT") {
1163             Op = pkgCache::Dep::Greater;
1164             RpmOp = RPMSENSE_GREATER;
1165          } else if (deptype == "LE") {
1166             Op = pkgCache::Dep::LessEq;
1167             RpmOp = RPMSENSE_LESS | RPMSENSE_EQUAL;
1168          } else if (deptype == "LT") {
1169             Op = pkgCache::Dep::Less;
1170             RpmOp = RPMSENSE_LESS;
1171          } else {
1172             Op = pkgCache::Dep::NoOp;
1173             RpmOp = RPMSENSE_ANY;
1174          }
1175       }
1176       if (InternalDep(depname, depver.c_str(), RpmOp) == true) {
1177          continue;
1178       }
1179       if (Type == pkgCache::Dep::Depends) {
1180          pre = (char*)xmlGetProp(n, (xmlChar*)"pre"); 
1181          if (pre) {
1182             Type = pkgCache::Dep::PreDepends;
1183          }
1184       }
1185       Dependency *Dep = new Dependency;
1186       Dep->Name = depname;
1187       Dep->Version = depver;
1188       Dep->Op = Op;
1189       Dep->Type = Type;
1190       Deps.push_back(Dep);
1191    }
1192    return true;
1193 }
1194
1195 bool RPMRepomdHandler::Provides(vector<Dependency*> &Provs)
1196 {
1197    xmlNode *format = FindNode("format");
1198    xmlNode *provides = FindNode(format, "provides");
1199    bool ret = true;
1200
1201    if (! provides)
1202       return true;
1203
1204    for (xmlNode *n = provides->children; n; n = n->next) {
1205       string depver = "";
1206       char *ver, *rel, *epoch, *depname, *flags;
1207
1208       if (strcmp((char*)n->name, "entry") != 0)  continue;
1209
1210       Dependency *Dep = new Dependency;
1211       if ((depname = (char*)xmlGetProp(n, (xmlChar*)"name")) == NULL) continue;
1212
1213       if ((flags = (char*)xmlGetProp(n, (xmlChar*)"flags"))) {
1214          epoch = (char*)xmlGetProp(n, (xmlChar*)"epoch");
1215          if (epoch)
1216             depver += string(epoch) + ":";
1217          ver = (char*)xmlGetProp(n, (xmlChar*)"ver");
1218          if (ver)
1219             depver += string(ver);
1220          rel = (char*)xmlGetProp(n, (xmlChar*)"rel");
1221          if (rel)
1222             depver += "-" + string(rel);
1223
1224       }
1225       Dep->Name = depname;
1226       Dep->Version = depver;
1227       if (depver.empty() == false)
1228          Dep->Op = pkgCache::Dep::Equals;
1229       Provs.push_back(Dep);
1230    }
1231    return true;
1232 }
1233
1234 bool RPMRepomdHandler::FileProvides(vector<string> &FileProvs)
1235 {
1236    xmlNode *format = FindNode("format");
1237    for (xmlNode *n = format->children; n; n = n->next) {
1238       if (strcmp((char*)n->name, "file") != 0)  continue;
1239       FileProvs.push_back((char*)xmlNodeGetContent(n));
1240    }
1241    return true;
1242 }
1243
1244 unsigned short RPMRepomdHandler::VersionHash()
1245 {
1246    // XXX FIXME: rpmlistparser versionhash for all the things we should do here
1247    unsigned long Result = INIT_FCS;
1248    string nevra = Name() + Version() + Arch();
1249    Result = AddCRC16(Result, nevra.c_str(), nevra.length());
1250    //cout << "versionhash: " << Result << endl;
1251    return Result;
1252 }
1253
1254
1255
1256 RPMRepomdHandler::~RPMRepomdHandler()
1257 {
1258    xmlFreeDoc(Primary);
1259 }
1260
1261 // vim:sts=3:sw=3