Replace bunch of rpm version checks with feature checks to get this to
[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 <signal.h>
20 #include <assert.h>
21 #include <libgen.h>
22
23 #include <apt-pkg/error.h>
24 #include <apt-pkg/configuration.h>
25 #include <apt-pkg/md5.h>
26 #include <apt-pkg/crc-16.h>
27
28 #include <apt-pkg/rpmhandler.h>
29 #include <apt-pkg/rpmpackagedata.h>
30
31 #ifdef APT_WITH_REPOMD
32 #include <libxml/parser.h>
33 #include <libxml/tree.h>
34 #include <libxml/xmlreader.h>
35 #include <sstream>
36 #ifdef WITH_SQLITE3
37 #include <apt-pkg/sqlite.h>
38 #endif
39 #include "xmlutil.h"
40 #endif
41
42 #include <apti18n.h>
43
44 #if RPM_VERSION >= 0x040100
45 #include <rpm/rpmts.h>
46 #include <rpm/rpmdb.h>
47 #include <rpm/rpmds.h>
48 #include <rpm/rpmfi.h>
49
50 // XXX we'd need to include rpmsq.h but it's not valid C++ in many
51 // existing rpm versions so just declare rpmsqCaught extern.. sigh.
52 #if 1
53 extern sigset_t rpmsqCaught;
54 #else
55 #include <rpm/rpmsq.h>
56 #endif
57
58 #define rpmxxInitIterator(a,b,c,d) rpmtsInitIterator(a,(rpmTag)b,c,d)
59 #else
60 #define rpmxxInitIterator(a,b,c,d) rpmdbInitIterator(a,b,c,d)
61 #endif
62
63 // An attempt to deal with false zero epochs from repomd. With older rpm's we
64 // can only blindly trust the repo admin created the repository with options
65 // suitable for those versions. For rpm >= 4.2.1 this is linked with
66 // promoteepoch behavior - if promoteepoch is used then epoch hiding must
67 // not happen.
68 bool HideZeroEpoch;
69
70 extern map<string,int> rpmIndexSizes;
71
72 string RPMHandler::EVR()
73 {
74    string e = Epoch();
75    string v = Version();
76    string r = Release();
77    string evr = "";
78    if (e.empty() == true) {
79       evr = v + '-' + r;
80    } else if (HideZeroEpoch && e == "0") {
81       evr = v + '-' + r;
82    } else {
83       evr = e + ':' + v + '-' + r;
84    }
85    return evr;
86
87
88 unsigned int RPMHandler::DepOp(int_32 rpmflags)
89 {
90    unsigned int Op = 0;
91    int_32 flags = (rpmflags & RPMSENSE_SENSEMASK);
92    if (flags == RPMSENSE_ANY) {
93       Op = pkgCache::Dep::NoOp;
94    } else if (flags & RPMSENSE_LESS) {
95       if (flags & RPMSENSE_EQUAL)
96           Op = pkgCache::Dep::LessEq;
97       else
98           Op = pkgCache::Dep::Less;
99    } else if (flags & RPMSENSE_GREATER) {
100       if (flags & RPMSENSE_EQUAL)
101           Op = pkgCache::Dep::GreaterEq;
102       else
103           Op = pkgCache::Dep::Greater;
104    } else if (flags & RPMSENSE_EQUAL) {
105       Op = pkgCache::Dep::Equals;
106    } else {
107       /* can't happen, right? */
108       _error->Error(_("Impossible flags %d in %s"), rpmflags, Name().c_str());
109    }
110       
111    return Op;
112 }
113
114 bool RPMHandler::HasFile(const char *File)
115 {
116    if (*File == '\0')
117       return false;
118    
119    vector<string> Files;
120    FileList(Files);
121    for (vector<string>::iterator I = Files.begin(); I != Files.end(); I++) {
122       if (string(File) == (*I)) {
123          return true;
124       }
125    }
126    return false;
127 }
128
129 bool RPMHandler::InternalDep(const char *name, const char *ver, int_32 flag) 
130 {
131    if (strncmp(name, "rpmlib(", strlen("rpmlib(")) == 0) {
132 #if RPM_VERSION >= 0x040100
133 #if RPM_HAVE_DSRPMLIB
134      rpmds rpmlibProv = NULL;
135      rpmds ds = rpmdsSingle(RPMTAG_PROVIDENAME,
136                             name, ver?ver:NULL, flag);
137      rpmdsRpmlib(&rpmlibProv, NULL);
138      int res = rpmdsSearch(rpmlibProv, ds) >= 0;
139      rpmdsFree(ds);
140      rpmdsFree(rpmlibProv);
141 #else
142       rpmds ds = rpmdsSingle(RPMTAG_PROVIDENAME,
143                              name, ver?ver:NULL, flag);
144       int res = rpmCheckRpmlibProvides(ds);
145       rpmdsFree(ds);
146 #endif
147 #else
148       int res = rpmCheckRpmlibProvides(name, ver?ver:NULL,
149                                        flag);
150 #endif
151       if (res) 
152          return true;
153    }
154
155 #if RPM_HAVE_DSGETCONF
156    // uhhuh, any of these changing would require full cache rebuild...
157    if (strncmp(name, "getconf(", strlen("getconf(")) == 0)
158    {
159      rpmds getconfProv = NULL;
160      rpmds ds = rpmdsSingle(RPMTAG_PROVIDENAME,
161                             name, ver?ver:NULL, flag);
162      rpmdsGetconf(&getconfProv, NULL);
163      int res = rpmdsSearch(getconfProv, ds);
164      rpmdsFree(ds);
165      rpmdsFree(getconfProv);
166      if (res) 
167          return true;
168    }
169
170    if (strncmp(name, "cpuinfo(", strlen("cpuinfo(")) == 0)
171    {
172      rpmds cpuinfoProv = NULL;
173      rpmds ds = rpmdsSingle(RPMTAG_PROVIDENAME,
174                             name, ver?ver:NULL, flag);
175      rpmdsCpuinfo(&cpuinfoProv, NULL);
176      int res = rpmdsSearch(cpuinfoProv, ds);
177      rpmdsFree(ds);
178      rpmdsFree(cpuinfoProv);
179      if (res) 
180          return true;
181    }
182
183    if (strncmp(name, "sysinfo(", strlen("sysinfo(")) == 0)
184    {
185      rpmds sysinfoProv = NULL;
186      rpmds ds = rpmdsSingle(RPMTAG_PROVIDENAME,
187                             name, ver?ver:NULL, flag);
188      rpmdsCpuinfo(&sysinfoProv, NULL);
189      int res = rpmdsSearch(sysinfoProv, ds);
190      rpmdsFree(ds);
191      rpmdsFree(sysinfoProv);
192      if (res)
193          return true;
194    }
195
196    if (strncmp(name, "uname(", strlen("uname(")) == 0)
197    {
198      rpmds unameProv = NULL;
199      rpmds ds = rpmdsSingle(RPMTAG_PROVIDENAME,
200                             name, ver?ver:NULL, flag);
201      rpmdsUname(&unameProv, NULL);
202      int res = rpmdsSearch(unameProv, ds);
203      rpmdsFree(ds);
204      rpmdsFree(unameProv);
205      if (res)
206          return true;
207    }
208
209    if (strlen(name) > 5 && name[strlen(name)-1] == ')' &&
210        ((strchr("Rr_", name[0]) != NULL &&
211          strchr("Ww_", name[1]) != NULL &&
212          strchr("Xx_", name[2]) != NULL &&
213          name[3] == '(') ||
214          strncmp(name, "exists(", strlen("exists(")) == 0 ||
215          strncmp(name, "executable(", strlen("executable(")) == 0 ||
216          strncmp(name, "readable(", strlen("readable(")) == 0 ||
217          strncmp(name, "writable(", strlen("writable("))== 0 ))
218    {
219       int res = rpmioAccess(name, NULL, X_OK);
220       if (res == 0)
221          return true;
222    }
223
224    /* TODO
225     * - /etc/rpm/sysinfo provides
226     * - macro probe provides 
227     * - actually implement soname() and access() dependencies
228     */
229    if (strncmp(name, "soname(", strlen("soname(")) == 0)
230    {
231       cout << "FIXME, ignoring soname() dependency: " << name << endl;
232       return true;
233    }
234 #endif
235    return false; 
236 }
237
238 bool RPMHandler::PutDep(const char *name, const char *ver, int_32 flags, 
239                         unsigned int Type, vector<Dependency*> &Deps)
240 {
241    if (InternalDep(name, ver, flags) == true) {
242       return true;
243    }
244
245    if (Type == pkgCache::Dep::Depends) {
246       if (flags & RPMSENSE_PREREQ)
247          Type = pkgCache::Dep::PreDepends;
248 #if RPM_VERSION >= 0x040403
249       else if (flags & RPMSENSE_MISSINGOK)
250          Type = pkgCache::Dep::Suggests;
251 #endif
252       else
253          Type = pkgCache::Dep::Depends;
254    }
255
256    Dependency *Dep = new Dependency;
257    Dep->Name = name;
258    Dep->Version = ver;
259
260    if (HideZeroEpoch && Dep->Version.substr(0, 2) == "0:") {
261       Dep->Version = Dep->Version.substr(2);
262    }
263
264    Dep->Op = DepOp(flags);
265    Dep->Type = Type;
266    Deps.push_back(Dep);
267    return true;
268 }
269
270 string RPMHdrHandler::Epoch()
271 {
272    char str[512] = "";
273    int_32 count, type, *epoch;
274    void *val;
275    assert(HeaderP != NULL);
276    int rc = headerGetEntry(HeaderP, RPMTAG_EPOCH, &type, &val, &count);
277    epoch = (int_32*)val;
278    if (rc == 1 && count > 0) {
279       snprintf(str, sizeof(str), "%i", epoch[0]);
280    }
281    return string(str);
282 }
283
284 off_t RPMHdrHandler::GetITag(rpmTag Tag)
285 {
286    int_32 count, type, *num;
287    void *val;
288    assert(HeaderP != NULL);
289    int rc = headerGetEntry(HeaderP, Tag,
290                            &type, (void**)&val, &count);
291    num = (int_32*)val;
292    return rc?num[0]:0;
293 }
294
295 string RPMHdrHandler::GetSTag(rpmTag Tag)
296 {
297    char *str;
298    void *val;
299    int_32 count, type;
300    assert(HeaderP != NULL);
301    int rc = headerGetEntry(HeaderP, Tag,
302                            &type, (void**)&val, &count);
303    str = (char *)val;
304    return string(rc?str:"");
305 }
306
307
308 bool RPMHdrHandler::PRCO(unsigned int Type, vector<Dependency*> &Deps)
309 #if RPM_VERSION >= 0x040100
310 {
311    rpmTag deptype = RPMTAG_REQUIRENAME;
312    switch (Type) {
313       case pkgCache::Dep::Depends:
314          deptype = RPMTAG_REQUIRENAME;
315          break;
316       case pkgCache::Dep::Obsoletes:
317          deptype = RPMTAG_OBSOLETENAME;
318          break;
319       case pkgCache::Dep::Conflicts:
320          deptype = RPMTAG_CONFLICTNAME;
321          break;
322       case pkgCache::Dep::Provides:
323          deptype = RPMTAG_PROVIDENAME;
324          break;
325 #if RPM_VERSION >= 0x040403
326       case pkgCache::Dep::Suggests:
327          deptype = RPMTAG_SUGGESTSNAME;
328          break;
329 #if 0 // Enhances dep type is not even known to apt, sigh..
330       case pkgCache::Dep::Enhances:
331          deptype = RPMTAG_ENHANCES;
332          break;
333 #endif
334 #endif
335       default:
336          /* can't happen... right? */
337          return false;
338          break;
339    }
340    rpmds ds = NULL;
341    ds = rpmdsNew(HeaderP, deptype, 0);
342    if (ds != NULL) {
343       while (rpmdsNext(ds) >= 0) {
344          bool r = PutDep(rpmdsN(ds), rpmdsEVR(ds), rpmdsFlags(ds), Type, Deps);
345       }
346    }
347    rpmdsFree(ds);
348    return true;
349 }
350 #else
351 bool RPMHdrHandler::PRCO(unsigned int Type, vector<Dependency*> &Deps)
352 {
353    char **namel = NULL;
354    char **verl = NULL;
355    int *flagl = NULL;
356    int res, type, count;
357    int_32 deptag, depver, depflags;
358    void *nameval = NULL;
359    void *verval = NULL;
360    void *flagval = NULL;
361
362    switch (Type) {
363       case pkgCache::Dep::Depends:
364          deptag = RPMTAG_REQUIRENAME;
365          depver = RPMTAG_REQUIREVERSION;
366          depflags = RPMTAG_REQUIREFLAGS;
367          break;
368       case pkgCache::Dep::Obsoletes:
369          deptag = RPMTAG_OBSOLETENAME;
370          depver = RPMTAG_OBSOLETEVERSION;
371          depflags = RPMTAG_OBSOLETEFLAGS;
372          break;
373       case pkgCache::Dep::Conflicts:
374          deptag = RPMTAG_CONFLICTNAME;
375          depver = RPMTAG_CONFLICTVERSION;
376          depflags = RPMTAG_CONFLICTFLAGS;
377          break;
378       case pkgCache::Dep::Provides:
379          deptag = RPMTAG_PROVIDENAME;
380          depver = RPMTAG_PROVIDEVERSION;
381          depflags = RPMTAG_PROVIDEFLAGS;
382          break;
383       default:
384          /* can't happen... right? */
385          return false;
386          break;
387    }
388    res = headerGetEntry(HeaderP, deptag, &type, (void **)&nameval, &count);
389    if (res != 1)
390       return true;
391    res = headerGetEntry(HeaderP, depver, &type, (void **)&verval, &count);
392    res = headerGetEntry(HeaderP, depflags, &type, (void **)&flagval, &count);
393
394    namel = (char**)nameval;
395    verl = (char**)verval;
396    flagl = (int*)flagval;
397
398    for (int i = 0; i < count; i++) {
399
400       bool res = PutDep(namel[i], verl[i], flagl[i], Type, Deps);
401    }
402    free(namel);
403    free(verl);
404    return true;
405       
406 }
407 #endif
408
409 // XXX rpmfi originates from somewhere around 2001 but what's the version?
410 #if RPM_VERSION >= 0x040100
411 bool RPMHdrHandler::FileList(vector<string> &FileList)
412 {
413    rpmfi fi = NULL;
414    fi = rpmfiNew(NULL, HeaderP, RPMTAG_BASENAMES, 0);
415    if (fi != NULL) {
416       while (rpmfiNext(fi) >= 0) {
417         FileList.push_back(rpmfiFN(fi));
418       }
419    }
420    fi = rpmfiFree(fi);
421    return true;
422 }
423 #else
424 bool RPMHdrHandler::FileList(vector<string> &FileList)
425 {
426    const char **names = NULL;
427    void *val = NULL;
428    int_32 count = 0;
429    bool ret = true;
430    rpmHeaderGetEntry(HeaderP, RPMTAG_OLDFILENAMES,
431                      NULL, (void **) &val, &count);
432    names = (const char **)val;
433    while (count--) {
434       FileList.push_back(names[count]);
435    }
436    free(names);
437    return ret;
438
439 }
440 #endif
441
442 bool RPMHdrHandler::ChangeLog(vector<ChangeLogEntry *> &ChangeLogs)
443 {
444    int *timel = NULL;
445    char **authorl = NULL;
446    char **entryl = NULL;
447    void *timeval, *authorval, *entryval;
448    int res, type, count;
449
450    res = headerGetEntry(HeaderP, RPMTAG_CHANGELOGTIME, &type, (void **)&timeval, &count);
451    res = headerGetEntry(HeaderP, RPMTAG_CHANGELOGNAME, &type, (void **)&authorval, &count);
452    res = headerGetEntry(HeaderP, RPMTAG_CHANGELOGTEXT, &type, (void **)&entryval, &count);
453
454    timel = (int*)timeval;
455    authorl = (char**)authorval;
456    entryl = (char**)entryval;
457
458    for (int i = 0; i < count; i++) {
459       ChangeLogEntry *Entry = new ChangeLogEntry;
460       Entry->Time = timel[i];
461       Entry->Author = authorl[i];
462       Entry->Text = entryl[i];
463       ChangeLogs.push_back(Entry);
464    }
465    free(entryl);
466    free(authorl);
467       
468    return true;
469 }
470
471 RPMFileHandler::RPMFileHandler(string File)
472 {
473    ID = File;
474    FD = Fopen(File.c_str(), "r");
475    if (FD == NULL)
476    {
477       /*
478       _error->Error(_("could not open RPM package list file %s: %s"),
479                     File.c_str(), rpmErrorString());
480       */
481       return;
482    }
483    iSize = fdSize(FD);
484    rpmIndexSizes[ID] = iSize;
485 }
486
487 RPMFileHandler::RPMFileHandler(FileFd *File)
488 {
489    FD = fdDup(File->Fd());
490    if (FD == NULL)
491    {
492       /*
493       _error->Error(_("could not create RPM file descriptor: %s"),
494                     rpmErrorString());
495       */
496       return;
497    }
498    iSize = fdSize(FD);
499    rpmIndexSizes[ID] = iSize;
500 }
501
502 RPMFileHandler::~RPMFileHandler()
503 {
504    if (HeaderP != NULL)
505       headerFree(HeaderP);
506    if (FD != NULL)
507       Fclose(FD);
508 }
509
510 bool RPMFileHandler::Skip()
511 {
512    if (FD == NULL)
513       return false;
514    iOffset = lseek(Fileno(FD),0,SEEK_CUR);
515    if (HeaderP != NULL)
516        headerFree(HeaderP);
517    HeaderP = headerRead(FD, HEADER_MAGIC_YES);
518    return (HeaderP != NULL);
519 }
520
521 bool RPMFileHandler::Jump(off_t Offset)
522 {
523    if (FD == NULL)
524       return false;
525    if (lseek(Fileno(FD),Offset,SEEK_SET) != Offset)
526       return false;
527    return Skip();
528 }
529
530 void RPMFileHandler::Rewind()
531 {
532    if (FD == NULL)
533       return;
534    iOffset = lseek(Fileno(FD),0,SEEK_SET);
535    if (iOffset != 0)
536       _error->Error(_("could not rewind RPMFileHandler"));
537 }
538
539 string RPMFileHandler::FileName()
540 {
541    return GetSTag(CRPMTAG_FILENAME);
542 }
543
544 string RPMFileHandler::Directory()
545 {
546    return GetSTag(CRPMTAG_DIRECTORY);
547 }
548
549 off_t RPMFileHandler::FileSize()
550 {
551    return GetITag(CRPMTAG_FILESIZE);
552 }
553
554 string RPMFileHandler::MD5Sum()
555 {
556    return GetSTag(CRPMTAG_MD5);
557 }
558
559 bool RPMSingleFileHandler::Skip()
560 {
561    if (FD == NULL)
562       return false;
563    if (HeaderP != NULL) {
564       headerFree(HeaderP);
565       HeaderP = NULL;
566       return false;
567    }
568 #if RPM_VERSION >= 0x040100
569    rpmts TS = rpmtsCreate();
570    rpmtsSetVSFlags(TS, (rpmVSFlags_e)-1);
571    int rc = rpmReadPackageFile(TS, FD, sFilePath.c_str(), &HeaderP);
572    if (rc != RPMRC_OK && rc != RPMRC_NOTTRUSTED && rc != RPMRC_NOKEY) {
573       _error->Error(_("Failed reading file %s"), sFilePath.c_str());
574       HeaderP = NULL;
575    }
576    rpmtsFree(TS);
577 #else
578    int rc = rpmReadPackageHeader(FD, &HeaderP, 0, NULL, NULL);
579    if (rc) {
580       _error->Error(_("Failed reading file %s"), sFilePath.c_str());
581       HeaderP = NULL;
582    }
583 #endif
584    return (HeaderP != NULL);
585 }
586
587 bool RPMSingleFileHandler::Jump(off_t Offset)
588 {
589    assert(Offset == 0);
590    Rewind();
591    return RPMFileHandler::Jump(Offset);
592 }
593
594 void RPMSingleFileHandler::Rewind()
595 {
596    if (FD == NULL)
597       return;
598    if (HeaderP != NULL) {
599       HeaderP = NULL;
600       headerFree(HeaderP);
601    }
602    lseek(Fileno(FD),0,SEEK_SET);
603 }
604
605 off_t RPMSingleFileHandler::FileSize()
606 {
607    struct stat S;
608    if (stat(sFilePath.c_str(),&S) != 0)
609       return 0;
610    return S.st_size;
611 }
612
613 string RPMSingleFileHandler::MD5Sum()
614 {
615    MD5Summation MD5;
616    FileFd File(sFilePath, FileFd::ReadOnly);
617    MD5.AddFD(File.Fd(), File.Size());
618    File.Close();
619    return MD5.Result().Value();
620 }
621
622 bool RPMSingleFileHandler::ChangeLog(vector<ChangeLogEntry* > &ChangeLogs)
623 {
624    return RPMHdrHandler::ChangeLog(ChangeLogs);
625 }
626
627 RPMDirHandler::RPMDirHandler(string DirName)
628    : sDirName(DirName)
629 {
630    ID = DirName;
631 #if RPM_VERSION >= 0x040100
632    TS = NULL;
633 #endif
634    Dir = opendir(sDirName.c_str());
635    if (Dir == NULL)
636       return;
637    iSize = 0;
638    while (nextFileName() != NULL)
639       iSize += 1;
640    rewinddir(Dir);
641 #if RPM_VERSION >= 0x040100
642    TS = rpmtsCreate();
643    rpmtsSetVSFlags(TS, (rpmVSFlags_e)-1);
644 #endif
645    rpmIndexSizes[ID] = iSize;
646 }
647
648 const char *RPMDirHandler::nextFileName()
649 {
650    for (struct dirent *Ent = readdir(Dir); Ent != 0; Ent = readdir(Dir))
651    {
652       const char *name = Ent->d_name;
653
654       if (name[0] == '.')
655          continue;
656
657       if (flExtension(name) != "rpm")
658          continue;
659
660       // Make sure it is a file and not something else
661       sFilePath = flCombine(sDirName,name);
662       struct stat St;
663       if (stat(sFilePath.c_str(),&St) != 0 || S_ISREG(St.st_mode) == 0)
664          continue;
665
666       sFileName = name;
667       
668       return name;
669    } 
670    return NULL;
671 }
672
673 RPMDirHandler::~RPMDirHandler()
674 {
675    if (HeaderP != NULL)
676       headerFree(HeaderP);
677 #if RPM_VERSION >= 0x040100
678    if (TS != NULL)
679       rpmtsFree(TS);
680 #endif
681    if (Dir != NULL)
682       closedir(Dir);
683 }
684
685 bool RPMDirHandler::Skip()
686 {
687    if (Dir == NULL)
688       return false;
689    if (HeaderP != NULL) {
690       headerFree(HeaderP);
691       HeaderP = NULL;
692    }
693    const char *fname = nextFileName();
694    bool Res = false;
695    for (; fname != NULL; fname = nextFileName()) {
696       iOffset++;
697       if (fname == NULL)
698          break;
699       FD_t FD = Fopen(sFilePath.c_str(), "r");
700       if (FD == NULL)
701          continue;
702 #if RPM_VERSION >= 0x040100
703       int rc = rpmReadPackageFile(TS, FD, fname, &HeaderP);
704       Fclose(FD);
705       if (rc != RPMRC_OK
706           && rc != RPMRC_NOTTRUSTED
707           && rc != RPMRC_NOKEY)
708          continue;
709 #else
710       int isSource;
711       int rc = rpmReadPackageHeader(FD, &HeaderP, &isSource, NULL, NULL);
712       Fclose(FD);
713       if (rc != 0)
714          continue;
715 #endif
716       Res = true;
717       break;
718    }
719    return Res;
720 }
721
722 bool RPMDirHandler::Jump(off_t Offset)
723 {
724    if (Dir == NULL)
725       return false;
726    rewinddir(Dir);
727    iOffset = 0;
728    while (1) {
729       if (iOffset+1 == Offset)
730          return Skip();
731       if (nextFileName() == NULL)
732          break;
733       iOffset++;
734    }
735    return false;
736 }
737
738 void RPMDirHandler::Rewind()
739 {
740    rewinddir(Dir);
741    iOffset = 0;
742 }
743
744 off_t RPMDirHandler::FileSize()
745 {
746    if (Dir == NULL)
747       return 0;
748    struct stat St;
749    if (stat(sFilePath.c_str(),&St) != 0) {
750       _error->Errno("stat",_("Unable to determine the file size"));
751       return 0;
752    }
753    return St.st_size;
754 }
755
756 string RPMDirHandler::MD5Sum()
757 {
758    if (Dir == NULL)
759       return "";
760    MD5Summation MD5;
761    FileFd File(sFilePath, FileFd::ReadOnly);
762    MD5.AddFD(File.Fd(), File.Size());
763    File.Close();
764    return MD5.Result().Value();
765 }
766
767
768 RPMDBHandler::RPMDBHandler(bool WriteLock)
769    : Handler(0), WriteLock(WriteLock)
770 {
771 #if RPM_VERSION >= 0x040000
772    RpmIter = NULL;
773 #endif
774    string Dir = _config->Find("RPM::RootDir");
775    
776    rpmReadConfigFiles(NULL, NULL);
777    ID = DataPath(false);
778
779    RPMPackageData::Singleton()->InitMinArchScore();
780
781    // Everytime we open a database for writing, it has its
782    // mtime changed, and kills our cache validity. As we never
783    // change any information in the database directly, we will
784    // restore the mtime and save our cache.
785    struct stat St;
786    stat(DataPath(false).c_str(), &St);
787    DbFileMtime = St.st_mtime;
788
789 #if RPM_VERSION >= 0x040100
790    Handler = rpmtsCreate();
791    rpmtsSetVSFlags(Handler, (rpmVSFlags_e)-1);
792    rpmtsSetRootDir(Handler, Dir.c_str());
793 #else
794    const char *RootDir = NULL;
795    if (!Dir.empty())
796       RootDir = Dir.c_str();
797    if (rpmdbOpen(RootDir, &Handler, O_RDONLY, 0644) != 0)
798    {
799       _error->Error(_("could not open RPM database"));
800       return;
801    }
802 #endif
803 #if RPM_VERSION >= 0x040000
804    RpmIter = rpmxxInitIterator(Handler, RPMDBI_PACKAGES, NULL, 0);
805    if (RpmIter == NULL) {
806       _error->Error(_("could not create RPM database iterator"));
807       return;
808    }
809    // iSize = rpmdbGetIteratorCount(RpmIter);
810    // This doesn't seem to work right now. Code in rpm (4.0.4, at least)
811    // returns a 0 from rpmdbGetIteratorCount() if rpmxxInitIterator() is
812    // called with RPMDBI_PACKAGES or with keyp == NULL. The algorithm
813    // below will be used until there's support for it.
814    iSize = 0;
815    rpmdbMatchIterator countIt;
816    countIt = rpmxxInitIterator(Handler, RPMDBI_PACKAGES, NULL, 0);
817    while (rpmdbNextIterator(countIt) != NULL)
818       iSize++;
819    rpmdbFreeIterator(countIt);
820 #else
821    iSize = St.st_size;
822
823 #endif
824    rpmIndexSizes[ID] = iSize;
825
826
827    // Restore just after opening the database, and just after closing.
828    if (WriteLock) {
829       struct utimbuf Ut;
830       Ut.actime = DbFileMtime;
831       Ut.modtime = DbFileMtime;
832       utime(DataPath(false).c_str(), &Ut);
833    }
834 }
835
836 RPMDBHandler::~RPMDBHandler()
837 {
838 #if RPM_VERSION >= 0x040000
839    if (RpmIter != NULL)
840       rpmdbFreeIterator(RpmIter);
841 #else
842    if (HeaderP != NULL)
843        headerFree(HeaderP);
844 #endif
845
846 #if RPM_VERSION >= 0x040100
847    /* 
848     * If termination signal, do nothing as rpmdb has already freed
849     * our ts set behind our back and rpmtsFree() will crash and burn with a 
850     * doublefree within rpmlib.
851     * There's a WTF involved as rpmCheckSignals() actually calls exit()
852     * so we shouldn't even get here really?!
853     */
854    if (sigismember(&rpmsqCaught, SIGINT) || 
855        sigismember(&rpmsqCaught, SIGQUIT) ||
856        sigismember(&rpmsqCaught, SIGHUP) ||
857        sigismember(&rpmsqCaught, SIGTERM) ||
858        sigismember(&rpmsqCaught, SIGPIPE)) {
859       /* do nothing */
860    } else if (Handler != NULL) {
861       rpmtsFree(Handler);
862    }
863 #else
864    if (Handler != NULL) {
865       rpmdbClose(Handler);
866    }
867 #endif
868
869    // Restore just after opening the database, and just after closing.
870    if (WriteLock) {
871       struct utimbuf Ut;
872       Ut.actime = DbFileMtime;
873       Ut.modtime = DbFileMtime;
874       utime(DataPath(false).c_str(), &Ut);
875    }
876 }
877
878 string RPMDBHandler::DataPath(bool DirectoryOnly)
879 {
880    string File = "packages.rpm";
881    char *tmp = (char *) rpmExpand("%{_dbpath}", NULL);
882    string DBPath(_config->Find("RPM::RootDir")+tmp);
883    free(tmp);
884
885 #if RPM_VERSION >= 0x040000
886    if (rpmExpandNumeric("%{_dbapi}") >= 3)
887       File = "Packages";       
888 #endif
889    if (DirectoryOnly == true)
890        return DBPath;
891    else
892        return DBPath+"/"+File;
893 }
894
895 bool RPMDBHandler::Skip()
896 {
897 #if RPM_VERSION >= 0x040000
898    if (RpmIter == NULL)
899        return false;
900    HeaderP = rpmdbNextIterator(RpmIter);
901    iOffset = rpmdbGetIteratorOffset(RpmIter);
902    if (HeaderP == NULL)
903       return false;
904 #else
905    if (iOffset == 0)
906       iOffset = rpmdbFirstRecNum(Handler);
907    else
908       iOffset = rpmdbNextRecNum(Handler, iOffset);
909    if (HeaderP != NULL)
910    {
911       headerFree(HeaderP);
912       HeaderP = NULL;
913    }
914    if (iOffset == 0)
915        return false;
916    HeaderP = rpmdbGetRecord(Handler, iOffset);
917 #endif
918    return true;
919 }
920
921 bool RPMDBHandler::Jump(off_t Offset)
922 {
923    iOffset = Offset;
924 #if RPM_VERSION >= 0x040000
925    // rpmdb indexes are hardcoded uint32_t, the size must match here
926    uint_32 rpmOffset = iOffset;
927    if (RpmIter == NULL)
928       return false;
929    rpmdbFreeIterator(RpmIter);
930    if (iOffset == 0)
931       RpmIter = rpmxxInitIterator(Handler, RPMDBI_PACKAGES, NULL, 0);
932    else {
933       RpmIter = rpmxxInitIterator(Handler, RPMDBI_PACKAGES,
934                                   &rpmOffset, sizeof(rpmOffset));
935       iOffset = rpmOffset;
936    }
937    HeaderP = rpmdbNextIterator(RpmIter);
938 #else
939    HeaderP = rpmdbGetRecord(Handler, iOffset);
940 #endif
941    return true;
942 }
943
944 bool RPMDBHandler::JumpByName(string PkgName)
945 {
946    if (RpmIter == NULL) return false;
947    rpmdbFreeIterator(RpmIter);
948 #if RPM_VERSION >= 0x040100
949    RpmIter = rpmtsInitIterator(Handler, (rpmTag)RPMDBI_LABEL, PkgName.c_str(), 0);
950 #else
951    RpmIter = rpmdbInitIterator(Handler, RPMDBI_LABEL, PkgName.c_str(), 0);
952 #endif
953
954    HeaderP = rpmdbNextIterator(RpmIter);
955    return (HeaderP != NULL);
956 }
957
958 void RPMDBHandler::Rewind()
959 {
960 #if RPM_VERSION >= 0x040000
961    if (RpmIter == NULL)
962       return;
963    rpmdbFreeIterator(RpmIter);   
964    RpmIter = rpmxxInitIterator(Handler, RPMDBI_PACKAGES, NULL, 0);
965 #else
966    if (HeaderP != NULL)
967    {
968       headerFree(HeaderP);
969       HeaderP = NULL;
970    }
971 #endif
972    iOffset = 0;
973 }
974 #endif
975
976 #ifdef APT_WITH_REPOMD
977 RPMRepomdHandler::RPMRepomdHandler(string File): RPMHandler(),
978       Root(NULL), Primary(NULL), PrimaryPath(File), HavePrimary(false)
979 {
980    string DBBase = PrimaryPath.substr(0, File.size() - strlen("primary.xml"));
981    FilelistPath = DBBase + "filelists.xml";
982    OtherPath = DBBase + "other.xml";
983
984    ID = File;
985
986    xmlTextReaderPtr Index;
987    unsigned long Res;
988    Index = xmlReaderForFile(PrimaryPath.c_str(), NULL,
989                           XML_PARSE_NONET|XML_PARSE_NOBLANKS);
990    if (Index == NULL) {
991       _error->Error(_("Failed to open package index %s"), PrimaryPath.c_str());
992       return;
993    }
994
995    if (xmlTextReaderRead(Index) == 1) {
996       xmlChar *pkgs = xmlTextReaderGetAttribute(Index, (xmlChar*)"packages");
997       iSize = atoi((char*)pkgs);
998       xmlFree(pkgs);
999    } else {
1000       iSize = 0;
1001    }
1002    xmlFreeTextReader(Index);
1003    rpmIndexSizes[ID] = iSize;
1004
1005 }
1006
1007 bool RPMRepomdHandler::LoadPrimary()
1008 {
1009    xmlChar *packages = NULL;
1010    off_t pkgcount = 0;
1011
1012    Primary = xmlReadFile(ID.c_str(), NULL, XML_PARSE_NONET|XML_PARSE_NOBLANKS);
1013    if ((Root = xmlDocGetRootElement(Primary)) == NULL) {
1014       _error->Error(_("Failed to open package index %s"), PrimaryPath.c_str());
1015       goto error;
1016    }
1017    if (xmlStrncmp(Root->name, (xmlChar*)"metadata", strlen("metadata")) != 0) {
1018       _error->Error(_("Corrupted package index %s"), PrimaryPath.c_str());
1019       goto error;
1020    }
1021
1022    packages = xmlGetProp(Root, (xmlChar*)"packages");
1023    iSize = atoi((char*)packages);
1024    xmlFree(packages);
1025    for (xmlNode *n = Root->children; n; n = n->next) {
1026       if (n->type != XML_ELEMENT_NODE ||
1027           xmlStrcmp(n->name, (xmlChar*)"package") != 0)
1028          continue;
1029       Pkgs.push_back(n);
1030       pkgcount++;
1031    }
1032    PkgIter = Pkgs.begin();
1033
1034    // There seem to be broken version(s) of createrepo around which report
1035    // to have one more package than is in the repository. Warn and work around.
1036    if (iSize != pkgcount) {
1037       _error->Warning(_("Inconsistent metadata, package count doesn't match in %s"), ID.c_str());
1038       iSize = pkgcount;
1039    }
1040    rpmIndexSizes[ID] = iSize;
1041    HavePrimary = true;
1042
1043    return true;
1044
1045 error:
1046    if (Primary) {
1047       xmlFreeDoc(Primary);
1048    }
1049    return false;
1050 }
1051
1052 bool RPMRepomdHandler::Skip()
1053 {
1054    if (HavePrimary == false) {
1055       LoadPrimary();
1056    }
1057    if (PkgIter == Pkgs.end()) {
1058       return false;
1059    }
1060    NodeP = *PkgIter;
1061    iOffset = PkgIter - Pkgs.begin();
1062
1063    PkgIter++;
1064    return true;
1065 }
1066
1067 bool RPMRepomdHandler::Jump(off_t Offset)
1068 {
1069    if (HavePrimary == false) {
1070       LoadPrimary();
1071    }
1072    if (Offset >= iSize) {
1073       return false;
1074    }
1075    iOffset = Offset;
1076    NodeP = Pkgs[Offset];
1077    // This isn't strictly necessary as Skip() and Jump() aren't mixed
1078    // in practise but doesn't hurt either...
1079    PkgIter = Pkgs.begin() + Offset + 1;
1080    return true;
1081
1082 }
1083
1084 void RPMRepomdHandler::Rewind()
1085 {
1086    iOffset = 0;
1087    PkgIter = Pkgs.begin();
1088 }
1089
1090 string RPMRepomdHandler::Name() 
1091 {
1092    return XmlFindNodeContent(NodeP, "name");
1093 }
1094
1095 string RPMRepomdHandler::Arch() 
1096 {
1097    return XmlFindNodeContent(NodeP, "arch");
1098 }
1099
1100 string RPMRepomdHandler::Packager() 
1101 {
1102    return XmlFindNodeContent(NodeP, "packager");
1103 }
1104
1105 string RPMRepomdHandler::Summary() 
1106 {
1107    return XmlFindNodeContent(NodeP, "summary");
1108 }
1109
1110 string RPMRepomdHandler::Description() 
1111 {
1112    return XmlFindNodeContent(NodeP, "description");
1113 }
1114
1115 string RPMRepomdHandler::Group()
1116 {
1117    xmlNode *n = XmlFindNode(NodeP, "format");
1118    return XmlFindNodeContent(n, "group");
1119 }
1120
1121 string RPMRepomdHandler::Vendor()
1122 {
1123    xmlNode *n = XmlFindNode(NodeP, "format");
1124    return XmlFindNodeContent(n, "vendor");
1125 }
1126
1127 string RPMRepomdHandler::Release()
1128 {
1129    xmlNode *n = XmlFindNode(NodeP, "version");
1130    return XmlGetProp(n, "rel");
1131 }
1132
1133 string RPMRepomdHandler::Version()
1134 {
1135    xmlNode *n = XmlFindNode(NodeP, "version");
1136    return XmlGetProp(n, "ver");
1137 }
1138
1139 string RPMRepomdHandler::Epoch()
1140 {
1141    string epoch;
1142    xmlNode *n = XmlFindNode(NodeP, "version");
1143    epoch = XmlGetProp(n, "epoch");
1144    // XXX createrepo stomps epoch zero on packages without epoch, hide
1145    // them. Rpm treats zero and empty equally anyway so it doesn't matter.
1146    if (epoch == "0")
1147       epoch = "";
1148    return epoch;
1149 }
1150
1151 string RPMRepomdHandler::FileName()
1152 {
1153    xmlNode *n;
1154    string str = "";
1155    if ((n = XmlFindNode(NodeP, "location"))) {
1156       xmlChar *prop = xmlGetProp(n, (xmlChar*)"href");
1157       str = basename((char*)prop);
1158       xmlFree(prop);
1159    }
1160    return str;
1161 }
1162
1163 string RPMRepomdHandler::Directory()
1164 {
1165    xmlNode *n;
1166    string str = "";
1167    if ((n = XmlFindNode(NodeP, "location"))) {
1168       xmlChar *prop = xmlGetProp(n, (xmlChar*)"href");
1169       if (prop) {
1170          str = dirname((char*)prop);
1171          xmlFree(prop);
1172       }
1173    }
1174    return str;
1175 }
1176
1177 string RPMRepomdHandler::MD5Sum()
1178 {
1179    // XXX FIXME the method should be an abstract Checksum type using
1180    // md5 / sha1 appropriately, for now relying on hacks elsewhere..
1181    return SHA1Sum();
1182 }
1183
1184 string RPMRepomdHandler::SHA1Sum()
1185 {
1186    xmlNode *n;
1187    string str = "";
1188    if ((n = XmlFindNode(NodeP, "checksum"))) {
1189       xmlChar *content = xmlNodeGetContent(n);
1190       str = (char*)content;
1191       xmlFree(content);
1192    }
1193    return str;
1194 }
1195
1196 off_t RPMRepomdHandler::FileSize()
1197 {
1198    xmlNode *n;
1199    off_t size = 0;
1200    if ((n = XmlFindNode(NodeP, "size"))) {
1201       xmlChar *prop = xmlGetProp(n, (xmlChar*)"package");
1202       size = atol((char*)prop);
1203       xmlFree(prop);
1204    } 
1205    return size;
1206 }
1207
1208 off_t RPMRepomdHandler::InstalledSize()
1209 {
1210    xmlNode *n;
1211    off_t size = 0;
1212    if ((n = XmlFindNode(NodeP, "size"))) {
1213       xmlChar *prop = xmlGetProp(n, (xmlChar*)"installed");
1214       size = atol((char*)prop);
1215       xmlFree(prop);
1216    } 
1217    return size;
1218 }
1219
1220 string RPMRepomdHandler::SourceRpm()
1221 {
1222    xmlNode *n = XmlFindNode(NodeP, "format");
1223    return XmlFindNodeContent(n, "sourcerpm");
1224 }
1225
1226 bool RPMRepomdHandler::PRCO(unsigned int Type, vector<Dependency*> &Deps)
1227 {
1228    xmlNode *format = XmlFindNode(NodeP, "format");
1229    xmlNode *prco = NULL;
1230
1231    switch (Type) {
1232       case pkgCache::Dep::Depends:
1233          prco = XmlFindNode(format, "requires");
1234          break;
1235       case pkgCache::Dep::Conflicts:
1236          prco = XmlFindNode(format, "conflicts");
1237          break;
1238       case pkgCache::Dep::Obsoletes:
1239          prco = XmlFindNode(format, "obsoletes");
1240          break;
1241       case pkgCache::Dep::Provides:
1242          prco = XmlFindNode(format, "provides");
1243          break;
1244    }
1245
1246    if (! prco) {
1247       return true;
1248    }
1249    for (xmlNode *n = prco->children; n; n = n->next) {
1250       int_32 RpmOp = 0;
1251       string deptype, depver;
1252       xmlChar *depname, *flags;
1253       if ((depname = xmlGetProp(n, (xmlChar*)"name")) == NULL) continue;
1254
1255       if ((flags = xmlGetProp(n, (xmlChar*)"flags"))) {
1256          deptype = string((char*)flags);
1257          xmlFree(flags);
1258
1259          xmlChar *epoch = xmlGetProp(n, (xmlChar*)"epoch");
1260          if (epoch) {
1261             depver += string((char*)epoch) + ":";
1262             xmlFree(epoch);
1263          }
1264          xmlChar *ver = xmlGetProp(n, (xmlChar*)"ver");
1265          if (ver) {
1266             depver += string((char*)ver);
1267             xmlFree(ver);
1268          }
1269          xmlChar *rel = xmlGetProp(n, (xmlChar*)"rel");
1270          if (rel) {
1271             depver += "-" + string((char*)rel);
1272             xmlFree(rel);
1273          }
1274
1275
1276          if (deptype == "EQ") {
1277             RpmOp = RPMSENSE_EQUAL;
1278          } else if (deptype == "GE") {
1279             RpmOp = RPMSENSE_GREATER | RPMSENSE_EQUAL;
1280          } else if (deptype == "GT") {
1281             RpmOp = RPMSENSE_GREATER;
1282          } else if (deptype == "LE") {
1283             RpmOp = RPMSENSE_LESS | RPMSENSE_EQUAL;
1284          } else if (deptype == "LT") {
1285             RpmOp = RPMSENSE_LESS;
1286          } else {
1287             // wtf, unknown dependency type?
1288             _error->Warning(_("Ignoring unknown dependency type %s"), 
1289                               deptype.c_str());
1290             continue;
1291          }
1292       } else {
1293          RpmOp = RPMSENSE_ANY;
1294       }
1295
1296       if (Type == pkgCache::Dep::Depends) {
1297          xmlChar *pre = xmlGetProp(n, (xmlChar*)"pre"); 
1298          if (pre) {
1299             RpmOp |= RPMSENSE_PREREQ;
1300             xmlFree(pre);
1301          }
1302       }
1303       bool res = PutDep((char*)depname, depver.c_str(), RpmOp, Type, Deps);
1304       xmlFree(depname);
1305    }
1306    return true;
1307 }
1308
1309 // XXX HasFile() usage with repomd with full filelists is slower than
1310 // having the user manually look it up, literally. So we only support the 
1311 // more common files which are stored in primary.xml which supports fast
1312 // random access.
1313 bool RPMRepomdHandler::HasFile(const char *File)
1314 {
1315    if (*File == '\0')
1316       return false;
1317    
1318    vector<string> Files;
1319    ShortFileList(Files);
1320    for (vector<string>::iterator I = Files.begin(); I != Files.end(); I++) {
1321       if (string(File) == (*I)) {
1322          return true;
1323       }
1324    }
1325    return false;
1326 }
1327
1328 bool RPMRepomdHandler::ShortFileList(vector<string> &FileList)
1329 {
1330    xmlNode *format = XmlFindNode(NodeP, "format");
1331    for (xmlNode *n = format->children; n; n = n->next) {
1332       if (xmlStrcmp(n->name, (xmlChar*)"file") != 0)  continue;
1333       xmlChar *Filename = xmlNodeGetContent(n);
1334       FileList.push_back(string((char*)Filename));
1335       xmlFree(Filename);
1336    }
1337    return true;
1338 }
1339
1340 bool RPMRepomdHandler::FileList(vector<string> &FileList)
1341 {
1342    RPMRepomdFLHandler *FL = new RPMRepomdFLHandler(FilelistPath);
1343    bool res = FL->Jump(iOffset);
1344    res &= FL->FileList(FileList);
1345    delete FL;
1346    return res; 
1347 }
1348
1349 bool RPMRepomdHandler::ChangeLog(vector<ChangeLogEntry* > &ChangeLogs)
1350 {
1351    RPMRepomdOtherHandler *OL = new RPMRepomdOtherHandler(OtherPath);
1352    bool res = OL->Jump(iOffset);
1353    res &= OL->ChangeLog(ChangeLogs);
1354    delete OL;
1355    return res; 
1356 }
1357
1358 RPMRepomdHandler::~RPMRepomdHandler()
1359 {
1360    xmlFreeDoc(Primary);
1361 }
1362
1363 RPMRepomdReaderHandler::RPMRepomdReaderHandler(string File) : RPMHandler(),
1364    XmlPath(File), NodeP(NULL), XmlFile(NULL)
1365 {
1366    ID = File;
1367    iOffset = -1;
1368
1369    if (FileExists(XmlPath)) {
1370       XmlFile = xmlReaderForFile(XmlPath.c_str(), NULL,
1371                                   XML_PARSE_NONET|XML_PARSE_NOBLANKS);
1372       if (XmlFile == NULL) {
1373         xmlFreeTextReader(XmlFile);
1374         _error->Error(_("Failed to open filelist index %s"), XmlPath.c_str());
1375         goto error;
1376       }
1377
1378       // seek into first package in xml
1379       int ret = xmlTextReaderRead(XmlFile);
1380       if (ret == 1) {
1381         xmlChar *pkgs = xmlTextReaderGetAttribute(XmlFile, (xmlChar*)"packages");
1382         iSize = atoi((char*)pkgs);
1383         xmlFree(pkgs);
1384       }
1385       while (ret == 1) {
1386         if (xmlStrcmp(xmlTextReaderConstName(XmlFile),
1387                      (xmlChar*)"package") == 0) {
1388            break;
1389         }
1390         ret = xmlTextReaderRead(XmlFile);
1391       }
1392    }
1393    rpmIndexSizes[ID] = iSize;
1394    return;
1395
1396 error:
1397    if (XmlFile) {
1398        xmlFreeTextReader(XmlFile);
1399    }
1400 }
1401
1402 bool RPMRepomdReaderHandler::Jump(off_t Offset)
1403 {
1404    bool res = false;
1405    while (iOffset != Offset) {
1406       res = Skip();
1407       if (res == false)
1408          break;
1409    }
1410       
1411    return res;
1412 }
1413
1414 void RPMRepomdReaderHandler::Rewind()
1415 {
1416    // XXX Ignore rewinds when already at start, any other cases we can't
1417    // handle at the moment. Other cases shouldn't be needed due to usage
1418    // patterns but just in case...
1419    if (iOffset != -1) {
1420       _error->Error(_("Unable to handle xmlReader rewind from offset %d!"), 
1421                       iOffset);
1422    }
1423 }
1424
1425 bool RPMRepomdReaderHandler::Skip()
1426 {
1427    if (iOffset +1 >= iSize) {
1428       return false;
1429    }
1430    if (iOffset >= 0) {
1431       xmlTextReaderNext(XmlFile);
1432    }
1433    NodeP = xmlTextReaderExpand(XmlFile);
1434    iOffset++;
1435
1436    return true;
1437 }
1438
1439 string RPMRepomdReaderHandler::FindTag(char *Tag)
1440 {
1441    string str = "";
1442    if (NodeP) {
1443        xmlChar *attr = xmlGetProp(NodeP, (xmlChar*)Tag);
1444        if (attr) {
1445           str = (char*)attr;
1446           xmlFree(attr);
1447        }
1448    }
1449    return str;
1450 }
1451
1452 string RPMRepomdReaderHandler::FindVerTag(char *Tag)
1453 {
1454    string str = "";
1455    for (xmlNode *n = NodeP->children; n; n = n->next) {
1456       if (xmlStrcmp(n->name, (xmlChar*)"version") != 0)  continue;
1457       xmlChar *attr = xmlGetProp(n, (xmlChar*)Tag);
1458       if (attr) {
1459          str = (char*)attr;
1460          xmlFree(attr);
1461       }
1462    }
1463    return str;
1464 }
1465
1466 RPMRepomdReaderHandler::~RPMRepomdReaderHandler()
1467 {
1468    xmlFreeTextReader(XmlFile);
1469 }
1470
1471 bool RPMRepomdFLHandler::FileList(vector<string> &FileList)
1472 {
1473    for (xmlNode *n = NodeP->children; n; n = n->next) {
1474       if (xmlStrcmp(n->name, (xmlChar*)"file") != 0)  continue;
1475       xmlChar *Filename = xmlNodeGetContent(n);
1476       FileList.push_back(string((char*)Filename));
1477       xmlFree(Filename);
1478    }
1479    return true;
1480 }
1481
1482 bool RPMRepomdOtherHandler::ChangeLog(vector<ChangeLogEntry* > &ChangeLogs)
1483 {
1484    // Changelogs aren't necessarily available at all
1485    if (! XmlFile) {
1486       return false;
1487    }
1488
1489    for (xmlNode *n = NodeP->children; n; n = n->next) {
1490       if (xmlStrcmp(n->name, (xmlChar*)"changelog") != 0)  continue;
1491       ChangeLogEntry *Entry = new ChangeLogEntry;
1492       xmlChar *Text = xmlNodeGetContent(n);
1493       xmlChar *Time = xmlGetProp(n, (xmlChar*)"date");
1494       xmlChar *Author = xmlGetProp(n, (xmlChar*)"author");
1495       Entry->Text = string((char*)Text);
1496       Entry->Time = atoi((char*)Time);
1497       Entry->Author = string((char*)Author);
1498       ChangeLogs.push_back(Entry);
1499       xmlFree(Text);
1500       xmlFree(Time);
1501       xmlFree(Author);
1502    }
1503    return true;
1504 }
1505
1506 #ifdef WITH_SQLITE3
1507 RPMSqliteHandler::RPMSqliteHandler(string File) : 
1508    Primary(NULL), Filelists(NULL), Other(NULL), Packages(NULL)
1509 {
1510    ID = File;
1511    DBPath = File; 
1512    // ugh, pass this in to the constructor or something..
1513    string DBBase = File.substr(0, File.size() - strlen("primary.sqlite"));
1514    FilesDBPath = DBBase + "filelists.sqlite";
1515    OtherDBPath = DBBase + "other.sqlite";
1516
1517    Primary = new SqliteDB(DBPath);
1518    // XXX open these only if needed? 
1519    Filelists = new SqliteDB(FilesDBPath);
1520    if (FileExists(OtherDBPath)) {
1521       Other = new SqliteDB(OtherDBPath);
1522    }
1523
1524    Packages = Primary->Query();
1525
1526    // see if it's a db scheme we support
1527    SqliteQuery *DBI = Primary->Query();
1528    DBI->Exec("select * from db_info");
1529    DBI->Step();
1530    DBVersion = DBI->GetColI("dbversion");
1531    delete DBI;
1532    if (DBVersion < 10) {
1533       _error->Error(_("Unsupported database scheme (%d)"), DBVersion);
1534       return;
1535    } 
1536
1537    Packages->Exec("select * from packages");
1538    iSize = Packages->Size();
1539    rpmIndexSizes[ID] = iSize;
1540 }
1541
1542 RPMSqliteHandler::~RPMSqliteHandler()
1543 {
1544    if (Primary) delete Primary;
1545    if (Filelists) delete Filelists;
1546    if (Other) delete Other;
1547    if (Packages) delete Packages;
1548 }
1549
1550
1551 bool RPMSqliteHandler::Skip()
1552 {
1553    bool res = Packages->Step();
1554    if (res)
1555       iOffset++;
1556    return res;
1557 }
1558
1559 bool RPMSqliteHandler::Jump(off_t Offset)
1560 {
1561    bool res = Packages->Jump(Offset);
1562    if (!res)
1563       return false;
1564    iOffset = Packages->Offset();
1565    return true;
1566 }
1567
1568 void RPMSqliteHandler::Rewind()
1569 {
1570    Packages->Rewind();
1571    iOffset = 0;
1572 }
1573
1574 string RPMSqliteHandler::Name()
1575 {
1576    return Packages->GetCol("name");
1577 }
1578
1579 string RPMSqliteHandler::Version()
1580 {
1581    return Packages->GetCol("version");
1582 }
1583
1584 string RPMSqliteHandler::Release()
1585 {
1586    return Packages->GetCol("release");
1587 }
1588
1589 string RPMSqliteHandler::Epoch()
1590 {
1591    return Packages->GetCol("epoch");
1592 }
1593
1594 string RPMSqliteHandler::Arch()
1595 {
1596    return Packages->GetCol("arch");
1597 }
1598
1599 string RPMSqliteHandler::Group()
1600 {
1601    return Packages->GetCol("rpm_group");
1602 }
1603
1604 string RPMSqliteHandler::Packager()
1605 {
1606    return Packages->GetCol("rpm_packager");
1607 }
1608 string RPMSqliteHandler::Vendor()
1609 {
1610    return Packages->GetCol("rpm_vendor");
1611 }
1612
1613 string RPMSqliteHandler::Summary()
1614 {
1615    return Packages->GetCol("summary");
1616 }
1617
1618 string RPMSqliteHandler::Description()
1619 {
1620    return Packages->GetCol("description");
1621 }
1622
1623 string RPMSqliteHandler::SourceRpm()
1624 {
1625    return Packages->GetCol("rpm_sourcerpm");
1626 }
1627
1628 string RPMSqliteHandler::FileName()
1629 {
1630    return flNotDir(Packages->GetCol("location_href"));
1631 }
1632
1633 string RPMSqliteHandler::Directory()
1634 {
1635    return flNotFile(Packages->GetCol("location_href"));
1636 }
1637
1638 off_t RPMSqliteHandler::FileSize()
1639 {
1640    return Packages->GetColI("size_package");
1641 }
1642
1643 off_t RPMSqliteHandler::InstalledSize()
1644 {
1645    return Packages->GetColI("size_installed");
1646 }
1647
1648 string RPMSqliteHandler::MD5Sum()
1649 {
1650    return SHA1Sum();
1651 }
1652
1653 string RPMSqliteHandler::SHA1Sum()
1654 {
1655    return Packages->GetCol("pkgId");
1656 }
1657
1658 bool RPMSqliteHandler::PRCO(unsigned int Type, vector<Dependency*> &Deps)
1659 {
1660    string what = "";
1661    switch (Type) {
1662       case pkgCache::Dep::Depends:
1663          what = "requires";
1664          break;
1665       case pkgCache::Dep::Conflicts:
1666          what = "conflicts";
1667          break;
1668       case pkgCache::Dep::Obsoletes:
1669          what = "obsoletes";
1670          break;
1671       case pkgCache::Dep::Provides:
1672          what = "provides";
1673          break;
1674    }
1675
1676    ostringstream sql;
1677    unsigned long pkgKey = Packages->GetColI("pkgKey");
1678    sql  << "select * from " << what << " where pkgKey=" << pkgKey << endl;
1679    SqliteQuery *prco = Primary->Query();
1680    if (!prco->Exec(sql.str())) {
1681       delete prco;
1682       return false;
1683    }
1684
1685    while (prco->Step()) {
1686       int_32 RpmOp = 0;
1687       string deptype, depver = "";
1688
1689       if (prco->GetCol("flags").empty()) {
1690          RpmOp == RPMSENSE_ANY;
1691       } else {
1692          deptype = prco->GetCol("flags");
1693          if (deptype == "EQ") {
1694             RpmOp = RPMSENSE_EQUAL;
1695          } else if (deptype == "GE") {
1696             RpmOp = RPMSENSE_GREATER | RPMSENSE_EQUAL;
1697          } else if (deptype == "GT") {
1698             RpmOp = RPMSENSE_GREATER;
1699          } else if (deptype == "LE") {
1700             RpmOp = RPMSENSE_LESS | RPMSENSE_EQUAL;
1701          } else if (deptype == "LT") {
1702             RpmOp = RPMSENSE_LESS;
1703          } else {
1704             // wtf, unknown dependency type?
1705             _error->Warning(_("Ignoring unknown dependency type %s"), 
1706                               deptype.c_str());
1707             continue;
1708          }
1709          if (! prco->GetCol("epoch").empty()) {
1710             depver += prco->GetCol("epoch") + ":";
1711          }
1712          if (! prco->GetCol("version").empty()) {
1713             depver += prco->GetCol("version");
1714          }
1715          if (! prco->GetCol("release").empty()) {
1716             depver += "-" + prco->GetCol("release");
1717          }
1718       }
1719       string depname = prco->GetCol("name");
1720       bool res = PutDep(depname.c_str(), depver.c_str(), RpmOp, Type, Deps);
1721    }
1722    delete prco;
1723    return true;
1724 }
1725
1726 bool RPMSqliteHandler::FileList(vector<string> &FileList)
1727 {
1728    ostringstream sql;
1729    unsigned long pkgKey = Packages->GetColI("pkgKey");
1730    sql  << "select * from filelist where pkgKey=" << pkgKey << endl;
1731    SqliteQuery *Files = Filelists->Query();
1732    if (!Files->Exec(sql.str())) {
1733       delete Files;
1734       return false;
1735    }
1736
1737    string delimiters = "/";
1738    while (Files->Step()) {
1739       string dir = Files->GetCol("dirname");
1740       string filenames = Files->GetCol("filenames");
1741
1742       string::size_type lastPos = filenames.find_first_not_of(delimiters, 0);
1743       string::size_type pos     = filenames.find_first_of(delimiters, lastPos);
1744
1745       while (string::npos != pos || string::npos != lastPos)
1746       {
1747          FileList.push_back(dir + "/" + filenames.substr(lastPos, pos - lastPos));
1748          lastPos = filenames.find_first_not_of(delimiters, pos);
1749          pos = filenames.find_first_of(delimiters, lastPos);
1750       } 
1751    }
1752    delete Files;
1753    return true;
1754 }
1755
1756 bool RPMSqliteHandler::ChangeLog(vector<ChangeLogEntry* > &ChangeLogs)
1757 {
1758    ostringstream sql;
1759    unsigned long pkgKey = Packages->GetColI("pkgKey");
1760    sql  << "select * from changelog where pkgKey=" << pkgKey << endl;
1761    if (! Other) {
1762       return false;
1763    }
1764
1765    SqliteQuery *Changes = Other->Query();
1766    if (!Changes->Exec(sql.str())) {
1767       delete Changes;
1768       return false;
1769    }
1770
1771    while (Changes->Step()) {
1772       ChangeLogEntry *Entry = new ChangeLogEntry;
1773       Entry->Time = Changes->GetColI("date");
1774       Entry->Author = Changes->GetCol("author");
1775       Entry->Text = Changes->GetCol("changelog");
1776       ChangeLogs.push_back(Entry);
1777    }
1778    delete Changes;
1779    return true;
1780 }
1781 #endif /* WITH_SQLITE3 */
1782
1783 #endif /* APT_WITH_REPOMD */
1784
1785
1786 // vim:sts=3:sw=3