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