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