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