30cd61630c2d984a0c6d9286ab1ae3aaa050ddaf
[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 #include <libxml/parser.h>
30 #include <libxml/tree.h>
31 #include <libxml/xmlreader.h>
32
33 #include <apti18n.h>
34
35 #if RPM_VERSION >= 0x040100
36 #include <rpm/rpmts.h>
37 #include <rpm/rpmdb.h>
38 #include <rpm/rpmds.h>
39 #define rpmxxInitIterator(a,b,c,d) rpmtsInitIterator(a,(rpmTag)b,c,d)
40 #else
41 #define rpmxxInitIterator(a,b,c,d) rpmdbInitIterator(a,b,c,d)
42 #endif
43
44 // An attempt to deal with false zero epochs from repomd. With older rpm's we
45 // can only blindly trust the repo admin created the repository with options
46 // suitable for those versions. For rpm >= 4.2.1 this is linked with
47 // promoteepoch behavior - if promoteepoch is used then epoch hiding must
48 // not happen.
49 bool HideZeroEpoch;
50
51 string RPMHandler::Epoch()
52 {
53    char str[512] = "";
54    int_32 count, type, *epoch;
55    void *val;
56    assert(HeaderP != NULL);
57    int rc = headerGetEntry(HeaderP, RPMTAG_EPOCH, &type, &val, &count);
58    epoch = (int_32*)val;
59    if (rc == 1 && count > 0) {
60       snprintf(str, sizeof(str), "%i", epoch[0]);
61    }
62    return string(str);
63 }
64
65 unsigned long RPMHandler::GetITag(rpmTag Tag)
66 {
67    int_32 count, type, *num;
68    void *val;
69    assert(HeaderP != NULL);
70    int rc = headerGetEntry(HeaderP, Tag,
71                            &type, (void**)&val, &count);
72    num = (int_32*)val;
73    return rc?num[0]:0;
74 }
75
76 string RPMHandler::GetSTag(rpmTag Tag)
77 {
78    char *str;
79    void *val;
80    int_32 count, type;
81    assert(HeaderP != NULL);
82    int rc = headerGetEntry(HeaderP, Tag,
83                            &type, (void**)&val, &count);
84    str = (char *)val;
85    return string(rc?str:"");
86 }
87
88 string RPMHandler::EVR()
89 {
90    string e = Epoch();
91    string v = Version();
92    string r = Release();
93    string evr = "";
94    if (e.empty() == true) {
95       evr = v + '-' + r;
96    } else if (HideZeroEpoch && e == "0") {
97       evr = v + '-' + r;
98    } else {
99       evr = e + ':' + v + '-' + r;
100    }
101    return evr;
102
103
104 bool RPMHandler::HasFile(const char *File)
105 {
106    if (*File == '\0')
107       return false;
108    char **names = NULL;
109    void *val;
110    int_32 count = 0;
111    rpmHeaderGetEntry(HeaderP, RPMTAG_OLDFILENAMES,
112                      NULL, (void **) &val, &count);
113    if (count < 1)
114       return false;
115
116    names = (char **)val;
117    while (count--)
118    {
119       char *name = names[count];
120       if (strcmp(name, File) == 0)
121          return true;
122    }
123    free(names);
124    return false;
125 }
126
127 bool RPMHandler::InternalDep(const char *name, const char *ver, int_32 flag) 
128 {
129    if (strncmp(name, "rpmlib(", strlen("rpmlib(")) == 0) {
130 #if RPM_VERSION >= 0x040404
131      rpmds rpmlibProv = NULL;
132      rpmds ds = rpmdsSingle(RPMTAG_PROVIDENAME,
133                             name, ver?ver:NULL, flag);
134      rpmdsRpmlib(&rpmlibProv, NULL);
135      int res = rpmdsSearch(rpmlibProv, ds) >= 0;
136      rpmdsFree(ds);
137      rpmdsFree(rpmlibProv);
138 #elif RPM_VERSION >= 0x040100
139       rpmds ds = rpmdsSingle(RPMTAG_PROVIDENAME,
140                              name, ver?ver:NULL, flag);
141       int res = rpmCheckRpmlibProvides(ds);
142       rpmdsFree(ds);
143 #else
144       int res = rpmCheckRpmlibProvides(name, ver?ver:NULL,
145                                        flag);
146 #endif
147       if (res) 
148          return true;
149    }
150
151 #if RPM_VERSION >= 0x040404
152    // uhhuh, any of these changing would require full cache rebuild...
153    if (strncmp(name, "getconf(", strlen("getconf(")) == 0)
154    {
155      rpmds getconfProv = NULL;
156      rpmds ds = rpmdsSingle(RPMTAG_PROVIDENAME,
157                             name, ver?ver:NULL, flag);
158      rpmdsGetconf(&getconfProv, NULL);
159      int res = rpmdsSearch(getconfProv, ds);
160      rpmdsFree(ds);
161      rpmdsFree(getconfProv);
162      if (res) 
163          return true;
164    }
165
166    if (strncmp(name, "cpuinfo(", strlen("cpuinfo(")) == 0)
167    {
168      rpmds cpuinfoProv = NULL;
169      rpmds ds = rpmdsSingle(RPMTAG_PROVIDENAME,
170                             name, ver?ver:NULL, flag);
171      rpmdsCpuinfo(&cpuinfoProv, NULL);
172      int res = rpmdsSearch(cpuinfoProv, ds);
173      rpmdsFree(ds);
174      rpmdsFree(cpuinfoProv);
175      if (res) 
176          return true;
177    }
178
179    if (strncmp(name, "sysinfo(", strlen("sysinfo(")) == 0)
180    {
181      rpmds sysinfoProv = NULL;
182      rpmds ds = rpmdsSingle(RPMTAG_PROVIDENAME,
183                             name, ver?ver:NULL, flag);
184      rpmdsCpuinfo(&sysinfoProv, NULL);
185      int res = rpmdsSearch(sysinfoProv, ds);
186      rpmdsFree(ds);
187      rpmdsFree(sysinfoProv);
188      if (res)
189          return true;
190    }
191
192    if (strncmp(name, "uname(", strlen("uname(")) == 0)
193    {
194      rpmds unameProv = NULL;
195      rpmds ds = rpmdsSingle(RPMTAG_PROVIDENAME,
196                             name, ver?ver:NULL, flag);
197      rpmdsUname(&unameProv, NULL);
198      int res = rpmdsSearch(unameProv, ds);
199      rpmdsFree(ds);
200      rpmdsFree(unameProv);
201      if (res)
202          return true;
203    }
204
205    if (strlen(name) > 5 && name[strlen(name)-1] == ')' &&
206        ((strchr("Rr_", name[0]) != NULL &&
207          strchr("Ww_", name[1]) != NULL &&
208          strchr("Xx_", name[2]) != NULL &&
209          name[3] == '(') ||
210          strncmp(name, "exists(", strlen("exists(")) == 0 ||
211          strncmp(name, "executable(", strlen("executable(")) == 0 ||
212          strncmp(name, "readable(", strlen("readable(")) == 0 ||
213          strncmp(name, "writable(", strlen("writable("))== 0 ))
214    {
215       int res = rpmioAccess(name, NULL, X_OK);
216       if (res == 0)
217          return true;
218    }
219
220    /* TODO
221     * - /etc/rpm/sysinfo provides
222     * - macro probe provides 
223     * - actually implement soname() and access() dependencies
224     */
225    if (strncmp(name, "soname(", strlen("soname(")) == 0)
226    {
227       cout << "FIXME, ignoring soname() dependency: " << name << endl;
228       return true;
229    }
230 #endif
231    return false; 
232 }
233
234 bool RPMHandler::Depends(unsigned int Type, vector<Dependency*> &Deps)
235 {
236    char **namel = NULL;
237    char **verl = NULL;
238    int *flagl = NULL;
239    int res, type, count;
240    void *nameval = NULL;
241    void *verval = NULL;
242    void *flagval = NULL;
243
244    switch (Type)
245    {
246    case pkgCache::Dep::Depends:
247       res = headerGetEntry(HeaderP, RPMTAG_REQUIRENAME, &type,
248                            (void **)&nameval, &count);
249       if (res != 1)
250           return true;
251       res = headerGetEntry(HeaderP, RPMTAG_REQUIREVERSION, &type,
252                            (void **)&verval, &count);
253       res = headerGetEntry(HeaderP, RPMTAG_REQUIREFLAGS, &type,
254                            (void **)&flagval, &count);
255       break;
256
257    case pkgCache::Dep::Obsoletes:
258       res = headerGetEntry(HeaderP, RPMTAG_OBSOLETENAME, &type,
259                            (void **)&nameval, &count);
260       if (res != 1)
261           return true;
262       res = headerGetEntry(HeaderP, RPMTAG_OBSOLETEVERSION, &type,
263                            (void **)&verval, &count);
264       res = headerGetEntry(HeaderP, RPMTAG_OBSOLETEFLAGS, &type,
265                            (void **)&flagval, &count);
266       break;
267    case pkgCache::Dep::Conflicts:
268       res = headerGetEntry(HeaderP, RPMTAG_CONFLICTNAME, &type,
269                            (void **)&nameval, &count);
270       if (res != 1)
271           return true;
272       res = headerGetEntry(HeaderP, RPMTAG_CONFLICTVERSION, &type,
273                            (void **)&verval, &count);
274       res = headerGetEntry(HeaderP, RPMTAG_CONFLICTFLAGS, &type,
275                            (void **)&flagval, &count);
276       break;
277 #if RPM_VERSION >= 0x040403
278    case pkgCache::Dep::Suggests:
279       res = headerGetEntry(HeaderP, RPMTAG_SUGGESTSNAME, &type,
280                            (void **)&nameval, &count);
281       if (res != 1)
282           return true; 
283       res = headerGetEntry(HeaderP, RPMTAG_SUGGESTSVERSION, &type,
284                            (void **)&verval, &count);
285       res = headerGetEntry(HeaderP, RPMTAG_SUGGESTSFLAGS, &type,
286                            (void **)&flagval, &count);
287       break;
288 #if 0 // Enhances is not even known to apt, sigh...
289    case pkgCache::Dep::Enhances:
290       res = headerGetEntry(HeaderP, RPMTAG_ENHANCESNAME, &type,
291                            (void **)&nameval, &count);
292       if (res != 1)
293           return true;
294       res = headerGetEntry(HeaderP, RPMTAG_ENHANCESVERSION, &type,
295                            (void **)&verval, &count);
296       res = headerGetEntry(HeaderP, RPMTAG_ENHANCESFLAGS, &type,
297                            (void **)&flagval, &count);
298       break;
299 #endif
300 #endif
301    }
302    namel = (char**)nameval;
303    verl = (char**)verval;
304    flagl = (int*)flagval;
305
306    unsigned int Op = 0;
307    bool DepMode = false;
308    if (Type == pkgCache::Dep::Depends)
309       DepMode = true;
310
311    for (int i = 0; i < count; i++) {
312
313       if (InternalDep(namel[i], verl[i] ? verl[i]:"", flagl[i]) == true) {
314          continue;
315       }
316       if (DepMode == true) {
317          if (flagl[i] & RPMSENSE_PREREQ)
318             Type = pkgCache::Dep::PreDepends;
319 #if RPM_VERSION >= 0x040403
320          else if (flagl[i] & RPMSENSE_MISSINGOK)
321             Type = pkgCache::Dep::Suggests;
322 #endif
323          else
324             Type = pkgCache::Dep::Depends;
325       }
326
327       Dependency *Dep = new Dependency;
328       Dep->Name = namel[i];
329       Dep->Version = verl[i] ? verl[i]:"";
330
331       if (HideZeroEpoch && Dep->Version.substr(0, 2) == "0:") {
332          Dep->Version = Dep->Version.substr(2);
333       }
334
335       if (!verl[i]) {
336          Op = pkgCache::Dep::NoOp;
337       } else {
338          if (flagl[i] & RPMSENSE_LESS) {
339             if (flagl[i] & RPMSENSE_EQUAL)
340                 Op = pkgCache::Dep::LessEq;
341             else
342                 Op = pkgCache::Dep::Less;
343          } else if (flagl[i] & RPMSENSE_GREATER) {
344             if (flagl[i] & RPMSENSE_EQUAL)
345                 Op = pkgCache::Dep::GreaterEq;
346             else
347                 Op = pkgCache::Dep::Greater;
348          } else if (flagl[i] & RPMSENSE_EQUAL) {
349             Op = pkgCache::Dep::Equals;
350          }
351       }
352       Dep->Op = Op;
353       Dep->Type = Type;
354       Deps.push_back(Dep);
355    }
356    free(namel);
357    free(verl);
358    return true;
359       
360 }
361
362 bool RPMHandler::Provides(vector<Dependency*> &Provs)
363 {
364    int type, count;
365    char **namel = NULL;
366    char **verl = NULL;
367    void *nameval = NULL;
368    void *verval = NULL;
369    int res;
370
371    res = headerGetEntry(HeaderP, RPMTAG_PROVIDENAME, &type,
372                         (void **)&nameval, &count);
373    if (res != 1)
374        return true;
375    namel = (char **)nameval;
376
377    res = headerGetEntry(HeaderP, RPMTAG_PROVIDEVERSION, &type,
378                         (void **)&verval, NULL);
379    verl = (char **)verval;
380
381    if (res != 1)
382       verl = NULL;
383
384    for (int i = 0; i < count; i++) {
385       Dependency *Dep = new Dependency;
386       Dep->Name = namel[i];
387       if (verl) {
388          Dep->Version = *verl[i] ? verl[i]:"";
389          if (HideZeroEpoch && Dep->Version.substr(0, 2) == "0:") {
390             Dep->Version = Dep->Version.substr(2);
391          }
392          Dep->Op = pkgCache::Dep::Equals;
393       } else {
394          Dep->Version = "";
395       }
396       Provs.push_back(Dep);
397    }
398    return true;
399
400 }
401
402 bool RPMHandler::FileProvides(vector<string> &FileProvs)
403 {
404    const char **names = NULL;
405    void *val = NULL;
406    int_32 count = 0;
407    bool ret = true;
408    rpmHeaderGetEntry(HeaderP, RPMTAG_OLDFILENAMES,
409                      NULL, (void **) &val, &count);
410    names = (const char **)val;
411    while (count--) {
412       FileProvs.push_back(names[count]);
413    }
414    free(names);
415    return ret;
416
417 }
418
419 RPMFileHandler::RPMFileHandler(string File)
420 {
421    ID = File;
422    FD = Fopen(File.c_str(), "r");
423    if (FD == NULL)
424    {
425       /*
426       _error->Error(_("could not open RPM package list file %s: %s"),
427                     File.c_str(), rpmErrorString());
428       */
429       return;
430    }
431    iSize = fdSize(FD);
432 }
433
434 RPMFileHandler::RPMFileHandler(FileFd *File)
435 {
436    FD = fdDup(File->Fd());
437    if (FD == NULL)
438    {
439       /*
440       _error->Error(_("could not create RPM file descriptor: %s"),
441                     rpmErrorString());
442       */
443       return;
444    }
445    iSize = fdSize(FD);
446 }
447
448 RPMFileHandler::~RPMFileHandler()
449 {
450    if (HeaderP != NULL)
451       headerFree(HeaderP);
452    if (FD != NULL)
453       Fclose(FD);
454 }
455
456 bool RPMFileHandler::Skip()
457 {
458    if (FD == NULL)
459       return false;
460    iOffset = lseek(Fileno(FD),0,SEEK_CUR);
461    if (HeaderP != NULL)
462        headerFree(HeaderP);
463    HeaderP = headerRead(FD, HEADER_MAGIC_YES);
464    return (HeaderP != NULL);
465 }
466
467 bool RPMFileHandler::Jump(off_t Offset)
468 {
469    if (FD == NULL)
470       return false;
471    if (lseek(Fileno(FD),Offset,SEEK_SET) != Offset)
472       return false;
473    return Skip();
474 }
475
476 void RPMFileHandler::Rewind()
477 {
478    if (FD == NULL)
479       return;
480    iOffset = lseek(Fileno(FD),0,SEEK_SET);
481    if (iOffset != 0)
482       _error->Error(_("could not rewind RPMFileHandler"));
483 }
484
485 string RPMFileHandler::FileName()
486 {
487    return GetSTag(CRPMTAG_FILENAME);
488 }
489
490 string RPMFileHandler::Directory()
491 {
492    return GetSTag(CRPMTAG_DIRECTORY);
493 }
494
495 unsigned long RPMFileHandler::FileSize()
496 {
497    return GetITag(CRPMTAG_FILESIZE);
498 }
499
500 string RPMFileHandler::MD5Sum()
501 {
502    return GetSTag(CRPMTAG_MD5);
503 }
504
505 bool RPMSingleFileHandler::Skip()
506 {
507    if (FD == NULL)
508       return false;
509    if (HeaderP != NULL) {
510       headerFree(HeaderP);
511       HeaderP = NULL;
512       return false;
513    }
514 #if RPM_VERSION >= 0x040100
515    rpmts TS = rpmtsCreate();
516    rpmtsSetVSFlags(TS, (rpmVSFlags_e)-1);
517    int rc = rpmReadPackageFile(TS, FD, sFilePath.c_str(), &HeaderP);
518    if (rc != RPMRC_OK && rc != RPMRC_NOTTRUSTED && rc != RPMRC_NOKEY) {
519       _error->Error(_("Failed reading file %s"), sFilePath.c_str());
520       HeaderP = NULL;
521    }
522    rpmtsFree(TS);
523 #else
524    int rc = rpmReadPackageHeader(FD, &HeaderP, 0, NULL, NULL);
525    if (rc) {
526       _error->Error(_("Failed reading file %s"), sFilePath.c_str());
527       HeaderP = NULL;
528    }
529 #endif
530    return (HeaderP != NULL);
531 }
532
533 bool RPMSingleFileHandler::Jump(off_t Offset)
534 {
535    assert(Offset == 0);
536    Rewind();
537    return RPMFileHandler::Jump(Offset);
538 }
539
540 void RPMSingleFileHandler::Rewind()
541 {
542    if (FD == NULL)
543       return;
544    if (HeaderP != NULL) {
545       HeaderP = NULL;
546       headerFree(HeaderP);
547    }
548    lseek(Fileno(FD),0,SEEK_SET);
549 }
550
551 unsigned long RPMSingleFileHandler::FileSize()
552 {
553    struct stat S;
554    if (stat(sFilePath.c_str(),&S) != 0)
555       return 0;
556    return S.st_size;
557 }
558
559 string RPMSingleFileHandler::MD5Sum()
560 {
561    MD5Summation MD5;
562    FileFd File(sFilePath, FileFd::ReadOnly);
563    MD5.AddFD(File.Fd(), File.Size());
564    File.Close();
565    return MD5.Result().Value();
566 }
567
568 RPMDirHandler::RPMDirHandler(string DirName)
569    : sDirName(DirName)
570 {
571    ID = DirName;
572 #if RPM_VERSION >= 0x040100
573    TS = NULL;
574 #endif
575    Dir = opendir(sDirName.c_str());
576    if (Dir == NULL)
577       return;
578    iSize = 0;
579    while (nextFileName() != NULL)
580       iSize += 1;
581    rewinddir(Dir);
582 #if RPM_VERSION >= 0x040100
583    TS = rpmtsCreate();
584    rpmtsSetVSFlags(TS, (rpmVSFlags_e)-1);
585 #endif
586 }
587
588 const char *RPMDirHandler::nextFileName()
589 {
590    for (struct dirent *Ent = readdir(Dir); Ent != 0; Ent = readdir(Dir))
591    {
592       const char *name = Ent->d_name;
593
594       if (name[0] == '.')
595          continue;
596
597       if (flExtension(name) != "rpm")
598          continue;
599
600       // Make sure it is a file and not something else
601       sFilePath = flCombine(sDirName,name);
602       struct stat St;
603       if (stat(sFilePath.c_str(),&St) != 0 || S_ISREG(St.st_mode) == 0)
604          continue;
605
606       sFileName = name;
607       
608       return name;
609    } 
610    return NULL;
611 }
612
613 RPMDirHandler::~RPMDirHandler()
614 {
615    if (HeaderP != NULL)
616       headerFree(HeaderP);
617 #if RPM_VERSION >= 0x040100
618    if (TS != NULL)
619       rpmtsFree(TS);
620 #endif
621    if (Dir != NULL)
622       closedir(Dir);
623 }
624
625 bool RPMDirHandler::Skip()
626 {
627    if (Dir == NULL)
628       return false;
629    if (HeaderP != NULL) {
630       headerFree(HeaderP);
631       HeaderP = NULL;
632    }
633    const char *fname = nextFileName();
634    bool Res = false;
635    for (; fname != NULL; fname = nextFileName()) {
636       iOffset++;
637       if (fname == NULL)
638          break;
639       FD_t FD = Fopen(sFilePath.c_str(), "r");
640       if (FD == NULL)
641          continue;
642 #if RPM_VERSION >= 0x040100
643       int rc = rpmReadPackageFile(TS, FD, fname, &HeaderP);
644       Fclose(FD);
645       if (rc != RPMRC_OK
646           && rc != RPMRC_NOTTRUSTED
647           && rc != RPMRC_NOKEY)
648          continue;
649 #else
650       int isSource;
651       int rc = rpmReadPackageHeader(FD, &HeaderP, &isSource, NULL, NULL);
652       Fclose(FD);
653       if (rc != 0)
654          continue;
655 #endif
656       Res = true;
657       break;
658    }
659    return Res;
660 }
661
662 bool RPMDirHandler::Jump(off_t Offset)
663 {
664    if (Dir == NULL)
665       return false;
666    rewinddir(Dir);
667    iOffset = 0;
668    while (1) {
669       if (iOffset+1 == Offset)
670          return Skip();
671       if (nextFileName() == NULL)
672          break;
673       iOffset++;
674    }
675    return false;
676 }
677
678 void RPMDirHandler::Rewind()
679 {
680    rewinddir(Dir);
681    iOffset = 0;
682 }
683
684 unsigned long RPMDirHandler::FileSize()
685 {
686    if (Dir == NULL)
687       return 0;
688    struct stat St;
689    if (stat(sFilePath.c_str(),&St) != 0) {
690       _error->Errno("stat",_("Unable to determine the file size"));
691       return 0;
692    }
693    return St.st_size;
694 }
695
696 string RPMDirHandler::MD5Sum()
697 {
698    if (Dir == NULL)
699       return "";
700    MD5Summation MD5;
701    FileFd File(sFilePath, FileFd::ReadOnly);
702    MD5.AddFD(File.Fd(), File.Size());
703    File.Close();
704    return MD5.Result().Value();
705 }
706
707
708 RPMDBHandler::RPMDBHandler(bool WriteLock)
709    : Handler(0), WriteLock(WriteLock)
710 {
711 #if RPM_VERSION >= 0x040000
712    RpmIter = NULL;
713 #endif
714    string Dir = _config->Find("RPM::RootDir");
715    
716    rpmReadConfigFiles(NULL, NULL);
717    ID = DataPath(false);
718
719    RPMPackageData::Singleton()->InitMinArchScore();
720
721    // Everytime we open a database for writing, it has its
722    // mtime changed, and kills our cache validity. As we never
723    // change any information in the database directly, we will
724    // restore the mtime and save our cache.
725    struct stat St;
726    stat(DataPath(false).c_str(), &St);
727    DbFileMtime = St.st_mtime;
728
729 #if RPM_VERSION >= 0x040100
730    Handler = rpmtsCreate();
731    rpmtsSetVSFlags(Handler, (rpmVSFlags_e)-1);
732    rpmtsSetRootDir(Handler, Dir.c_str());
733 #else
734    const char *RootDir = NULL;
735    if (!Dir.empty())
736       RootDir = Dir.c_str();
737    if (rpmdbOpen(RootDir, &Handler, O_RDONLY, 0644) != 0)
738    {
739       _error->Error(_("could not open RPM database"));
740       return;
741    }
742 #endif
743 #if RPM_VERSION >= 0x040000
744    RpmIter = rpmxxInitIterator(Handler, RPMDBI_PACKAGES, NULL, 0);
745    if (RpmIter == NULL) {
746       _error->Error(_("could not create RPM database iterator"));
747       return;
748    }
749    // iSize = rpmdbGetIteratorCount(RpmIter);
750    // This doesn't seem to work right now. Code in rpm (4.0.4, at least)
751    // returns a 0 from rpmdbGetIteratorCount() if rpmxxInitIterator() is
752    // called with RPMDBI_PACKAGES or with keyp == NULL. The algorithm
753    // below will be used until there's support for it.
754    iSize = 0;
755    rpmdbMatchIterator countIt;
756    countIt = rpmxxInitIterator(Handler, RPMDBI_PACKAGES, NULL, 0);
757    while (rpmdbNextIterator(countIt) != NULL)
758       iSize++;
759    rpmdbFreeIterator(countIt);
760 #else
761    iSize = St.st_size;
762
763 #endif
764
765
766    // Restore just after opening the database, and just after closing.
767    if (WriteLock) {
768       struct utimbuf Ut;
769       Ut.actime = DbFileMtime;
770       Ut.modtime = DbFileMtime;
771       utime(DataPath(false).c_str(), &Ut);
772    }
773 }
774
775 RPMDBHandler::~RPMDBHandler()
776 {
777 #if RPM_VERSION >= 0x040000
778    if (RpmIter != NULL)
779       rpmdbFreeIterator(RpmIter);
780 #else
781    if (HeaderP != NULL)
782        headerFree(HeaderP);
783 #endif
784
785    if (Handler != NULL) {
786 #if RPM_VERSION >= 0x040100
787       rpmtsFree(Handler);
788 #else
789       rpmdbClose(Handler);
790 #endif
791    }
792
793    // Restore just after opening the database, and just after closing.
794    if (WriteLock) {
795       struct utimbuf Ut;
796       Ut.actime = DbFileMtime;
797       Ut.modtime = DbFileMtime;
798       utime(DataPath(false).c_str(), &Ut);
799    }
800 }
801
802 string RPMDBHandler::DataPath(bool DirectoryOnly)
803 {
804    string File = "packages.rpm";
805    char *tmp = (char *) rpmExpand("%{_dbpath}", NULL);
806    string DBPath(_config->Find("RPM::RootDir")+tmp);
807    free(tmp);
808
809 #if RPM_VERSION >= 0x040000
810    if (rpmExpandNumeric("%{_dbapi}") >= 3)
811       File = "Packages";       
812 #endif
813    if (DirectoryOnly == true)
814        return DBPath;
815    else
816        return DBPath+"/"+File;
817 }
818
819 bool RPMDBHandler::Skip()
820 {
821 #if RPM_VERSION >= 0x040000
822    if (RpmIter == NULL)
823        return false;
824    HeaderP = rpmdbNextIterator(RpmIter);
825    iOffset = rpmdbGetIteratorOffset(RpmIter);
826    if (HeaderP == NULL)
827       return false;
828 #else
829    if (iOffset == 0)
830       iOffset = rpmdbFirstRecNum(Handler);
831    else
832       iOffset = rpmdbNextRecNum(Handler, iOffset);
833    if (HeaderP != NULL)
834    {
835       headerFree(HeaderP);
836       HeaderP = NULL;
837    }
838    if (iOffset == 0)
839        return false;
840    HeaderP = rpmdbGetRecord(Handler, iOffset);
841 #endif
842    return true;
843 }
844
845 bool RPMDBHandler::Jump(off_t Offset)
846 {
847    iOffset = Offset;
848 #if RPM_VERSION >= 0x040000
849    // rpmdb indexes are hardcoded uint32_t, the size must match here
850    uint_32 rpmOffset = iOffset;
851    if (RpmIter == NULL)
852       return false;
853    rpmdbFreeIterator(RpmIter);
854    if (iOffset == 0)
855       RpmIter = rpmxxInitIterator(Handler, RPMDBI_PACKAGES, NULL, 0);
856    else {
857       RpmIter = rpmxxInitIterator(Handler, RPMDBI_PACKAGES,
858                                   &rpmOffset, sizeof(rpmOffset));
859       iOffset = rpmOffset;
860    }
861    HeaderP = rpmdbNextIterator(RpmIter);
862 #else
863    HeaderP = rpmdbGetRecord(Handler, iOffset);
864 #endif
865    return true;
866 }
867
868 bool RPMDBHandler::JumpByName(string PkgName)
869 {
870    if (RpmIter == NULL) return false;
871    rpmdbFreeIterator(RpmIter);
872 #if RPM_VERSION >= 0x040100
873    RpmIter = rpmtsInitIterator(Handler, (rpmTag)RPMDBI_LABEL, PkgName.c_str(), 0);
874 #else
875    RpmIter = rpmdbInitIterator(Handler, RPMDBI_LABEL, PkgName.c_str(), 0);
876 #endif
877
878    HeaderP = rpmdbNextIterator(RpmIter);
879    return (HeaderP != NULL);
880 }
881
882 void RPMDBHandler::Rewind()
883 {
884 #if RPM_VERSION >= 0x040000
885    if (RpmIter == NULL)
886       return;
887    rpmdbFreeIterator(RpmIter);   
888    RpmIter = rpmxxInitIterator(Handler, RPMDBI_PACKAGES, NULL, 0);
889 #else
890    if (HeaderP != NULL)
891    {
892       headerFree(HeaderP);
893       HeaderP = NULL;
894    }
895 #endif
896    iOffset = 0;
897 }
898 #endif
899
900 RPMRepomdHandler::RPMRepomdHandler(string File, bool useFilelist)
901 {
902    WithFilelist = useFilelist;
903    PrimaryFile = File;
904    FilelistFile = File.substr(0, File.size() - 11) + "filelists.xml";
905
906    ID = File;
907    Root = NULL;
908    Primary = NULL;
909    Filelist = NULL;
910    xmlChar *packages = NULL;
911    off_t pkgcount = 0;
912    
913
914    Primary = xmlReadFile(File.c_str(), NULL, XML_PARSE_NONET|XML_PARSE_NOBLANKS);
915    if ((Root = xmlDocGetRootElement(Primary)) == NULL) {
916       _error->Error(_("Failed to open package index %s"), PrimaryFile.c_str());
917       goto error;
918    }
919    if (xmlStrncmp(Root->name, (xmlChar*)"metadata", strlen("metadata")) != 0) {
920       _error->Error(_("Corrupted package index %s"), PrimaryFile.c_str());
921       goto error;
922    }
923
924    if (WithFilelist && FileExists(FilelistFile)) {
925       Filelist = xmlReaderForFile(FilelistFile.c_str(), NULL,
926                                   XML_PARSE_NONET|XML_PARSE_NOBLANKS);
927       if (Filelist == NULL) {
928          xmlFreeTextReader(Filelist);
929          _error->Error(_("Failed to open filelist index %s"), FilelistFile.c_str());
930          goto error;
931       } 
932
933       // seek into first package in filelists.xml
934       int ret = xmlTextReaderRead(Filelist);
935       while (ret == 1) {
936          if (xmlStrcmp(xmlTextReaderConstName(Filelist), 
937                       (xmlChar*)"package") == 0) {
938             break;
939          }
940          ret = xmlTextReaderRead(Filelist);
941       }
942    }     
943
944    packages = xmlGetProp(Root, (xmlChar*)"packages");
945    iSize = atoi((char*)packages);
946    xmlFree(packages);
947    for (xmlNode *n = Root->children; n; n = n->next) {
948       if (n->type != XML_ELEMENT_NODE ||
949           xmlStrcmp(n->name, (xmlChar*)"package") != 0)
950          continue;
951       Pkgs.push_back(n);
952       pkgcount++;
953    }
954    PkgIter = Pkgs.begin();
955
956    // There seem to be broken version(s) of createrepo around which report
957    // to have one more package than is in the repository. Warn and work around.
958    if (iSize != pkgcount) {
959       _error->Warning(_("Inconsistent metadata, package count doesn't match in %s"), File.c_str());
960       iSize = pkgcount;
961    }
962
963    return;
964
965 error:
966    if (Primary) {
967       xmlFreeDoc(Primary);
968    }
969    if (Filelist) {
970       xmlFreeTextReader(Filelist);
971    }
972 }
973
974 bool RPMRepomdHandler::Skip()
975 {
976    if (PkgIter == Pkgs.end()) {
977       return false;
978    }
979    NodeP = *PkgIter;
980    iOffset = PkgIter - Pkgs.begin();
981
982    if (WithFilelist) {
983       if (iOffset > 0) {
984          xmlTextReaderNext(Filelist);
985       }
986    }
987
988    PkgIter++;
989    return true;
990 }
991
992 bool RPMRepomdHandler::Jump(off_t Offset)
993 {
994    if (Offset >= iSize) {
995       return false;
996    }
997    iOffset = Offset;
998    NodeP = Pkgs[Offset];
999    // This isn't strictly necessary as Skip() and Jump() aren't mixed
1000    // in practise but doesn't hurt either...
1001    PkgIter = Pkgs.begin() + Offset + 1;
1002    return true;
1003
1004 }
1005
1006 void RPMRepomdHandler::Rewind()
1007 {
1008    iOffset = 0;
1009    PkgIter = Pkgs.begin();
1010 }
1011
1012 xmlNode *RPMRepomdHandler::FindNode(const string Name)
1013 {
1014    for (xmlNode *n = NodeP->children; n; n = n->next) {
1015       if (xmlStrcmp(n->name, (xmlChar*)Name.c_str()) == 0) {
1016          return n;
1017       }
1018    }
1019    return NULL;
1020 }
1021
1022 xmlNode *RPMRepomdHandler::FindNode(xmlNode *Node, const string Name)
1023 {
1024    for (xmlNode *n = Node->children; n; n = n->next) {
1025       if (xmlStrcmp(n->name, (xmlChar*)Name.c_str()) == 0) {
1026          return n;
1027       }
1028    }
1029    return NULL;
1030 }
1031
1032 string RPMRepomdHandler::FindTag(xmlNode *Node, string Tag)
1033 {
1034    xmlNode *n = FindNode(Node, Tag);
1035    string str = "";
1036    if (n) {
1037       xmlChar *content = xmlNodeGetContent(n);
1038       if (content) {
1039          str = (char*)content;
1040          xmlFree(content);
1041       }
1042    }
1043    return str;
1044 }
1045
1046 string RPMRepomdHandler::GetProp(xmlNode *Node, char *Prop)
1047 {
1048    string str = "";
1049    if (Node) {
1050       xmlChar *prop = xmlGetProp(Node, (xmlChar*)Prop);
1051       if (prop) {
1052          str = (char*)prop;
1053          xmlFree(prop);
1054       }
1055    }
1056    return str;
1057 }
1058
1059 string RPMRepomdHandler::Group()
1060 {
1061    xmlNode *n = FindNode("format");
1062    return FindTag(n, "group");
1063 }
1064
1065 string RPMRepomdHandler::Vendor()
1066 {
1067    xmlNode *n = FindNode("format");
1068    return FindTag(n, "vendor");
1069 }
1070
1071 string RPMRepomdHandler::Release()
1072 {
1073    xmlNode *n = FindNode("version");
1074    return GetProp(n, "rel");
1075 }
1076
1077 string RPMRepomdHandler::Version()
1078 {
1079    xmlNode *n = FindNode("version");
1080    return GetProp(n, "ver");
1081 }
1082
1083 string RPMRepomdHandler::Epoch()
1084 {
1085    string epoch;
1086    xmlNode *n = FindNode("version");
1087    epoch = GetProp(n, "epoch");
1088    // XXX createrepo stomps epoch zero on packages without epoch, hide
1089    // them. Rpm treats zero and empty equally anyway so it doesn't matter.
1090    if (epoch == "0")
1091       epoch = "";
1092    return epoch;
1093 }
1094
1095 string RPMRepomdHandler::FileName()
1096 {
1097    xmlNode *n;
1098    string str = "";
1099    if ((n = FindNode("location"))) {
1100       xmlChar *prop = xmlGetProp(n, (xmlChar*)"href");
1101       str = basename((char*)prop);
1102       xmlFree(prop);
1103    }
1104    return str;
1105 }
1106
1107 string RPMRepomdHandler::Directory()
1108 {
1109    xmlNode *n;
1110    string str = "";
1111    if ((n = FindNode("location"))) {
1112       xmlChar *prop = xmlGetProp(n, (xmlChar*)"href");
1113       if (prop) {
1114          str = dirname((char*)prop);
1115          xmlFree(prop);
1116       }
1117    }
1118    return str;
1119 }
1120
1121 string RPMRepomdHandler::MD5Sum()
1122 {
1123    // XXX FIXME the method should be an abstract Checksum type using
1124    // md5 / sha1 appropriately, for now relying on hacks elsewhere..
1125    return SHA1Sum();
1126 }
1127
1128 string RPMRepomdHandler::SHA1Sum()
1129 {
1130    xmlNode *n;
1131    string str = "";
1132    if ((n = FindNode("checksum"))) {
1133       xmlChar *content = xmlNodeGetContent(n);
1134       str = (char*)content;
1135       xmlFree(content);
1136    }
1137    return str;
1138 }
1139 unsigned long RPMRepomdHandler::FileSize()
1140 {
1141    xmlNode *n;
1142    unsigned long size = 0;
1143    if ((n = FindNode("size"))) {
1144       xmlChar *prop = xmlGetProp(n, (xmlChar*)"package");
1145       size = atol((char*)prop);
1146       xmlFree(prop);
1147    } 
1148    return size;
1149 }
1150
1151 unsigned long RPMRepomdHandler::InstalledSize()
1152 {
1153    xmlNode *n;
1154    unsigned long size = 0;
1155    if ((n = FindNode("size"))) {
1156       xmlChar *prop = xmlGetProp(n, (xmlChar*)"installed");
1157       size = atol((char*)prop);
1158       xmlFree(prop);
1159    } 
1160    return size;
1161 }
1162
1163 string RPMRepomdHandler::SourceRpm()
1164 {
1165    xmlNode *n = FindNode("format");
1166    return FindTag(n, "sourcerpm");
1167 }
1168
1169 bool RPMRepomdHandler::HasFile(const char *File)
1170 {
1171    if (*File == '\0')
1172       return false;
1173
1174    bool found = false;
1175
1176    xmlNode *format = FindNode("format");
1177    for (xmlNode *n = format->children; n; n = n->next) {
1178       if (xmlStrcmp(n->name, (xmlChar*)"file") != 0) 
1179          continue;
1180       if (xmlStrcmp(xmlNodeGetContent(n), (xmlChar*)File) == 0) {
1181          found = true;
1182          break;
1183       }
1184    }
1185
1186 #if 0
1187    // look through filelists.xml for the file if not in primary.xml
1188    if (! found) {
1189       for (xmlNode *n = FlNodeP->children; n; n = n->next) {
1190          if (strcmp((char*)n->name, "file") != 0) 
1191             continue;
1192          if (strcmp(File, (char*)xmlNodeGetContent(n)) == 0) {
1193             found = true;
1194             break;
1195          }
1196       }
1197    }
1198 #endif
1199    return found;
1200
1201 }
1202
1203 bool RPMRepomdHandler::Depends(unsigned int Type, vector<Dependency*> &Deps)
1204 {
1205    xmlNode *format = FindNode("format");
1206    xmlNode *dco = NULL;
1207
1208    switch (Type) {
1209       case pkgCache::Dep::Depends:
1210          dco = FindNode(format, "requires");
1211          break;
1212       case pkgCache::Dep::Conflicts:
1213          dco = FindNode(format, "conflicts");
1214          break;
1215       case pkgCache::Dep::Obsoletes:
1216          dco = FindNode(format, "obsoletes");
1217          break;
1218    }
1219
1220    if (! dco) {
1221       return true;
1222    }
1223    for (xmlNode *n = dco->children; n; n = n->next) {
1224       unsigned int Op = 0;
1225       int_32 RpmOp = 0;
1226       string deptype, depver;
1227       xmlChar *depname, *flags;
1228       if ((depname = xmlGetProp(n, (xmlChar*)"name")) == NULL) continue;
1229
1230       if ((flags = xmlGetProp(n, (xmlChar*)"flags"))) {
1231          deptype = string((char*)flags);
1232          xmlFree(flags);
1233
1234          xmlChar *epoch = xmlGetProp(n, (xmlChar*)"epoch");
1235          if (epoch) {
1236             depver += string((char*)epoch) + ":";
1237             xmlFree(epoch);
1238          }
1239          xmlChar *ver = xmlGetProp(n, (xmlChar*)"ver");
1240          if (ver) {
1241             depver += string((char*)ver);
1242             xmlFree(ver);
1243          }
1244          xmlChar *rel = xmlGetProp(n, (xmlChar*)"rel");
1245          if (rel) {
1246             depver += "-" + string((char*)rel);
1247             xmlFree(rel);
1248          }
1249
1250
1251          if (deptype == "EQ") {
1252             Op = pkgCache::Dep::Equals;
1253             RpmOp = RPMSENSE_EQUAL;
1254          } else if (deptype == "GE") {
1255             Op = pkgCache::Dep::GreaterEq;
1256             RpmOp = RPMSENSE_GREATER | RPMSENSE_EQUAL;
1257          } else if (deptype == "GT") {
1258             Op = pkgCache::Dep::Greater;
1259             RpmOp = RPMSENSE_GREATER;
1260          } else if (deptype == "LE") {
1261             Op = pkgCache::Dep::LessEq;
1262             RpmOp = RPMSENSE_LESS | RPMSENSE_EQUAL;
1263          } else if (deptype == "LT") {
1264             Op = pkgCache::Dep::Less;
1265             RpmOp = RPMSENSE_LESS;
1266          } else {
1267             // erm, unknown dependency type?
1268          }
1269       } else {
1270          Op = pkgCache::Dep::NoOp;
1271          RpmOp = RPMSENSE_ANY;
1272       }
1273
1274       if (InternalDep((char*)depname, depver.c_str(), RpmOp) == true) {
1275          continue;
1276       }
1277       if (Type == pkgCache::Dep::Depends) {
1278          xmlChar *pre = xmlGetProp(n, (xmlChar*)"pre"); 
1279          if (pre) {
1280             Type = pkgCache::Dep::PreDepends;
1281             xmlFree(pre);
1282          }
1283       }
1284       Dependency *Dep = new Dependency;
1285       Dep->Name = string((char*)depname);
1286       xmlFree(depname);
1287       Dep->Version = depver;
1288       if (HideZeroEpoch && Dep->Version.substr(0, 2) == "0:") {
1289          Dep->Version = Dep->Version.substr(2);
1290       }
1291
1292       
1293       Dep->Op = Op;
1294       Dep->Type = Type;
1295       Deps.push_back(Dep);
1296    }
1297    return true;
1298 }
1299
1300 bool RPMRepomdHandler::Provides(vector<Dependency*> &Provs)
1301 {
1302    xmlNode *format = FindNode("format");
1303    xmlNode *provides = FindNode(format, "provides");
1304
1305    if (! provides)
1306       return true;
1307
1308    for (xmlNode *n = provides->children; n; n = n->next) {
1309       string depver = "";
1310       xmlChar *depname, *flags;
1311
1312       if (xmlStrcmp(n->name, (xmlChar*)"entry") != 0)  continue;
1313
1314       Dependency *Dep = new Dependency;
1315       if ((depname = xmlGetProp(n, (xmlChar*)"name")) == NULL) continue;
1316       Dep->Name = string((char*)depname);
1317       xmlFree(depname);
1318
1319       if ((flags = xmlGetProp(n, (xmlChar*)"flags"))) {
1320          xmlFree(flags);
1321
1322          xmlChar *epoch = xmlGetProp(n, (xmlChar*)"epoch");
1323          if (epoch) {
1324             depver += string((char*)epoch) + ":";
1325             xmlFree(epoch);
1326          }
1327          xmlChar *ver = xmlGetProp(n, (xmlChar*)"ver");
1328          if (ver) {
1329             depver += string((char*)ver);
1330             xmlFree(ver);
1331          }
1332          xmlChar *rel = xmlGetProp(n, (xmlChar*)"rel");
1333          if (rel) {
1334             depver += "-" + string((char*)rel);
1335             xmlFree(rel);
1336          }
1337
1338       }
1339       Dep->Version = depver;
1340       if (HideZeroEpoch && Dep->Version.substr(0, 2) == "0:") {
1341          Dep->Version = Dep->Version.substr(2);
1342       }
1343
1344       if (depver.empty() == false)
1345          Dep->Op = pkgCache::Dep::Equals;
1346       Provs.push_back(Dep);
1347    }
1348    return true;
1349 }
1350
1351 bool RPMRepomdHandler::FileProvides(vector<string> &FileProvs)
1352 {
1353    // XXX maybe this should be made runtime configurable?
1354    if ( !WithFilelist ) {
1355       xmlNode *format = FindNode("format");
1356       for (xmlNode *n = format->children; n; n = n->next) {
1357          if (xmlStrcmp(n->name, (xmlChar*)"file") != 0)  continue;
1358          xmlChar *Filename = xmlNodeGetContent(n);
1359          FileProvs.push_back(string((char*)Filename));
1360          xmlFree(Filename);
1361       }
1362    } else {
1363       xmlNode *FlP = xmlTextReaderExpand(Filelist);
1364       if (FlP == NULL) {
1365          return false;
1366       }
1367       for (xmlNode *n = FlP->children; n; n = n->next) {
1368          if (xmlStrcmp(n->name, (xmlChar*)"file") != 0) 
1369             continue;
1370          xmlChar *Filename = xmlNodeGetContent(n);
1371          FileProvs.push_back(string((char*)Filename));
1372          xmlFree(Filename);
1373       }
1374    }
1375    return true;
1376 }
1377
1378 RPMRepomdHandler::~RPMRepomdHandler()
1379 {
1380    xmlFreeDoc(Primary);
1381    if (WithFilelist) {
1382       xmlFreeTextReader(Filelist);
1383    }
1384 }
1385
1386 // vim:sts=3:sw=3