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