1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 """
19 Classes and functions dealing with rpm package representations.
20 """
21
22 import rpm
23 import os
24 import os.path
25 import misc
26 import i18n
27 import re
28 import fnmatch
29 import stat
30 import warnings
31 from subprocess import Popen, PIPE
32 from rpmUtils import RpmUtilsError
33 import rpmUtils.miscutils
34 from rpmUtils.miscutils import flagToString, stringToVersion
35 import Errors
36 import errno
37 import struct
38 from constants import *
39
40 import urlparse
41 urlparse.uses_fragment.append("media")
42 from urlgrabber.grabber import URLGrabber, URLGrabError
43
44
45 import pwd
46 import grp
47
49 """
50 Compare two Package or PackageEVR objects.
51 """
52 (e1, v1, r1) = (po1.epoch, po1.version, po1.release)
53 (e2, v2, r2) = (po2.epoch, po2.version, po2.release)
54 return rpmUtils.miscutils.compareEVR((e1, v1, r1), (e2, v2, r2))
56 """
57 Compare two Package or PackageEVR objects for equality.
58 """
59 (e1, v1, r1) = (po1.epoch, po1.version, po1.release)
60 (e2, v2, r2) = (po2.epoch, po2.version, po2.release)
61 if r1 != r2: return False
62 if v1 != v2: return False
63 if e1 != e2: return False
64 return True
65
67 """take a list of pkg objects and return a dict the contains all the possible
68 naming conventions for them eg: for (name,i386,0,1,1)
69 dict[name] = (name, i386, 0, 1, 1)
70 dict[name.i386] = (name, i386, 0, 1, 1)
71 dict[name-1-1.i386] = (name, i386, 0, 1, 1)
72 dict[name-1] = (name, i386, 0, 1, 1)
73 dict[name-1-1] = (name, i386, 0, 1, 1)
74 dict[0:name-1-1.i386] = (name, i386, 0, 1, 1)
75 dict[name-0:1-1.i386] = (name, i386, 0, 1, 1)
76 """
77 pkgdict = {}
78 for pkg in pkgs:
79 (n, a, e, v, r) = pkg.pkgtup
80 if not casematch:
81 n = n.lower()
82 a = a.lower()
83 e = e.lower()
84 v = v.lower()
85 r = r.lower()
86 name = n
87 nameArch = '%s.%s' % (n, a)
88 nameVerRelArch = '%s-%s-%s.%s' % (n, v, r, a)
89 nameVer = '%s-%s' % (n, v)
90 nameVerRel = '%s-%s-%s' % (n, v, r)
91 envra = '%s:%s-%s-%s.%s' % (e, n, v, r, a)
92 nevra = '%s-%s:%s-%s.%s' % (n, e, v, r, a)
93 for item in [name, nameArch, nameVerRelArch, nameVer, nameVerRel, envra, nevra]:
94 if not pkgdict.has_key(item):
95 pkgdict[item] = []
96 pkgdict[item].append(pkg)
97
98 return pkgdict
99
100 -def parsePackages(pkgs, usercommands, casematch=0,
101 unique='repo-epoch-name-version-release-arch'):
102 """matches up the user request versus a pkg list:
103 for installs/updates available pkgs should be the 'others list'
104 for removes it should be the installed list of pkgs
105 takes an optional casematch option to determine if case should be matched
106 exactly. Defaults to not matching."""
107
108 pkgdict = buildPkgRefDict(pkgs, bool(casematch))
109 exactmatch = []
110 matched = []
111 unmatched = []
112 for command in usercommands:
113 if not casematch:
114 command = command.lower()
115 if command in pkgdict:
116 exactmatch.extend(pkgdict[command])
117 del pkgdict[command]
118 else:
119
120
121 if misc.re_glob(command):
122 trylist = pkgdict.keys()
123
124
125 restring = fnmatch.translate(command)
126 regex = re.compile(restring)
127 foundit = 0
128 for item in trylist:
129 if regex.match(item):
130 matched.extend(pkgdict[item])
131 del pkgdict[item]
132 foundit = 1
133
134 if not foundit:
135 unmatched.append(command)
136
137 else:
138 unmatched.append(command)
139
140 unmatched = misc.unique(unmatched)
141 if unique == 'repo-epoch-name-version-release-arch':
142 matched = misc.unique(matched)
143 exactmatch = misc.unique(exactmatch)
144 elif unique == 'repo-pkgkey':
145 def pkgunique(pkgs):
146 u = {}
147 for pkg in pkgs:
148 mark = "%s%s" % (pkg.repo.id, pkg.pkgKey)
149 u[mark] = pkg
150 return u.values()
151 matched = pkgunique(matched)
152 exactmatch = pkgunique(exactmatch)
153 else:
154 raise ValueError, "Bad value for unique: %s" % unique
155 return exactmatch, matched, unmatched
156
158 """ Fake PackageSack to use with FakeRepository"""
161
163 """delete a pkgobject, do nothing, but make localpackages work with --skip-broken"""
164 pass
165
167 """Fake repository class for use in rpmsack package objects"""
168
170 """ Set the repoid, but because it can be random ... clean it up. """
171
172
173
174
175 pathbased = False
176 if '/' in repoid:
177 repoid = os.path.basename(repoid)
178 pathbased = True
179
180 if repoid.endswith(".rpm"):
181 repoid = repoid[:-4]
182 pathbased = True
183
184 bytes = []
185 if pathbased:
186 bytes.append('/')
187
188 for byte in repoid:
189 if ord(byte) >= 128:
190 byte = '?'
191 bytes.append(byte)
192 self.id = "".join(bytes)
193
197
199 if self.id > other.id:
200 return 1
201 elif self.id < other.id:
202 return -1
203 else:
204 return 0
205
208
211
212
213
214
216 """Base Package Object - sets up the default storage dicts and the
217 most common returns"""
218
220 self.name = None
221 self.version = None
222 self.release = None
223 self.epoch = None
224 self.arch = None
225
226 self._checksums = []
227
241 ui_envra = property(fget=lambda self: self._ui_envra())
242
256 ui_nevra = property(fget=lambda self: self._ui_nevra())
257
260
262 """ Compare package to another one, only rpm-version ordering. """
263 if not other:
264 return 1
265 ret = cmp(self.name, other.name)
266 if ret == 0:
267 ret = comparePoEVR(self, other)
268 return ret
269
271 """ Compare packages, this is just for UI/consistency. """
272 ret = self.verCMP(other)
273 if ret == 0:
274 ret = cmp(self.arch, other.arch)
275 if ret == 0 and hasattr(self, 'repoid') and hasattr(other, 'repoid'):
276 ret = cmp(self.repoid, other.repoid)
277 return ret
279 """ Compare packages for yes/no equality, includes everything in the
280 UI package comparison. """
281 if not other:
282 return False
283 if self.pkgtup != other.pkgtup:
284 return False
285 if self.repoid != other.repoid:
286 return False
287 return True
289 if not (self == other):
290 return True
291 return False
292
294 return getattr(self, key)
295
297 """ Compare package to another one, only rpm-version equality. """
298 if not other:
299 return False
300 ret = cmp(self.name, other.name)
301 if ret != 0:
302 return False
303 return comparePoEVREQ(self, other)
305 """ Uses verCMP, tests if the other _rpm-version_ is < ours. """
306 return self.verCMP(other) < 0
308 """ Uses verCMP, tests if the other _rpm-version_ is <= ours. """
309 return self.verCMP(other) <= 0
311 """ Uses verCMP, tests if the other _rpm-version_ is > ours. """
312 return self.verCMP(other) > 0
314 """ Uses verCMP, tests if the other _rpm-version_ is >= ours. """
315 return self.verCMP(other) >= 0
316
318 return "<%s : %s (%s)>" % (self.__class__.__name__, str(self),hex(id(self)))
319
321 warnings.warn("returnSimple() will go away in a future version of Yum.\n",
322 Errors.YumFutureDeprecationWarning, stacklevel=2)
323 return getattr(self, varname)
324
326 return self._checksums
327
328 checksums = property(fget=lambda self: self.returnChecksums())
329
331 for (csumtype, csum, csumid) in self.checksums:
332 if csumid:
333 return (csumtype, csum)
334
336 """return functions and storage for rpm-specific data"""
337
339 self.prco = {}
340 self.prco['obsoletes'] = []
341 self.prco['conflicts'] = []
342 self.prco['requires'] = []
343 self.prco['provides'] = []
344 self.files = {}
345 self.files['file'] = []
346 self.files['dir'] = []
347 self.files['ghost'] = []
348 self._changelog = []
349 self.licenses = []
350 self._hash = None
351
352
353
355 if not other:
356 return False
357 if self.pkgtup != other.pkgtup:
358 return False
359 if self.repoid != other.repoid:
360 return False
361 return True
363 if not (self == other):
364 return True
365 return False
366
369
371 if self._hash is None:
372 mystr = '%s - %s:%s-%s-%s.%s' % (self.repo.id, self.epoch, self.name,
373 self.version, self.release, self.arch)
374 self._hash = hash(mystr)
375 return self._hash
376
378 """return list of provides, requires, conflicts or obsoletes"""
379
380 prcos = []
381 if self.prco.has_key(prcotype):
382 prcos = self.prco[prcotype]
383
384 if printable:
385 results = []
386 for prco in prcos:
387 results.append(misc.prco_tuple_to_string(prco))
388 return results
389
390 return prcos
391
393 """returns 1 or 0 if the pkg contains the requested tuple/tuple range"""
394
395 if prcotype not in self.prco:
396 return 0
397
398
399
400 if len(self.prco[prcotype]) <= 8:
401 if prcotuple in self.prco[prcotype]:
402 return 1
403 else:
404 if not hasattr(self, '_prco_lookup'):
405 self._prco_lookup = {'obsoletes' : None, 'conflicts' : None,
406 'requires' : None, 'provides' : None}
407
408 if self._prco_lookup[prcotype] is None:
409 self._prco_lookup[prcotype] = set(self.prco[prcotype])
410
411 if prcotuple in self._prco_lookup[prcotype]:
412 return 1
413
414 if True:
415
416 (reqn, reqf, (reqe, reqv ,reqr)) = prcotuple
417 if reqf is not None:
418 return self.inPrcoRange(prcotype, prcotuple)
419 else:
420 for (n, f, (e, v, r)) in self.returnPrco(prcotype):
421 if i18n.str_eq(reqn, n):
422 return 1
423
424 return 0
425
427 """returns true if the package has a the prco that satisfies
428 the reqtuple range, assume false.
429 Takes: prcotype, requested prco tuple"""
430 return bool(self.matchingPrcos(prcotype, reqtuple))
431
433 (reqn, reqf, (reqe, reqv, reqr)) = reqtuple
434
435 result = []
436 for (n, f, (e, v, r)) in self.returnPrco(prcotype):
437 if not i18n.str_eq(reqn, n):
438 continue
439
440 if f == '=':
441 f = 'EQ'
442 if f != 'EQ' and prcotype == 'provides':
443
444
445
446 if e is None:
447 e = self.epoch
448 if v is None:
449 v = self.ver
450 if r is None:
451 r = self.rel
452
453
454 matched = rpmUtils.miscutils.rangeCompare(
455 reqtuple, (n, f, (e, v, r)))
456 if matched:
457 result.append((n, f, (e, v, r)))
458
459 return result
460
461
462
464 """return changelog entries"""
465 return self._changelog
466
468 """return list of files based on type, you can pass primary_only=True
469 to limit to those files in the primary repodata"""
470 if self.files:
471 if ftype in self.files:
472 if primary_only:
473 if ftype == 'dir':
474 match = misc.re_primary_dirname
475 else:
476 match = misc.re_primary_filename
477 return [fn for fn in self.files[ftype] if match(fn)]
478 return self.files[ftype]
479 return []
480
482 """return list of types of files in the package"""
483
484 return self.files.keys()
485
487 if not hasattr(self, '_cache_prco_names_' + prcotype):
488 data = [n for (n, f, v) in self.returnPrco(prcotype)]
489 setattr(self, '_cache_prco_names_' + prcotype, data)
490 return getattr(self, '_cache_prco_names_' + prcotype)
491
496
498 warnings.warn('simpleFiles() will go away in a future version of Yum.'
499 'Use returnFileEntries(primary_only=True)\n',
500 Errors.YumDeprecationWarning, stacklevel=2)
501 if self.files and ftype in self.files:
502 return self.files[ftype]
503 return []
504
505 filelist = property(fget=lambda self: self.returnFileEntries(ftype='file'))
506 dirlist = property(fget=lambda self: self.returnFileEntries(ftype='dir'))
507 ghostlist = property(fget=lambda self: self.returnFileEntries(ftype='ghost'))
508 requires = property(fget=lambda self: self.returnPrco('requires'))
509 provides = property(fget=lambda self: self.returnPrco('provides'))
510 obsoletes = property(fget=lambda self: self.returnPrco('obsoletes'))
511 conflicts = property(fget=lambda self: self.returnPrco('conflicts'))
512 provides_names = property(fget=lambda self: self.returnPrcoNames('provides'))
513 requires_names = property(fget=lambda self: self.returnPrcoNames('requires'))
514 conflicts_names = property(fget=lambda self: self.returnPrcoNames('conflicts'))
515 obsoletes_names = property(fget=lambda self: self.returnPrcoNames('obsoletes'))
516 provides_print = property(fget=lambda self: self.returnPrco('provides', True))
517 requires_print = property(fget=lambda self: self.returnPrco('requires', True))
518 conflicts_print = property(fget=lambda self: self.returnPrco('conflicts', True))
519 obsoletes_print = property(fget=lambda self: self.returnPrco('obsoletes', True))
520 changelog = property(fget=lambda self: self.returnChangelog())
521 EVR = property(fget=lambda self: self.returnEVR())
522
524 """ Return the "base name" of the package, atm. we can only look at
525 the sourcerpm. """
526 if hasattr(self, '_base_package_name_ret'):
527 return self._base_package_name_ret
528
529 if hasattr(self, 'sourcerpm') and self.sourcerpm:
530 (n, v, r, e, a) = rpmUtils.miscutils.splitFilename(self.sourcerpm)
531 if n != self.name:
532 self._base_package_name_ret = n
533 return n
534
535
536 self._base_package_name_ret = self.name
537 return self._base_package_name_ret
538
539 base_package_name = property(fget=lambda self: self._getBaseName())
540
541
543
544 """
545 A comparable epoch, version, and release representation.
546 """
547
554
557
559 if self.compare(other) < 0:
560 return True
561 return False
562
563
565 if self.compare(other) > 0:
566 return True
567 return False
568
570 if self.compare(other) <= 0:
571 return True
572 return False
573
575 if self.compare(other) >= 0:
576 return True
577 return False
578
581
583 if not (self == other):
584 return True
585 return False
586
587
588
590 """derived class for the packageobject and RpmBase packageobject yum
591 uses this for dealing with packages in a repository"""
592
593 - def __init__(self, repo, pkgdict = None):
608
610 """ This reports the repo the package is from, we integrate YUMDB info.
611 for RPM packages so a package from "fedora" that is installed has a
612 ui_from_repo of "@fedora". Note that, esp. with the --releasever
613 option, "fedora" or "rawhide" isn't authoritive.
614 So we also check against the current releasever and if it is
615 different we also print the YUMDB releasever. This means that
616 installing from F12 fedora, while running F12, would report as
617 "@fedora/13". """
618 if self.repoid == 'installed' and 'from_repo' in self.yumdb_info:
619 end = ''
620 if (self.rpmdb.releasever is not None and
621 'releasever' in self.yumdb_info and
622 self.yumdb_info.releasever != self.rpmdb.releasever):
623 end = '/' + self.yumdb_info.releasever
624 return '@' + self.yumdb_info.from_repo + end
625 return self.repoid
626 ui_from_repo = property(fget=lambda self: self._ui_from_repo())
627
629 """remove self from package sack"""
630 self.repo.sack.delPackage(self)
631
633 """returns a printable version string - including epoch, if it's set"""
634 if self.epoch != '0':
635 ver = '%s:%s-%s' % (self.epoch, self.version, self.release)
636 else:
637 ver = '%s-%s' % (self.version, self.release)
638
639 return ver
640
644
646 return self.packagesize
647
649 return self.relativepath
650
652 """returns a URL that can be used for downloading the package.
653 Note that if you're going to download the package in your tool,
654 you should use self.repo.getPackage."""
655 base = self.basepath
656 if base:
657
658 if base[-1] != '/':
659 base = base + '/'
660 return urlparse.urljoin(base, self.remote_path)
661 return urlparse.urljoin(self.repo.urls[0], self.remote_path)
662
663 size = property(fget=lambda self: self._size())
664 remote_path = property(_remote_path)
665 remote_url = property(_remote_url)
666
668 "Returns the name of the last person to do a commit to the changelog."
669
670 if hasattr(self, '_committer_ret'):
671 return self._committer_ret
672
673 def _nf2ascii(x):
674 """ does .encode("ascii", "replace") but it never fails. """
675 ret = []
676 for val in x:
677 if ord(val) >= 128:
678 val = '?'
679 ret.append(val)
680 return "".join(ret)
681
682 if not len(self.changelog):
683 self._committer_ret = self.packager
684 return self._committer_ret
685 val = self.changelog[0][1]
686
687
688 val = _nf2ascii(val)
689
690 ix = val.find('> ')
691 if ix != -1:
692 val = val[0:ix+1]
693 self._committer_ret = val
694 return self._committer_ret
695
696 committer = property(_committer)
697
699 "Returns the time of the last commit to the changelog."
700
701 if hasattr(self, '_committime_ret'):
702 return self._committime_ret
703
704 if not len(self.changelog):
705 self._committime_ret = self.buildtime
706 return self._committime_ret
707
708 self._committime_ret = self.changelog[0][0]
709 return self._committime_ret
710
711 committime = property(_committime)
712
713
715 "Returns the 'default' checksum"
716 return self.checksums[0][1]
717 checksum = property(_checksum)
718
720 if self.basepath is None:
721 return None
722 (scheme, netloc, path, query, fragid) = urlparse.urlsplit(self.basepath)
723 if scheme == "media":
724 if len(fragid) == 0:
725 return 0
726 return int(fragid)
727 return None
728
734
736 """returns an rpm header object from the package object's local
737 header cache"""
738
739 if os.path.exists(self.localHdr()):
740 try:
741 hlist = rpm.readHeaderListFromFile(self.localHdr())
742 hdr = hlist[0]
743 except (rpm.error, IndexError):
744 raise Errors.RepoError, 'Cannot open package header'
745 else:
746 raise Errors.RepoError, 'Package Header Not Available'
747
748 return hdr
749
750
752 """return path to local package (whether it is present there, or not)"""
753 if not hasattr(self, 'localpath'):
754 rpmfn = os.path.basename(self.remote_path)
755 self.localpath = self.repo.pkgdir + '/' + rpmfn
756 return self.localpath
757
759 """return path to local cached Header file downloaded from package
760 byte ranges"""
761
762 if not hasattr(self, 'hdrpath'):
763 pkgname = os.path.basename(self.remote_path)
764 hdrname = pkgname[:-4] + '.hdr'
765 self.hdrpath = self.repo.hdrdir + '/' + hdrname
766
767 return self.hdrpath
768
770 """check the package checksum vs the localPkg
771 return True if pkg is good, False if not"""
772
773
774
775 try:
776 nst = os.stat(self.localPkg())
777 except OSError, e:
778 return False
779 if (hasattr(self, '_verify_local_pkg_cache') and
780 self._verify_local_pkg_cache):
781 ost = self._verify_local_pkg_cache
782 if (ost.st_ino == nst.st_ino and
783 ost.st_dev == nst.st_dev and
784 ost.st_mtime == nst.st_mtime and
785 ost.st_size == nst.st_size):
786 return True
787
788 (csum_type, csum) = self.returnIdSum()
789
790 try:
791 filesum = misc.checksum(csum_type, self.localPkg(),
792 datasize=self.packagesize)
793 except Errors.MiscError:
794 return False
795
796 if filesum != csum:
797 return False
798
799 self._verify_local_pkg_cache = nst
800
801 return True
802
808
810 """return a list of requires in normal rpm format"""
811 return self.requires_print
812
814 return [(self.checksum_type, self.pkgId, 1)]
815
817 """handles an mdCache package dictionary item to populate out
818 the package information"""
819
820
821
822
823 if hasattr(pkgdict, 'nevra'):
824 (n, e, v, r, a) = pkgdict.nevra
825 self.name = n
826 self.epoch = e
827 self.version = v
828 self.arch = a
829 self.release = r
830
831 if hasattr(pkgdict, 'time'):
832 self.buildtime = pkgdict.time['build']
833 self.filetime = pkgdict.time['file']
834
835 if hasattr(pkgdict, 'size'):
836 self.packagesize = pkgdict.size['package']
837 self.archivesize = pkgdict.size['archive']
838 self.installedsize = pkgdict.size['installed']
839
840 if hasattr(pkgdict, 'location'):
841 if not pkgdict.location.has_key('base'):
842 url = None
843 elif pkgdict.location['base'] == '':
844 url = None
845 else:
846 url = pkgdict.location['base']
847
848 self.basepath = url
849 self.relativepath = pkgdict.location['href']
850
851 if hasattr(pkgdict, 'hdrange'):
852 self.hdrstart = pkgdict.hdrange['start']
853 self.hdrend = pkgdict.hdrange['end']
854
855 if hasattr(pkgdict, 'info'):
856 for item in ['summary', 'description', 'packager', 'group',
857 'buildhost', 'sourcerpm', 'url', 'vendor']:
858 setattr(self, item, pkgdict.info[item])
859 self.summary = self.summary.replace('\n', '')
860
861 self.licenses.append(pkgdict.info['license'])
862
863 if hasattr(pkgdict, 'files'):
864 for fn in pkgdict.files:
865 ftype = pkgdict.files[fn]
866 if not self.files.has_key(ftype):
867 self.files[ftype] = []
868 self.files[ftype].append(fn)
869
870 if hasattr(pkgdict, 'prco'):
871 for rtype in pkgdict.prco:
872 for rdict in pkgdict.prco[rtype]:
873 name = rdict['name']
874 f = e = v = r = None
875 if rdict.has_key('flags'): f = rdict['flags']
876 if rdict.has_key('epoch'): e = rdict['epoch']
877 if rdict.has_key('ver'): v = rdict['ver']
878 if rdict.has_key('rel'): r = rdict['rel']
879 self.prco[rtype].append((name, f, (e,v,r)))
880
881 if hasattr(pkgdict, 'changelog'):
882 for cdict in pkgdict.changelog:
883 date = text = author = None
884 if cdict.has_key('date'): date = cdict['date']
885 if cdict.has_key('value'): text = cdict['value']
886 if cdict.has_key('author'): author = cdict['author']
887 self._changelog.append((date, author, text))
888
889 if hasattr(pkgdict, 'checksum'):
890 ctype = pkgdict.checksum['type']
891 csum = pkgdict.checksum['value']
892 csumid = pkgdict.checksum['pkgid']
893 if csumid is None or csumid.upper() == 'NO':
894 csumid = 0
895 elif csumid.upper() == 'YES':
896 csumid = 1
897 else:
898 csumid = 0
899 self._checksums.append((ctype, csum, csumid))
900
901
902
903
905
906 base = os.path.dirname(self.remote_url)
907 href = os.path.basename(self.remote_url)
908 msg = """<location xml:base="%s" href="%s"/>\n""" % (
909 misc.to_xml(base,attrib=True), misc.to_xml(href, attrib=True))
910 return msg
911
913
914 packager = url = ''
915 if self.packager:
916 packager = misc.to_unicode(misc.to_xml(self.packager))
917
918 if self.url:
919 url = misc.to_unicode(misc.to_xml(self.url))
920 (csum_type, csum, csumid) = self.checksums[0]
921 msg = """
922 <name>%s</name>
923 <arch>%s</arch>
924 <version epoch="%s" ver="%s" rel="%s"/>
925 <checksum type="%s" pkgid="YES">%s</checksum>
926 <summary>%s</summary>
927 <description>%s</description>
928 <packager>%s</packager>
929 <url>%s</url>
930 <time file="%s" build="%s"/>
931 <size package="%s" installed="%s" archive="%s"/>\n""" % (self.name,
932 self.arch, self.epoch, self.ver, self.rel, csum_type, csum,
933 misc.to_unicode(misc.to_xml(self.summary)),
934 misc.to_unicode(misc.to_xml(self.description)),
935 packager, url, self.filetime,
936 self.buildtime, self.packagesize, self.size, self.archivesize)
937
938 msg += self._return_remote_location()
939 return msg
940
974
976
977 msg = ""
978 mylist = getattr(self, pcotype)
979 if mylist: msg = "\n <rpm:%s>\n" % pcotype
980 for (name, flags, (e,v,r)) in mylist:
981 pcostring = ''' <rpm:entry name="%s"''' % misc.to_xml(name, attrib=True)
982 if flags:
983 pcostring += ''' flags="%s"''' % misc.to_xml(flags, attrib=True)
984 if e:
985 pcostring += ''' epoch="%s"''' % misc.to_xml(e, attrib=True)
986 if v:
987 pcostring += ''' ver="%s"''' % misc.to_xml(v, attrib=True)
988 if r:
989 pcostring += ''' rel="%s"''' % misc.to_xml(r, attrib=True)
990
991 pcostring += "/>\n"
992 msg += pcostring
993
994 if mylist: msg += " </rpm:%s>" % pcotype
995 return msg
996
1016
1017
1019 raise NotImplementedError()
1020
1022 """returns deps in format"""
1023 mylist = self._requires_with_pre()
1024
1025 msg = ""
1026
1027 if mylist: msg = "\n <rpm:requires>\n"
1028 for (name, flags, (e,v,r),pre) in mylist:
1029 if name.startswith('rpmlib('):
1030 continue
1031
1032 if name in self.provides_names or name in self.filelist + \
1033 self.dirlist + self.ghostlist:
1034 if not flags:
1035 continue
1036 else:
1037 if self.checkPrco('provides', (name, flags, (e,v,r))):
1038 continue
1039 prcostring = ''' <rpm:entry name="%s"''' % misc.to_xml(name, attrib=True)
1040 if flags:
1041 prcostring += ''' flags="%s"''' % misc.to_xml(flags, attrib=True)
1042 if e:
1043 prcostring += ''' epoch="%s"''' % misc.to_xml(e, attrib=True)
1044 if v:
1045 prcostring += ''' ver="%s"''' % misc.to_xml(v, attrib=True)
1046 if r:
1047 prcostring += ''' rel="%s"''' % misc.to_xml(r, attrib=True)
1048 if pre:
1049 prcostring += ''' pre="%s"''' % pre
1050
1051 prcostring += "/>\n"
1052 msg += prcostring
1053
1054 if mylist: msg += " </rpm:requires>"
1055 return msg
1056
1058 if not self.changelog:
1059 return ""
1060 msg = "\n"
1061
1062 if not clog_limit:
1063 clogs = self.changelog
1064 else:
1065 clogs = self.changelog[:clog_limit]
1066 last_ts = 0
1067 hack_ts = 0
1068 for (ts, author, content) in reversed(clogs):
1069 if ts != last_ts:
1070 hack_ts = 0
1071 else:
1072 hack_ts += 1
1073 last_ts = ts
1074 ts += hack_ts
1075 msg += """<changelog author="%s" date="%s">%s</changelog>\n""" % (
1076 misc.to_xml(author, attrib=True), misc.to_xml(str(ts)),
1077 misc.to_xml(content))
1078 return msg
1079
1086
1094
1101
1102
1103
1104
1106 """Package object built from an rpm header"""
1108 """hand in an rpm header, we'll assume it's installed and query from there"""
1109
1110 YumAvailablePackage.__init__(self, repo)
1111
1112 self.hdr = hdr
1113 self.name = misc.share_data(self.hdr['name'])
1114 this_a = self.hdr['arch']
1115 if not this_a:
1116 this_a = 'noarch'
1117 self.arch = misc.share_data(this_a)
1118 self.epoch = misc.share_data(self.doepoch())
1119 self.version = misc.share_data(self.hdr['version'])
1120 self.release = misc.share_data(self.hdr['release'])
1121 self.ver = self.version
1122 self.rel = self.release
1123 self.pkgtup = (self.name, self.arch, self.epoch, self.version, self.release)
1124
1125 self.summary = self.hdr['summary'] or ''
1126 self.summary = misc.share_data(self.summary.replace('\n', ''))
1127 self.description = self.hdr['description'] or ''
1128 self.description = misc.share_data(self.description)
1129 self.pkgid = self.hdr[rpm.RPMTAG_SHA1HEADER]
1130 if not self.pkgid:
1131 self.pkgid = "%s.%s" %(self.hdr['name'], self.hdr['buildtime'])
1132 self.packagesize = self.hdr['size']
1133 self.__mode_cache = {}
1134 self.__prcoPopulated = False
1135
1144
1150
1153
1155 "Populate the package object with the needed PRCO interface."
1156
1157 tag2prco = { "OBSOLETE": misc.share_data("obsoletes"),
1158 "CONFLICT": misc.share_data("conflicts"),
1159 "REQUIRE": misc.share_data("requires"),
1160 "PROVIDE": misc.share_data("provides") }
1161 hdr = self._get_hdr()
1162 for tag in tag2prco:
1163 name = hdr[getattr(rpm, 'RPMTAG_%sNAME' % tag)]
1164 name = map(misc.share_data, name)
1165 if name is None:
1166 continue
1167
1168 lst = hdr[getattr(rpm, 'RPMTAG_%sFLAGS' % tag)]
1169 flag = map(rpmUtils.miscutils.flagToString, lst)
1170 flag = map(misc.share_data, flag)
1171
1172 lst = hdr[getattr(rpm, 'RPMTAG_%sVERSION' % tag)]
1173 vers = map(rpmUtils.miscutils.stringToVersion, lst)
1174 vers = map(lambda x: (misc.share_data(x[0]), misc.share_data(x[1]),
1175 misc.share_data(x[2])), vers)
1176
1177 prcotype = tag2prco[tag]
1178 self.prco[prcotype] = map(misc.share_data, zip(name,flag,vers))
1179
1181 warnings.warn("tagByName() will go away in a furture version of Yum.\n",
1182 Errors.YumFutureDeprecationWarning, stacklevel=2)
1183 try:
1184 return getattr(self, tag)
1185 except AttributeError:
1186 raise Errors.MiscError, "Unknown header tag %s" % tag
1187
1189
1190
1191 if thing.startswith('__') and thing.endswith('__'):
1192
1193
1194 raise AttributeError, "%s has no attribute %s" % (self, thing)
1195 try:
1196 return self.hdr[thing]
1197 except KeyError:
1198
1199
1200 raise KeyError, "%s has no attribute %s" % (self, thing)
1201
1203 tmpepoch = self.hdr['epoch']
1204 if tmpepoch is None:
1205 epoch = '0'
1206 else:
1207 epoch = str(tmpepoch)
1208
1209 return epoch
1210
1213
1214
1216 files = self.hdr['filenames']
1217 fileflags = self.hdr['fileflags']
1218 filemodes = self.hdr['filemodes']
1219 filetuple = zip(files, filemodes, fileflags)
1220 if not self._loadedfiles:
1221 for (fn, mode, flag) in filetuple:
1222
1223 if mode is None or mode == '':
1224 if not self.files.has_key('file'):
1225 self.files['file'] = []
1226 self.files['file'].append(fn)
1227 continue
1228 if mode not in self.__mode_cache:
1229 self.__mode_cache[mode] = stat.S_ISDIR(mode)
1230
1231 fkey = 'file'
1232 if self.__mode_cache[mode]:
1233 fkey = 'dir'
1234 elif flag is not None and (flag & 64):
1235 fkey = 'ghost'
1236 self.files.setdefault(fkey, []).append(fn)
1237
1238 self._loadedfiles = True
1239
1244
1254
1256 raise NotImplementedError()
1257
1259 return self.hdr['size']
1260
1262 """check the flags for a requirement, return 1 or 0 whether or not requires
1263 is a pre-requires or a not"""
1264
1265
1266 if flag is not None:
1267
1268 if flag & (rpm.RPMSENSE_PREREQ |
1269 rpm.RPMSENSE_SCRIPT_PRE |
1270 rpm.RPMSENSE_SCRIPT_POST):
1271 return 1
1272 return 0
1273
1275 """returns requires with pre-require bit"""
1276 name = self.hdr[rpm.RPMTAG_REQUIRENAME]
1277 lst = self.hdr[rpm.RPMTAG_REQUIREFLAGS]
1278 flag = map(flagToString, lst)
1279 pre = map(self._is_pre_req, lst)
1280 lst = self.hdr[rpm.RPMTAG_REQUIREVERSION]
1281 vers = map(stringToVersion, lst)
1282 if name is not None:
1283 lst = zip(name, flag, vers, pre)
1284 mylist = misc.unique(lst)
1285 return mylist
1286
1288 """ Has just a read() method, and keeps a count so we can find out how much
1289 has been read. Implemented so we can get the real size of the file from
1290 prelink. """
1291
1293 self.fp = fp
1294 self.read_size = 0
1295
1296 - def read(self, size):
1297 ret = self.fp.read(size)
1298 self.read_size += len(ret)
1299 return ret
1300
1302 """ Holder for each "problem" we find with a pkg.verify(). """
1303
1304 - def __init__(self, type, msg, ftypes, fake=False):
1305 self.type = type
1306 self.message = msg
1307 self.database_value = None
1308 self.disk_value = None
1309 self.file_types = ftypes
1310 self.fake = fake
1311
1313 if other is None:
1314 return 1
1315 type2sort = {'type' : 1, 'symlink' : 2, 'checksum' : 3, 'size' : 4,
1316 'user' : 4, 'group' : 5, 'mode' : 6, 'genchecksum' : 7,
1317 'mtime' : 8, 'missing' : 9, 'permissions-missing' : 10,
1318 'state' : 11, 'missingok' : 12, 'ghost' : 13}
1319 ret = cmp(type2sort[self.type], type2sort[other.type])
1320 if not ret:
1321 for attr in ['disk_value', 'database_value', 'file_types']:
1322 x = getattr(self, attr)
1323 y = getattr(other, attr)
1324 if x is None:
1325 assert y is None
1326 continue
1327 ret = cmp(x, y)
1328 if ret:
1329 break
1330 return ret
1331
1332
1333 _RPMVERIFY_DIGEST = (1 << 0)
1334 _RPMVERIFY_FILESIZE = (1 << 1)
1335 _RPMVERIFY_LINKTO = (1 << 2)
1336 _RPMVERIFY_USER = (1 << 3)
1337 _RPMVERIFY_GROUP = (1 << 4)
1338 _RPMVERIFY_MTIME = (1 << 5)
1339 _RPMVERIFY_MODE = (1 << 6)
1340 _RPMVERIFY_RDEV = (1 << 7)
1341
1342 _installed_repo = FakeRepository('installed')
1343 _installed_repo.cost = 0
1345 """super class for dealing with packages in the rpmdb"""
1350
1351 - def verify(self, patterns=[], deps=False, script=False,
1352 fake_problems=True, all=False, fast=False):
1353 """verify that the installed files match the packaged checksum
1354 optionally verify they match only if they are in the 'pattern' list
1355 returns a tuple """
1356 def _ftype(mode):
1357 """ Given a "mode" return the name of the type of file. """
1358 if stat.S_ISREG(mode): return "file"
1359 if stat.S_ISDIR(mode): return "directory"
1360 if stat.S_ISLNK(mode): return "symlink"
1361 if stat.S_ISFIFO(mode): return "fifo"
1362 if stat.S_ISCHR(mode): return "character device"
1363 if stat.S_ISBLK(mode): return "block device"
1364 return "<unknown>"
1365
1366 statemap = {rpm.RPMFILE_STATE_REPLACED : 'replaced',
1367 rpm.RPMFILE_STATE_NOTINSTALLED : 'not installed',
1368 rpm.RPMFILE_STATE_WRONGCOLOR : 'wrong color',
1369 rpm.RPMFILE_STATE_NETSHARED : 'netshared'}
1370
1371 fi = self.hdr.fiFromHeader()
1372 results = {}
1373
1374
1375 prelink_cmd = "/usr/sbin/prelink"
1376 have_prelink = os.path.exists(prelink_cmd)
1377
1378
1379 csum_type = 'md5'
1380 if hasattr(rpm, 'RPMTAG_FILEDIGESTALGO'):
1381 csum_num = self.hdr[rpm.RPMTAG_FILEDIGESTALGO]
1382 if csum_num:
1383 if csum_num in RPM_CHECKSUM_TYPES:
1384 csum_type = RPM_CHECKSUM_TYPES[csum_num]
1385
1386
1387 for filetuple in fi:
1388
1389
1390 (fn, size, mode, mtime, flags, dev, inode, link, state, vflags,
1391 user, group, csum) = filetuple
1392 if patterns:
1393 matched = False
1394 for p in patterns:
1395 if fnmatch.fnmatch(fn, p):
1396 matched = True
1397 break
1398 if not matched:
1399 continue
1400
1401 ftypes = []
1402 if flags & rpm.RPMFILE_CONFIG:
1403 ftypes.append('configuration')
1404 if flags & rpm.RPMFILE_DOC:
1405 ftypes.append('documentation')
1406 if flags & rpm.RPMFILE_GHOST:
1407 ftypes.append('ghost')
1408 if flags & rpm.RPMFILE_LICENSE:
1409 ftypes.append('license')
1410 if flags & rpm.RPMFILE_PUBKEY:
1411 ftypes.append('public key')
1412 if flags & rpm.RPMFILE_README:
1413 ftypes.append('README')
1414 if flags & rpm.RPMFILE_MISSINGOK:
1415 ftypes.append('missing ok')
1416
1417
1418
1419
1420 if all:
1421 vflags = -1
1422
1423 if state != rpm.RPMFILE_STATE_NORMAL:
1424 if state in statemap:
1425 ftypes.append("state=" + statemap[state])
1426 else:
1427 ftypes.append("state=<unknown>")
1428 if fake_problems:
1429 results[fn] = [_PkgVerifyProb('state',
1430 'state is not normal',
1431 ftypes, fake=True)]
1432 continue
1433
1434 if flags & rpm.RPMFILE_MISSINGOK and fake_problems:
1435 results[fn] = [_PkgVerifyProb('missingok', 'missing but ok',
1436 ftypes, fake=True)]
1437 if flags & rpm.RPMFILE_MISSINGOK and not all:
1438 continue
1439
1440 if flags & rpm.RPMFILE_GHOST and fake_problems:
1441 results[fn] = [_PkgVerifyProb('ghost', 'ghost file', ftypes,
1442 fake=True)]
1443 if flags & rpm.RPMFILE_GHOST and not all:
1444 continue
1445
1446
1447 problems = []
1448 if os.path.lexists(fn):
1449
1450 my_st = os.lstat(fn)
1451 my_st_size = my_st.st_size
1452 try:
1453 my_user = pwd.getpwuid(my_st[stat.ST_UID])[0]
1454 except KeyError, e:
1455 my_user = 'uid %s not found' % my_st[stat.ST_UID]
1456 try:
1457 my_group = grp.getgrgid(my_st[stat.ST_GID])[0]
1458 except KeyError, e:
1459 my_group = 'gid %s not found' % my_st[stat.ST_GID]
1460
1461 if mode < 0:
1462
1463
1464 mode = (mode & 0xFFFF)
1465
1466 ftype = _ftype(mode)
1467 my_ftype = _ftype(my_st.st_mode)
1468
1469 if vflags & _RPMVERIFY_RDEV and ftype != my_ftype:
1470 prob = _PkgVerifyProb('type', 'file type does not match',
1471 ftypes)
1472 prob.database_value = ftype
1473 prob.disk_value = my_ftype
1474 problems.append(prob)
1475
1476 if (ftype == "symlink" and my_ftype == "symlink" and
1477 vflags & _RPMVERIFY_LINKTO):
1478 fnl = fi.FLink()
1479 my_fnl = os.readlink(fn)
1480 if my_fnl != fnl:
1481 prob = _PkgVerifyProb('symlink',
1482 'symlink does not match', ftypes)
1483 prob.database_value = fnl
1484 prob.disk_value = my_fnl
1485 problems.append(prob)
1486
1487 check_content = True
1488 if 'ghost' in ftypes:
1489 check_content = False
1490 if my_ftype == "symlink" and ftype == "file":
1491
1492 my_st_size = os.stat(fn).st_size
1493 elif my_ftype != "file":
1494 check_content = False
1495 check_perms = True
1496 if my_ftype == "symlink":
1497 check_perms = False
1498
1499 if (check_content and vflags & _RPMVERIFY_MTIME and
1500 my_st.st_mtime != mtime):
1501 prob = _PkgVerifyProb('mtime', 'mtime does not match',
1502 ftypes)
1503 prob.database_value = mtime
1504 prob.disk_value = my_st.st_mtime
1505 problems.append(prob)
1506
1507 if check_perms and vflags & _RPMVERIFY_USER and my_user != user:
1508 prob = _PkgVerifyProb('user', 'user does not match', ftypes)
1509 prob.database_value = user
1510 prob.disk_value = my_user
1511 problems.append(prob)
1512 if (check_perms and vflags & _RPMVERIFY_GROUP and
1513 my_group != group):
1514 prob = _PkgVerifyProb('group', 'group does not match',
1515 ftypes)
1516 prob.database_value = group
1517 prob.disk_value = my_group
1518 problems.append(prob)
1519
1520 my_mode = my_st.st_mode
1521 if 'ghost' in ftypes:
1522 my_mode &= 0777
1523 mode &= 0777
1524 if check_perms and vflags & _RPMVERIFY_MODE and my_mode != mode:
1525 prob = _PkgVerifyProb('mode', 'mode does not match', ftypes)
1526 prob.database_value = mode
1527 prob.disk_value = my_st.st_mode
1528 problems.append(prob)
1529
1530 if fast and not problems and (my_st_size == size):
1531 vflags &= ~_RPMVERIFY_DIGEST
1532
1533
1534
1535
1536 if (check_content and
1537 ((have_prelink and (vflags & _RPMVERIFY_FILESIZE) and
1538 (my_st_size != size)) or
1539 (csum and vflags & _RPMVERIFY_DIGEST))):
1540 try:
1541 my_csum = misc.checksum(csum_type, fn)
1542 gen_csum = True
1543 except Errors.MiscError:
1544
1545 gen_csum = False
1546
1547 if csum and vflags & _RPMVERIFY_DIGEST and not gen_csum:
1548 prob = _PkgVerifyProb('genchecksum',
1549 'checksum not available', ftypes)
1550 prob.database_value = csum
1551 prob.disk_value = None
1552 problems.append(prob)
1553
1554 if gen_csum and my_csum != csum and have_prelink:
1555
1556
1557 p = Popen([prelink_cmd, "-y", fn],
1558 bufsize=-1, stdin=PIPE,
1559 stdout=PIPE, stderr=PIPE, close_fds=True)
1560 (ig, fp, er) = (p.stdin, p.stdout, p.stderr)
1561
1562 fp = _CountedReadFile(fp)
1563 tcsum = misc.checksum(csum_type, fp)
1564 if fp.read_size:
1565 my_csum = tcsum
1566 my_st_size = fp.read_size
1567
1568 if (csum and vflags & _RPMVERIFY_DIGEST and gen_csum and
1569 my_csum != csum):
1570 prob = _PkgVerifyProb('checksum',
1571 'checksum does not match', ftypes)
1572 prob.database_value = csum
1573 prob.disk_value = my_csum
1574 problems.append(prob)
1575
1576
1577 if (check_content and vflags & _RPMVERIFY_FILESIZE and
1578 my_st_size != size):
1579 prob = _PkgVerifyProb('size', 'size does not match', ftypes)
1580 prob.database_value = size
1581 prob.disk_value = my_st_size
1582 problems.append(prob)
1583
1584 else:
1585 try:
1586 os.stat(fn)
1587 perms_ok = True
1588 except OSError, e:
1589 perms_ok = True
1590 if e.errno == errno.EACCES:
1591 perms_ok = False
1592
1593 if perms_ok:
1594 prob = _PkgVerifyProb('missing', 'file is missing', ftypes)
1595 else:
1596 prob = _PkgVerifyProb('permissions-missing',
1597 'file is missing (Permission denied)',
1598 ftypes)
1599 problems.append(prob)
1600
1601 if problems:
1602 results[fn] = problems
1603
1604 return results
1605
1606
1608 """Class to handle an arbitrary package from a file path
1609 this inherits most things from YumInstalledPackage because
1610 installed packages and an arbitrary package on disk act very
1611 much alike. init takes a ts instance and a filename/path
1612 to the package."""
1613
1614 - def __init__(self, ts=None, filename=None):
1615 if ts is None:
1616 raise Errors.MiscError, \
1617 'No Transaction Set Instance for YumLocalPackage instance creation'
1618 if filename is None:
1619 raise Errors.MiscError, \
1620 'No Filename specified for YumLocalPackage instance creation'
1621
1622 self.pkgtype = 'local'
1623 self.localpath = filename
1624 self._checksum = None
1625
1626
1627 try:
1628 hdr = rpmUtils.miscutils.hdrFromPackage(ts, self.localpath)
1629 except RpmUtilsError, e:
1630 raise Errors.MiscError, \
1631 'Could not open local rpm file: %s: %s' % (self.localpath, e)
1632
1633 fakerepo = FakeRepository(filename)
1634 fakerepo.cost = 0
1635 YumHeaderPackage.__init__(self, fakerepo, hdr)
1636 self.id = self.pkgid
1637 self._stat = os.stat(self.localpath)
1638 self.filetime = str(self._stat[-2])
1639 self.packagesize = str(self._stat[6])
1640 self.arch = self.isSrpm()
1641 self.pkgtup = (self.name, self.arch, self.epoch, self.ver, self.rel)
1642 self._hdrstart = None
1643 self._hdrend = None
1644 self.checksum_type = misc._default_checksums[0]
1645
1646
1647 self._reldir = None
1648 self._baseurl = ""
1649
1650
1651
1653 if self.tagByName('sourcepackage') == 1 or not self.tagByName('sourcerpm'):
1654 return 'src'
1655 else:
1656 return self.tagByName('arch')
1657
1659 return self.localpath
1660
1669
1670 checksum = property(fget=lambda self: self._do_checksum())
1671
1675
1677 """ don't bother "checking" the package matches itself. """
1678 return True
1679
1681 """takes an rpm file or fileobject and returns byteranges for location of the header"""
1682 if self._hdrstart and self._hdrend:
1683 return (self._hdrstart, self._hdrend)
1684
1685
1686 fo = open(self.localpath, 'r')
1687
1688 fo.seek(104)
1689
1690 binindex = fo.read(4)
1691
1692 (sigindex, ) = struct.unpack('>I', binindex)
1693 bindata = fo.read(4)
1694
1695 (sigdata, ) = struct.unpack('>I', bindata)
1696
1697 sigindexsize = sigindex * 16
1698 sigsize = sigdata + sigindexsize
1699
1700 disttoboundary = (sigsize % 8)
1701 if disttoboundary != 0:
1702 disttoboundary = 8 - disttoboundary
1703
1704 hdrstart = 112 + sigsize + disttoboundary
1705
1706 fo.seek(hdrstart)
1707 fo.seek(8,1)
1708
1709 binindex = fo.read(4)
1710 (hdrindex, ) = struct.unpack('>I', binindex)
1711 bindata = fo.read(4)
1712 (hdrdata, ) = struct.unpack('>I', bindata)
1713
1714
1715 hdrindexsize = hdrindex * 16
1716
1717
1718 hdrsize = hdrdata + hdrindexsize + 16
1719
1720
1721 hdrend = hdrstart + hdrsize
1722 fo.close()
1723 self._hdrstart = hdrstart
1724 self._hdrend = hdrend
1725
1726 return (hdrstart, hdrend)
1727
1728 hdrend = property(fget=lambda self: self._get_header_byte_range()[1])
1729 hdrstart = property(fget=lambda self: self._get_header_byte_range()[0])
1730
1732
1733
1734 if self._reldir and self.localpath.startswith(self._reldir):
1735 relpath = self.localpath.replace(self._reldir, '')
1736 if relpath[0] == '/': relpath = relpath[1:]
1737 else:
1738 relpath = self.localpath
1739
1740 if self._baseurl:
1741 msg = """<location xml:base="%s" href="%s"/>\n""" % (
1742 misc.to_xml(self._baseurl, attrib=True),
1743 misc.to_xml(relpath, attrib=True))
1744 else:
1745 msg = """<location href="%s"/>\n""" % misc.to_xml(relpath, attrib=True)
1746
1747 return msg
1748
1749
1751 """Class to handle an arbitrary package from a URL
1752 this inherits most things from YumLocalPackage, but will download a
1753 remote package to make it local.
1754 init takes a YumBase, a ts instance and a url to the package."""
1755
1756 - def __init__(self, yb=None, ts=None, url=None, ua=None):
1757 if url.lower().startswith("file:"):
1758 result = url[len("file:"):]
1759 elif not misc.re_remote_url(url):
1760 result = url
1761 else:
1762 cb = None
1763 pd = {}
1764 for repo in yb.repos.listEnabled():
1765 cb = repo.callback
1766 if (repo.proxy == yb.conf.proxy and
1767 repo.proxy_username == yb.conf.proxy_username and
1768 repo.proxy_password == yb.conf.proxy_password):
1769
1770 pd = repo.proxy_dict
1771 break
1772 fname = os.path.basename(url)
1773 local = misc.getCacheDir()
1774 if local is None:
1775 local = "%s/../" % repo.cachedir
1776 local = "%s/%s" % (local, fname)
1777 try:
1778 ug = URLGrabber(bandwidth = yb.conf.bandwidth,
1779 retry = yb.conf.retries,
1780 throttle = yb.conf.throttle,
1781 progress_obj = cb,
1782 proxies=pd)
1783 if ua is not None:
1784 ug.opts.user_agent = ua
1785 result = ug.urlgrab(url, local, text=fname)
1786 except URLGrabError, e:
1787 raise Errors.MiscError("Cannot download %s: %s" % (url, e))
1788 YumLocalPackage.__init__(self, ts, result)
1789