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