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