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