~ubuntu-branches/ubuntu/jaunty/yum/jaunty

« back to all changes in this revision

Viewing changes to yum/packages.py

  • Committer: Bazaar Package Importer
  • Author(s): Ben Hutchings
  • Date: 2008-07-28 23:20:59 UTC
  • mfrom: (2.1.3 intrepid)
  • Revision ID: james.westby@ubuntu.com-20080728232059-24lo1r17smhr71l8
Tags: 3.2.12-1.2
* Non-maintainer upload
* Updated for compatibility with current python-pyme (Closes: #490368)
  based on patch by Martin Meredith <mez@ubuntu.com>
  - Changed import in yum/misc.py
  - Set versioned dependency on python-pyme

Show diffs side-by-side

added added

removed removed

Lines of Context:
15
15
# Copyright 2004 Duke University 
16
16
# Written by Seth Vidal <skvidal at phy.duke.edu>
17
17
 
 
18
"""
 
19
Classes and functions dealing with rpm package representations.
 
20
"""
 
21
 
18
22
import rpm
19
23
import os
20
24
import os.path
21
25
import misc
22
26
import re
23
 
import types
24
27
import fnmatch
25
 
import rpmUtils
 
28
import stat
 
29
import warnings
 
30
from rpmUtils import RpmUtilsError
26
31
import rpmUtils.arch
27
32
import rpmUtils.miscutils
28
33
import Errors
29
 
 
30
 
import repomd.packageObject
31
 
 
32
 
base=None
33
 
 
34
 
def buildPkgRefDict(pkgs):
 
34
import errno
 
35
 
 
36
import urlparse
 
37
urlparse.uses_fragment.append("media")
 
38
 
 
39
# For verify
 
40
import pwd
 
41
import grp
 
42
 
 
43
def comparePoEVR(po1, po2):
 
44
    """
 
45
    Compare two PackageEVR objects.
 
46
    """
 
47
    (e1, v1, r1) = (po1.epoch, po1.version, po1.release)
 
48
    (e2, v2, r2) = (po2.epoch, po2.version, po2.release)
 
49
    return rpmUtils.miscutils.compareEVR((e1, v1, r1), (e2, v2, r2))
 
50
 
 
51
def buildPkgRefDict(pkgs, casematch=True):
35
52
    """take a list of pkg objects and return a dict the contains all the possible
36
53
       naming conventions for them eg: for (name,i386,0,1,1)
37
54
       dict[name] = (name, i386, 0, 1, 1)
44
61
       """
45
62
    pkgdict = {}
46
63
    for pkg in pkgs:
47
 
        pkgtup = (pkg.name, pkg.arch, pkg.epoch, pkg.version, pkg.release)
48
 
        (n, a, e, v, r) = pkgtup
 
64
        (n, a, e, v, r) = pkg.pkgtup
 
65
        if not casematch:
 
66
            n = n.lower()
 
67
            a = a.lower()
 
68
            e = e.lower()
 
69
            v = v.lower()
 
70
            r = r.lower()
49
71
        name = n
50
72
        nameArch = '%s.%s' % (n, a)
51
73
        nameVerRelArch = '%s-%s-%s.%s' % (n, v, r, a)
60
82
            
61
83
    return pkgdict
62
84
       
63
 
def parsePackages(pkgs, usercommands, casematch=0):
 
85
def parsePackages(pkgs, usercommands, casematch=0,
 
86
                  unique='repo-epoch-name-version-release-arch'):
64
87
    """matches up the user request versus a pkg list:
65
88
       for installs/updates available pkgs should be the 'others list' 
66
89
       for removes it should be the installed list of pkgs
67
90
       takes an optional casematch option to determine if case should be matched
68
91
       exactly. Defaults to not matching."""
69
92
 
70
 
    pkgdict = buildPkgRefDict(pkgs)
 
93
    def contains(haystack, needle):
 
94
        """ If anything from needle is in haystack, return True. """
 
95
        for x in needle:
 
96
            if x in haystack:
 
97
                return True
 
98
        return False
 
99
 
 
100
    pkgdict = buildPkgRefDict(pkgs, bool(casematch))
71
101
    exactmatch = []
72
102
    matched = []
73
103
    unmatched = []
74
104
    for command in usercommands:
 
105
        if not casematch:
 
106
            command = command.lower()
75
107
        if pkgdict.has_key(command):
76
108
            exactmatch.extend(pkgdict[command])
77
109
            del pkgdict[command]
78
110
        else:
79
111
            # anything we couldn't find a match for
80
112
            # could mean it's not there, could mean it's a wildcard
81
 
            if re.match('.*[\*,\[,\],\{,\},\?].*', command):
 
113
            if contains(command, "*,[]{}?"):
82
114
                trylist = pkgdict.keys()
 
115
                # command and pkgdict are already lowered if not casematch
 
116
                # so case sensitive is always fine
83
117
                restring = fnmatch.translate(command)
84
 
                if casematch:
85
 
                    regex = re.compile(restring) # case sensitive
86
 
                else:
87
 
                    regex = re.compile(restring, flags=re.I) # case insensitive
 
118
                regex = re.compile(restring)
88
119
                foundit = 0
89
120
                for item in trylist:
90
121
                    if regex.match(item):
96
127
                    unmatched.append(command)
97
128
                    
98
129
            else:
99
 
                # we got nada
100
130
                unmatched.append(command)
101
131
 
102
 
    matched = misc.unique(matched)
103
132
    unmatched = misc.unique(unmatched)
104
 
    exactmatch = misc.unique(exactmatch)
 
133
    if unique == 'repo-epoch-name-version-release-arch': # pkg.__hash__
 
134
        matched    = misc.unique(matched)
 
135
        exactmatch = misc.unique(exactmatch)
 
136
    elif unique == 'repo-pkgkey': # So we get all pkg entries from a repo
 
137
        def pkgunique(pkgs):
 
138
            u = {}
 
139
            for pkg in pkgs:
 
140
                mark = "%s%s" % (pkg.repo.id, pkg.pkgKey)
 
141
                u[mark] = pkg
 
142
            return u.values()
 
143
        matched    = pkgunique(matched)
 
144
        exactmatch = pkgunique(exactmatch)
 
145
    else:
 
146
        raise ValueError, "Bad value for unique: %s" % unique
105
147
    return exactmatch, matched, unmatched
106
148
 
107
 
 
108
 
def returnBestPackages(pkgdict, arch=None):
109
 
    """returns a list of package tuples that are the 'best' packages for this
110
 
       arch. Best == highest version and best scoring/sorting arch
111
 
       should consider multiarch separately"""
112
 
    returnlist = []
113
 
    compatArchList = rpmUtils.arch.getArchList(arch)
114
 
    for pkgname in pkgdict.keys():
115
 
        # go through the packages, pitch out the ones that can't be used
116
 
        # on this system at all
117
 
        pkglist = pkgdict[pkgname]
118
 
        uselist = []
119
 
        multiLib = []
120
 
        singleLib = []
121
 
        for pkg in pkglist:
122
 
            (n, a, e, v, r) = pkg
123
 
            if a not in compatArchList:
124
 
                continue
125
 
            elif rpmUtils.arch.isMultiLibArch(arch=a):
126
 
                multiLib.append(pkg)
127
 
            else:
128
 
                singleLib.append(pkg)
129
 
        # we should have two lists now - one of singleLib packages
130
 
        # one of multilib packages
131
 
        # go through each one and find the best package(s)
132
 
        for pkglist in [multiLib, singleLib]:
133
 
            if len(pkglist) > 0:
134
 
                best = pkglist[0]
135
 
            else:
136
 
                continue
137
 
            for pkg in pkglist[1:]:
138
 
                best = bestPackage(best, pkg)
139
 
            if best is not None:
140
 
                returnlist.append(best)
141
 
    
142
 
    return returnlist
143
 
 
144
 
def bestPackage(pkg1, pkg2):
145
 
    """compares two package tuples (assumes the names are the same), and returns
146
 
       the one with the best version and the best arch, the sorting is:
147
 
       for compatible arches, the highest version is best so:
148
 
       foo-1.1-1.i686 is better than foo-1.1-1.i386 on an i686 machine
149
 
       but foo-1.2-1.alpha is not better than foo-1.1-1.i386 on an i686
150
 
       machine and foo-1.3-1.i386 is better than foo-1.1-1.i686 on an i686
151
 
       machine."""
152
 
    (n1, a1, e1, v1, r1) = pkg1
153
 
    (n2, a2, e2, v2, r2) = pkg2
154
 
    rc = rpmUtils.miscutils.compareEVR((e1, v1, r1), (e2, v2, r2))
155
 
    if rc == 0:
156
 
        # tiebreaker
157
 
        bestarch = rpmUtils.arch.getBestArchFromList([a1, a2])
158
 
        if bestarch is None: # how the hell did this happen?
159
 
            return None
160
 
        if bestarch == a1:
161
 
            return pkg1
162
 
        if bestarch == a2:
163
 
            return pkg2
164
 
    elif rc > 0:
165
 
        return pkg1
166
 
    elif rc < 0:
167
 
        return pkg2
168
 
    
 
149
class FakeRepository:
 
150
    """Fake repository class for use in rpmsack package objects"""
 
151
    def __init__(self, repoid):
 
152
        self.id = repoid
 
153
 
 
154
    def __cmp__(self, other):
 
155
        if self.id > other.id:
 
156
            return 1
 
157
        elif self.id < other.id:
 
158
            return -1
 
159
        else:
 
160
            return 0
 
161
        
 
162
    def __hash__(self):
 
163
        return hash(self.id)
 
164
 
 
165
    def __str__(self):
 
166
        return self.id
 
167
 
 
168
 
169
169
# goal for the below is to have a packageobject that can be used by generic
170
170
# functions independent of the type of package - ie: installed or available
171
 
 
172
 
 
173
 
class YumInstalledPackage:
174
 
    """super class for dealing with packages in the rpmdb"""
175
 
    def __init__(self, hdr):
176
 
        """hand in an rpm header, we'll assume it's installed and query from there"""
177
 
        self.hdr = hdr
178
 
        self.name = self.tagByName('name')
179
 
        self.arch = self.tagByName('arch')
180
 
        self.epoch = self.doepoch()
181
 
        self.version = self.tagByName('version')
182
 
        self.release = self.tagByName('release')
183
 
        self.pkgtup = self._pkgtup()
184
 
        self.repoid = 'installed'
185
 
        self.summary = self.tagByName('summary')
186
 
        self.description = self.tagByName('description')
187
 
        self.pkgid = self.tagByName(rpm.RPMTAG_SHA1HEADER)
188
 
    
 
171
class PackageObject(object):
 
172
    """Base Package Object - sets up the default storage dicts and the
 
173
       most common returns"""
 
174
       
 
175
    def __init__(self):
 
176
        self.name = None
 
177
        self.version = None
 
178
        self.release = None
 
179
        self.epoch = None
 
180
        self.arch = None
 
181
        # self.pkgtup = (self.name, self.arch, self.epoch, self.version, self.release)
 
182
        self._checksums = [] # (type, checksum, id(0,1)
 
183
        
189
184
    def __str__(self):
190
185
        if self.epoch == '0':
191
 
            val = '%s - %s-%s.%s' % (self.name, self.version, self.release, 
192
 
                                        self.arch)
193
 
        else:
194
 
            val = '%s - %s:%s-%s.%s' % (self.name, self.epoch, self.version,
195
 
                                           self.release, self.arch)
196
 
        return val
197
 
 
198
 
    def tagByName(self, tag):
199
 
        data = self.hdr[tag]
200
 
        return data
201
 
    
202
 
    def doepoch(self):
203
 
        tmpepoch = self.hdr['epoch']
204
 
        if tmpepoch is None:
205
 
            epoch = '0'
206
 
        else:
207
 
            epoch = str(tmpepoch)
208
 
        
209
 
        return epoch
210
 
    
211
 
    def returnSimple(self, thing):
212
 
        if hasattr(self, thing):
213
 
            return getattr(self, thing)
214
 
        else:
215
 
            return self.tagByName(thing)
216
 
 
217
 
    def returnLocalHeader(self):
218
 
        return self.hdr
219
 
 
 
186
            out = '%s-%s-%s.%s' % (self.name, 
 
187
                                   self.version,
 
188
                                   self.release, 
 
189
                                   self.arch)
 
190
        else:
 
191
            out = '%s:%s-%s-%s.%s' % (self.epoch,
 
192
                                      self.name,  
 
193
                                      self.version, 
 
194
                                      self.release, 
 
195
                                      self.arch)
 
196
        return out
 
197
 
 
198
    def __cmp__(self, other):
 
199
        """ Compare packages. """
 
200
        if not other:
 
201
            return 1
 
202
        ret = cmp(self.name, other.name)
 
203
        if ret == 0:
 
204
            ret = comparePoEVR(self, other)
 
205
        if ret == 0:
 
206
            ret = cmp(self.arch, other.arch)
 
207
        return ret
 
208
 
 
209
    def __repr__(self):
 
210
        return "<%s : %s (%s)>" % (self.__class__.__name__, str(self),hex(id(self))) 
 
211
 
 
212
    def returnSimple(self, varname):
 
213
        warnings.warn("returnSimple() will go away in a future version of Yum.\n",
 
214
                      Errors.YumFutureDeprecationWarning, stacklevel=2)
 
215
        return getattr(self, varname)
 
216
 
 
217
    def returnChecksums(self):
 
218
        return self._checksums
 
219
 
 
220
    checksums = property(fget=lambda self: self.returnChecksums())
 
221
    
 
222
    def returnIdSum(self):
 
223
        for (csumtype, csum, csumid) in self.checksums:
 
224
            if csumid:
 
225
                return (csumtype, csum)
 
226
 
 
227
class RpmBase(object):
 
228
    """return functions and storage for rpm-specific data"""
 
229
 
 
230
    def __init__(self):
 
231
        self.prco = {}
 
232
        self.prco['obsoletes'] = [] # (name, flag, (e,v,r))
 
233
        self.prco['conflicts'] = [] # (name, flag, (e,v,r))
 
234
        self.prco['requires'] = [] # (name, flag, (e,v,r))
 
235
        self.prco['provides'] = [] # (name, flag, (e,v,r))
 
236
        self.files = {}
 
237
        self.files['file'] = []
 
238
        self.files['dir'] = []
 
239
        self.files['ghost'] = []
 
240
        self._changelog = [] # (ctime, cname, ctext)
 
241
        self.licenses = []
 
242
        self._hash = None
 
243
 
 
244
    #  Do we still need __eq__ and __ne__ given that
 
245
    # PackageObject has a working __cmp__?
 
246
    def __eq__(self, other):
 
247
        if not other: # check if other not is a package object. 
 
248
            return False
 
249
        if comparePoEVR(self, other) == 0 and self.arch == other.arch and self.name == other.name:
 
250
            return True
 
251
        return False
 
252
 
 
253
    def __ne__(self, other):
 
254
        if not other:
 
255
            return True
 
256
        if comparePoEVR(self, other) != 0 or self.arch != other.arch or self.name != other.name:
 
257
            return True
 
258
        return False
 
259
       
 
260
    def returnEVR(self):
 
261
        return PackageEVR(self.epoch, self.version, self.release)
 
262
    
 
263
    def __hash__(self):
 
264
        if self._hash is None:
 
265
            mystr = '%s - %s:%s-%s-%s.%s' % (self.repo.id, self.epoch, self.name,
 
266
                                         self.version, self.release, self.arch)
 
267
            self._hash = hash(mystr)
 
268
        return self._hash
 
269
        
 
270
    def returnPrco(self, prcotype, printable=False):
 
271
        """return list of provides, requires, conflicts or obsoletes"""
 
272
        
 
273
        prcos = []
 
274
        if self.prco.has_key(prcotype):
 
275
            prcos = self.prco[prcotype]
 
276
 
 
277
        if printable:
 
278
            results = []
 
279
            for prco in prcos:
 
280
                results.append(misc.prco_tuple_to_string(prco))
 
281
            return results
 
282
 
 
283
        return prcos
 
284
 
 
285
    def checkPrco(self, prcotype, prcotuple):
 
286
        """returns 1 or 0 if the pkg contains the requested tuple/tuple range"""
 
287
        # get rid of simple cases - nothing
 
288
        if not self.prco.has_key(prcotype):
 
289
            return 0
 
290
        # exact match    
 
291
        if prcotuple in self.prco[prcotype]:
 
292
            return 1
 
293
        else:
 
294
            # make us look it up and compare
 
295
            (reqn, reqf, (reqe, reqv ,reqr)) = prcotuple
 
296
            if reqf is not None:
 
297
                return self.inPrcoRange(prcotype, prcotuple)
 
298
            else:
 
299
                for (n, f, (e, v, r)) in self.returnPrco(prcotype):
 
300
                    if reqn == n:
 
301
                        return 1
 
302
 
 
303
        return 0
 
304
 
 
305
    def inPrcoRange(self, prcotype, reqtuple):
 
306
        """returns true if the package has a the prco that satisfies 
 
307
           the reqtuple range, assume false.
 
308
           Takes: prcotype, requested prco tuple"""
 
309
        return bool(self.matchingPrcos(prcotype, reqtuple))
 
310
 
 
311
    def matchingPrcos(self, prcotype, reqtuple):
 
312
        # we only ever get here if we have a versioned prco
 
313
        # nameonly shouldn't ever raise it
 
314
        (reqn, reqf, (reqe, reqv, reqr)) = reqtuple
 
315
        # however, just in case
 
316
        # find the named entry in pkgobj, do the comparsion
 
317
        result = []
 
318
        for (n, f, (e, v, r)) in self.returnPrco(prcotype):
 
319
            if reqn != n:
 
320
                continue
 
321
 
 
322
            if f == '=':
 
323
                f = 'EQ'
 
324
            if f != 'EQ' and prcotype == 'provides':
 
325
                # isn't this odd, it's not 'EQ' and it is a provides
 
326
                # - it really should be EQ
 
327
                # use the pkgobj's evr for the comparison
 
328
                if e is None:
 
329
                    e = self.epoch
 
330
                if v is None:
 
331
                    v = self.ver
 
332
                if r is None:
 
333
                    r = self.rel
 
334
                #(e, v, r) = (self.epoch, self.ver, self.rel)
 
335
 
 
336
            matched = rpmUtils.miscutils.rangeCompare(
 
337
                reqtuple, (n, f, (e, v, r)))
 
338
            if matched:
 
339
                result.append((n, f, (e, v, r)))
 
340
 
 
341
        return result
 
342
 
 
343
 
 
344
        
 
345
    def returnChangelog(self):
 
346
        """return changelog entries"""
 
347
        return self._changelog
 
348
        
 
349
    def returnFileEntries(self, ftype='file'):
 
350
        """return list of files based on type"""
 
351
        # fixme - maybe should die - use direct access to attribute
 
352
        if self.files:
 
353
            if self.files.has_key(ftype):
 
354
                return self.files[ftype]
 
355
        return []
 
356
            
 
357
    def returnFileTypes(self):
 
358
        """return list of types of files in the package"""
 
359
        # maybe should die - use direct access to attribute
 
360
        return self.files.keys()
 
361
 
 
362
    def returnPrcoNames(self, prcotype):
 
363
        results = []
 
364
        lists = self.returnPrco(prcotype)
 
365
        for (name, flag, vertup) in lists:
 
366
            results.append(name)
 
367
        return results
220
368
 
221
369
    def getProvidesNames(self):
222
 
        """returns a list of providesNames"""
223
 
        
224
 
        provnames = self.tagByName('providename')
225
 
        if type(provnames) is not types.ListType():
226
 
            if type(provnames) is types.StringType():
227
 
                provnames = [provnames]
228
 
            else:
229
 
                provnames = []
230
 
 
231
 
        return provnames
232
 
 
233
 
    def requiresList(self):
234
 
        """return a list of all of the strings of the package requirements"""
235
 
        reqlist = []
236
 
        names = self.hdr[rpm.RPMTAG_REQUIRENAME]
237
 
        flags = self.hdr[rpm.RPMTAG_REQUIREFLAGS]
238
 
        ver = self.hdr[rpm.RPMTAG_REQUIREVERSION]
239
 
        if names is not None:
240
 
            tmplst = zip(names, flags, ver)
241
 
        
242
 
        for (n, f, v) in tmplst:
243
 
            req = rpmUtils.miscutils.formatRequire(n, v, f)
244
 
            reqlist.append(req)
245
 
        
246
 
        return reqlist
247
 
 
248
 
    def _pkgtup(self):
249
 
        return (self.name, self.arch, self.epoch, self.version, self.release)
250
 
    
251
 
    def size(self):
252
 
        return self.tagByName('size')
253
 
 
254
 
    def printVer(self):
255
 
        """returns a printable version string - including epoch, if it's set"""
256
 
        if self.epoch != '0':
257
 
            ver = '%s:%s-%s' % (self.epoch, self.version, self.release)
258
 
        else:
259
 
            ver = '%s-%s' % (self.version, self.release)
260
 
        
261
 
        return ver
262
 
    
263
 
    def compactPrint(self):
264
 
        ver = self.printVer()
265
 
        return "%s.%s %s" % (self.name, self.arch, ver)
266
 
    
267
 
 
268
 
class YumLocalPackage(YumInstalledPackage):
269
 
    """Class to handle an arbitrary package from a file path
270
 
       this inherits most things from YumInstalledPackage because
271
 
       installed packages and an arbitrary package on disk act very
272
 
       much alike. init takes a ts instance and a filename/path 
273
 
       to the package."""
274
 
 
275
 
    def __init__(self, ts=None, filename=None):
276
 
        if ts is None:
277
 
            raise Errors.MiscError, \
278
 
                 'No Transaction Set Instance for YumLocalPackage instance creation'
279
 
        if filename is None:
280
 
            raise Errors.MiscError, \
281
 
                 'No Filename specified for YumLocalPackage instance creation'
282
 
                 
283
 
        self.pkgtype = 'local'
284
 
        self.localpath = filename
285
 
        self.repoid = filename
286
 
        try:
287
 
            self.hdr = rpmUtils.miscutils.hdrFromPackage(ts, self.localpath)
288
 
        except rpmUtils.RpmUtilsError, e:
289
 
            raise Errors.MiscError, \
290
 
                'Could not open local rpm file: %s' % self.localpath
291
 
        self.name = self.tagByName('name')
292
 
        self.arch = self.tagByName('arch')
293
 
        self.epoch = self.doepoch()
294
 
        self.version = self.tagByName('version')
295
 
        self.release = self.tagByName('release')
296
 
        self.summary = self.tagByName('summary')
297
 
        self.description = self.tagByName('description')
298
 
        self.pkgtup = self._pkgtup()
299
 
        
300
 
    
301
 
    def _pkgtup(self):
302
 
        return (self.name, self.arch, self.epoch, self.version, self.release)
303
 
    
304
 
    def localPkg(self):
305
 
        return self.localpath
306
 
    
307
 
        
308
 
 
309
 
 
310
 
class YumAvailablePackage(repomd.packageObject.PackageObject, repomd.packageObject.RpmBase):
311
 
    """derived class for the repomd packageobject and RpmBase packageobject yum
 
370
        warnings.warn('getProvidesNames() will go away in a future version of Yum.\n',
 
371
                      Errors.YumDeprecationWarning, stacklevel=2)
 
372
        return self.provides_names
 
373
 
 
374
    def simpleFiles(self, ftype='files'):
 
375
        if self.files and self.files.has_key(ftype):
 
376
            return self.files[ftype]
 
377
        return []
 
378
    
 
379
    filelist = property(fget=lambda self: self.returnFileEntries(ftype='file'))
 
380
    dirlist = property(fget=lambda self: self.returnFileEntries(ftype='dir'))
 
381
    ghostlist = property(fget=lambda self: self.returnFileEntries(ftype='ghost'))
 
382
    requires = property(fget=lambda self: self.returnPrco('requires'))
 
383
    provides = property(fget=lambda self: self.returnPrco('provides'))
 
384
    obsoletes = property(fget=lambda self: self.returnPrco('obsoletes'))
 
385
    conflicts = property(fget=lambda self: self.returnPrco('conflicts'))
 
386
    provides_names = property(fget=lambda self: self.returnPrcoNames('provides'))
 
387
    requires_names = property(fget=lambda self: self.returnPrcoNames('requires'))
 
388
    conflicts_names = property(fget=lambda self: self.returnPrcoNames('conflicts'))
 
389
    obsoletes_names = property(fget=lambda self: self.returnPrcoNames('obsoletes'))
 
390
    provides_print = property(fget=lambda self: self.returnPrco('provides', True))
 
391
    requires_print = property(fget=lambda self: self.returnPrco('requires', True))
 
392
    conflicts_print = property(fget=lambda self: self.returnPrco('conflicts', True))
 
393
    obsoletes_print = property(fget=lambda self: self.returnPrco('obsoletes', True))
 
394
    changelog = property(fget=lambda self: self.returnChangelog())
 
395
    EVR = property(fget=lambda self: self.returnEVR())
 
396
    
 
397
class PackageEVR:
 
398
 
 
399
    """
 
400
    A comparable epoch, version, and release representation.
 
401
    """
 
402
    
 
403
    def __init__(self,e,v,r):
 
404
        self.epoch = e
 
405
        self.ver = v
 
406
        self.rel = r
 
407
        
 
408
    def compare(self,other):
 
409
        return rpmUtils.miscutils.compareEVR((self.epoch, self.ver, self.rel), (other.epoch, other.ver, other.rel))
 
410
    
 
411
    def __lt__(self, other):
 
412
        if self.compare(other) < 0:
 
413
            return True
 
414
        return False
 
415
 
 
416
        
 
417
    def __gt__(self, other):
 
418
        if self.compare(other) > 0:
 
419
            return True
 
420
        return False
 
421
 
 
422
    def __le__(self, other):
 
423
        if self.compare(other) <= 0:
 
424
            return True
 
425
        return False
 
426
 
 
427
    def __ge__(self, other):
 
428
        if self.compare(other) >= 0:
 
429
            return True
 
430
        return False
 
431
 
 
432
    def __eq__(self, other):
 
433
        if self.compare(other) == 0:
 
434
            return True
 
435
        return False
 
436
 
 
437
    def __ne__(self, other):
 
438
        if self.compare(other) != 0:
 
439
            return True
 
440
        return False
 
441
    
 
442
 
 
443
 
 
444
class YumAvailablePackage(PackageObject, RpmBase):
 
445
    """derived class for the  packageobject and RpmBase packageobject yum
312
446
       uses this for dealing with packages in a repository"""
313
447
 
314
 
    def __init__(self, pkgdict, repoid):
315
 
        repomd.packageObject.PackageObject.__init__(self)
316
 
        repomd.packageObject.RpmBase.__init__(self)
317
 
        
318
 
        self.importFromDict(pkgdict, repoid)
319
 
        # quick, common definitions
320
 
        self.name = self.returnSimple('name')
321
 
        self.epoch = self.returnSimple('epoch')
322
 
        self.version = self.returnSimple('version')
323
 
        self.release = self.returnSimple('release')
324
 
        self.arch = self.returnSimple('arch')
325
 
        self.repoid = self.returnSimple('repoid')
326
 
        self.pkgtup = self._pkgtup()
327
 
 
328
 
    def size(self):
329
 
        return self.returnSimple('packagesize')
330
 
 
331
 
    def _pkgtup(self):
332
 
        return self.returnPackageTuple()
333
 
 
 
448
    def __init__(self, repo, pkgdict = None):
 
449
        PackageObject.__init__(self)
 
450
        RpmBase.__init__(self)
 
451
        
 
452
        self.repoid = repo.id
 
453
        self.repo = repo
 
454
        self.state = None
 
455
        self._loadedfiles = False
 
456
 
 
457
        if pkgdict != None:
 
458
            self.importFromDict(pkgdict)
 
459
            self.ver = self.version
 
460
            self.rel = self.release
 
461
        self.pkgtup = (self.name, self.arch, self.epoch, self.version, self.release)
 
462
 
 
463
    def exclude(self):
 
464
        """remove self from package sack"""
 
465
        self.repo.sack.delPackage(self)
 
466
        
334
467
    def printVer(self):
335
468
        """returns a printable version string - including epoch, if it's set"""
336
469
        if self.epoch != '0':
344
477
        ver = self.printVer()
345
478
        return "%s.%s %s" % (self.name, self.arch, ver)
346
479
 
 
480
    def _size(self):
 
481
        return self.packagesize
 
482
    
 
483
    def _remote_path(self):
 
484
        return self.relativepath
 
485
 
 
486
    def _remote_url(self):
 
487
        """returns a URL that can be used for downloading the package.
 
488
        Note that if you're going to download the package in your tool,
 
489
        you should use self.repo.getPackage."""
 
490
        base = self.basepath
 
491
        if base:
 
492
            return urlparse.urljoin(base, self.remote_path)
 
493
        return urlparse.urljoin(self.repo.urls[0], self.remote_path)
 
494
    
 
495
    size = property(_size)
 
496
    remote_path = property(_remote_path)
 
497
    remote_url = property(_remote_url)
 
498
 
 
499
    def _committer(self):
 
500
        "Returns the name of the last person to do a commit to the changelog."
 
501
 
 
502
        if hasattr(self, '_committer_ret'):
 
503
            return self._committer_ret
 
504
 
 
505
        def _nf2ascii(x):
 
506
            """ does .encode("ascii", "replace") but it never fails. """
 
507
            ret = []
 
508
            for val in x:
 
509
                if ord(val) >= 128:
 
510
                    val = '?'
 
511
                ret.append(val)
 
512
            return "".join(ret)
 
513
 
 
514
        if not len(self.changelog): # Empty changelog is _possible_ I guess
 
515
            self._committer_ret = self.packager
 
516
            return self._committer_ret
 
517
        val = self.changelog[0][1]
 
518
        # Chagnelog data is in multiple locale's, so we convert to ascii
 
519
        # ignoring "bad" chars.
 
520
        val = _nf2ascii(val)
 
521
        # Hacky way to get rid of version numbers...
 
522
        self._committer_ret = re.sub("""> .*""", '>', val)
 
523
        return self._committer_ret
 
524
 
 
525
    committer  = property(_committer)
 
526
    
 
527
    def _committime(self):
 
528
        "Returns the time of the last commit to the changelog."
 
529
 
 
530
        if hasattr(self, '_committime_ret'):
 
531
            return self._committime_ret
 
532
 
 
533
        if not len(self.changelog): # Empty changelog is _possible_ I guess
 
534
            self._committime_ret = self.buildtime
 
535
            return self._committime_ret
 
536
        
 
537
        self._committime_ret = self.changelog[0][0]
 
538
        return self._committime_ret
 
539
 
 
540
    committime = property(_committime)
 
541
 
 
542
    def getDiscNum(self):
 
543
        if self.basepath is None:
 
544
            return None
 
545
        (scheme, netloc, path, query, fragid) = urlparse.urlsplit(self.basepath)
 
546
        if scheme == "media":
 
547
            if len(fragid) == 0:
 
548
                return 0
 
549
            return int(fragid)
 
550
        return None
 
551
    
 
552
    def returnHeaderFromPackage(self):
 
553
        rpmfile = self.localPkg()
 
554
        ts = rpmUtils.transaction.initReadOnlyTransaction()
 
555
        hdr = rpmUtils.miscutils.hdrFromPackage(ts, rpmfile)
 
556
        return hdr
 
557
        
347
558
    def returnLocalHeader(self):
348
559
        """returns an rpm header object from the package object's local
349
560
           header cache"""
359
570
 
360
571
        return hdr
361
572
 
362
 
    def getProvidesNames(self):
363
 
        """returns a list of providesNames"""
364
 
        
365
 
        provnames = []
366
 
        prov = self.returnPrco('provides')
367
 
        
368
 
        for (name, flag, vertup) in prov:
369
 
            provnames.append(name)
370
 
 
371
 
        return provnames
372
573
       
373
574
    def localPkg(self):
374
575
        """return path to local package (whether it is present there, or not)"""
375
576
        if not hasattr(self, 'localpath'):
376
 
            repo = base.repos.getRepo(self.repoid)
377
 
            remote = self.returnSimple('relativepath')
378
 
            rpmfn = os.path.basename(remote)
379
 
            self.localpath = repo.pkgdir + '/' + rpmfn
 
577
            rpmfn = os.path.basename(self.remote_path)
 
578
            self.localpath = self.repo.pkgdir + '/' + rpmfn
380
579
        return self.localpath
381
580
 
382
581
    def localHdr(self):
384
583
           byte ranges"""
385
584
           
386
585
        if not hasattr(self, 'hdrpath'):
387
 
            repo = base.repos.getRepo(self.repoid)
388
 
            pkgpath = self.returnSimple('relativepath')
389
 
            pkgname = os.path.basename(pkgpath)
 
586
            pkgname = os.path.basename(self.remote_path)
390
587
            hdrname = pkgname[:-4] + '.hdr'
391
 
            self.hdrpath = repo.hdrdir + '/' + hdrname
 
588
            self.hdrpath = self.repo.hdrdir + '/' + hdrname
392
589
 
393
590
        return self.hdrpath
394
591
    
 
592
    def verifyLocalPkg(self):
 
593
        """check the package checksum vs the localPkg
 
594
           return True if pkg is good, False if not"""
 
595
           
 
596
        (csum_type, csum) = self.returnIdSum()
 
597
        
 
598
        try:
 
599
            filesum = misc.checksum(csum_type, self.localPkg())
 
600
        except Errors.MiscError:
 
601
            return False
 
602
        
 
603
        if filesum != csum:
 
604
            return False
 
605
        
 
606
        return True
 
607
        
395
608
    def prcoPrintable(self, prcoTuple):
396
609
        """convert the prco tuples into a nicer human string"""
397
 
        (name, flag, (e, v, r)) = prcoTuple
398
 
        flags = {'GT':'>', 'GE':'>=', 'EQ':'=', 'LT':'<', 'LE':'<='}
399
 
        if flag is None:
400
 
            return name
401
 
        
402
 
        base = '%s %s ' % (name, flags[flag])
403
 
        if e not in [0, '0', None]:
404
 
            base += '%s:' % e
405
 
        if v is not None:
406
 
            base += '%s' % v
407
 
        if r is not None:
408
 
            base += '-%s' % r
409
 
        
410
 
        return base
411
 
    
 
610
        warnings.warn('prcoPrintable() will go away in a future version of Yum.\n',
 
611
                      Errors.YumDeprecationWarning, stacklevel=2)
 
612
        return misc.prco_tuple_to_string(prcoTuple)
 
613
 
412
614
    def requiresList(self):
413
615
        """return a list of requires in normal rpm format"""
414
 
        
415
 
        reqlist = []
416
 
        
417
 
        for prcoTuple in self.returnPrco('requires'):
418
 
            prcostr = self.prcoPrintable(prcoTuple)
419
 
            reqlist.append(prcostr)
420
 
        
421
 
        return reqlist
422
 
        
423
 
    def importFromDict(self, pkgdict, repoid):
 
616
        return self.requires_print
 
617
 
 
618
    def importFromDict(self, pkgdict):
424
619
        """handles an mdCache package dictionary item to populate out 
425
620
           the package information"""
426
621
        
427
 
        self.simple['repoid'] = repoid
428
622
        # translates from the pkgdict, populating out the information for the
429
623
        # packageObject
430
624
        
431
625
        if hasattr(pkgdict, 'nevra'):
432
626
            (n, e, v, r, a) = pkgdict.nevra
433
 
            self.simple['name'] = n
434
 
            self.simple['epoch'] = e
435
 
            self.simple['version'] = v
436
 
            self.simple['arch'] = a
437
 
            self.simple['release'] = r
 
627
            self.name = n
 
628
            self.epoch = e
 
629
            self.version = v
 
630
            self.arch = a
 
631
            self.release = r
438
632
        
439
633
        if hasattr(pkgdict, 'time'):
440
 
            self.simple['buildtime'] = pkgdict.time['build']
441
 
            self.simple['filetime'] = pkgdict.time['file']
 
634
            self.buildtime = pkgdict.time['build']
 
635
            self.filetime = pkgdict.time['file']
442
636
        
443
637
        if hasattr(pkgdict, 'size'):
444
 
            self.simple['packagesize'] = pkgdict.size['package']
445
 
            self.simple['archivesize'] = pkgdict.size['archive']
446
 
            self.simple['installedsize'] = pkgdict.size['installed']
 
638
            self.packagesize = pkgdict.size['package']
 
639
            self.archivesize = pkgdict.size['archive']
 
640
            self.installedsize = pkgdict.size['installed']
447
641
        
448
642
        if hasattr(pkgdict, 'location'):
449
 
            if pkgdict.location['value'] == '':
 
643
            if not pkgdict.location.has_key('base'):
 
644
                url = None
 
645
            elif pkgdict.location['base'] == '':
450
646
                url = None
451
647
            else:
452
 
                url = pkgdict.location['value']
453
 
            
454
 
            self.simple['basepath'] = url
455
 
            self.simple['relativepath'] = pkgdict.location['href']
456
 
        
 
648
                url = pkgdict.location['base']
 
649
 
 
650
            self.basepath = url
 
651
            self.relativepath = pkgdict.location['href']
 
652
 
457
653
        if hasattr(pkgdict, 'hdrange'):
458
 
            self.simple['hdrstart'] = pkgdict.hdrange['start']
459
 
            self.simple['hdrend'] = pkgdict.hdrange['end']
 
654
            self.hdrstart = pkgdict.hdrange['start']
 
655
            self.hdrend = pkgdict.hdrange['end']
460
656
        
461
657
        if hasattr(pkgdict, 'info'):
462
 
            infodict = pkgdict.info
463
658
            for item in ['summary', 'description', 'packager', 'group',
464
659
                         'buildhost', 'sourcerpm', 'url', 'vendor']:
465
 
                self.simple[item] = infodict[item]
 
660
                setattr(self, item, pkgdict.info[item])
 
661
            self.summary = self.summary.replace('\n', '')
466
662
            
467
 
            self.licenses.append(infodict['license'])
 
663
            self.licenses.append(pkgdict.info['license'])
468
664
        
469
665
        if hasattr(pkgdict, 'files'):
470
 
            for file in pkgdict.files.keys():
471
 
                ftype = pkgdict.files[file]
 
666
            for fn in pkgdict.files:
 
667
                ftype = pkgdict.files[fn]
472
668
                if not self.files.has_key(ftype):
473
669
                    self.files[ftype] = []
474
 
                self.files[ftype].append(file)
 
670
                self.files[ftype].append(fn)
475
671
        
476
672
        if hasattr(pkgdict, 'prco'):
477
 
            for rtype in pkgdict.prco.keys():
 
673
            for rtype in pkgdict.prco:
478
674
                for rdict in pkgdict.prco[rtype]:
479
675
                    name = rdict['name']
480
676
                    f = e = v = r  = None
490
686
                if cdict.has_key('date'): date = cdict['date']
491
687
                if cdict.has_key('value'): text = cdict['value']
492
688
                if cdict.has_key('author'): author = cdict['author']
493
 
                self.changelog.append((date, author, text))
 
689
                self._changelog.append((date, author, text))
494
690
        
495
691
        if hasattr(pkgdict, 'checksum'):
496
692
            ctype = pkgdict.checksum['type']
502
698
                csumid = 1
503
699
            else:
504
700
                csumid = 0
505
 
            self.checksums.append((ctype, csum, csumid))
506
 
            
507
 
            
508
 
        
509
 
    
510
 
    
 
701
            self._checksums.append((ctype, csum, csumid))
 
702
 
 
703
 
 
704
class YumHeaderPackage(YumAvailablePackage):
 
705
    """Package object built from an rpm header"""
 
706
    def __init__(self, repo, hdr):
 
707
        """hand in an rpm header, we'll assume it's installed and query from there"""
 
708
       
 
709
        YumAvailablePackage.__init__(self, repo)
 
710
 
 
711
        self.hdr = hdr
 
712
        self.name = self.hdr['name']
 
713
        self.arch = self.hdr['arch']
 
714
        self.epoch = self.doepoch()
 
715
        self.version = self.hdr['version']
 
716
        self.release = self.hdr['release']
 
717
        self.ver = self.version
 
718
        self.rel = self.release
 
719
        self.pkgtup = (self.name, self.arch, self.epoch, self.version, self.release)
 
720
        self.summary = self.hdr['summary'].replace('\n', '')
 
721
        self.description = self.hdr['description']
 
722
        self.pkgid = self.hdr[rpm.RPMTAG_SHA1HEADER]
 
723
        if not self.pkgid:
 
724
            self.pkgid = "%s.%s" %(self.hdr['name'], self.hdr['buildtime'])
 
725
        self.packagesize = self.hdr['size']
 
726
        self.__mode_cache = {}
 
727
        self.__prcoPopulated = False
 
728
        
 
729
    def __str__(self):
 
730
        if self.epoch == '0':
 
731
            val = '%s-%s-%s.%s' % (self.name, self.version, self.release,
 
732
                                        self.arch)
 
733
        else:
 
734
            val = '%s:%s-%s-%s.%s' % (self.epoch,self.name, self.version,
 
735
                                           self.release, self.arch)
 
736
        return val
 
737
 
 
738
    def returnPrco(self, prcotype, printable=False):
 
739
        if not self.__prcoPopulated:
 
740
            self._populatePrco()
 
741
            self.__prcoPopulated = True
 
742
        return YumAvailablePackage.returnPrco(self, prcotype, printable)
 
743
 
 
744
    def _populatePrco(self):
 
745
        "Populate the package object with the needed PRCO interface."
 
746
 
 
747
        tag2prco = { "OBSOLETE": "obsoletes",
 
748
                     "CONFLICT": "conflicts",
 
749
                     "REQUIRE": "requires",
 
750
                     "PROVIDE": "provides" }
 
751
        for tag in tag2prco:
 
752
            name = self.hdr[getattr(rpm, 'RPMTAG_%sNAME' % tag)]
 
753
 
 
754
            lst = self.hdr[getattr(rpm, 'RPMTAG_%sFLAGS' % tag)]
 
755
            flag = map(rpmUtils.miscutils.flagToString, lst)
 
756
 
 
757
            lst = self.hdr[getattr(rpm, 'RPMTAG_%sVERSION' % tag)]
 
758
            vers = map(rpmUtils.miscutils.stringToVersion, lst)
 
759
 
 
760
            prcotype = tag2prco[tag]
 
761
            if name is not None:
 
762
                self.prco[prcotype] = zip(name, flag, vers)
 
763
    
 
764
    def tagByName(self, tag):
 
765
        warnings.warn("tagByName() will go away in a furture version of Yum.\n",
 
766
                      Errors.YumFutureDeprecationWarning, stacklevel=2)
 
767
        try:
 
768
            return getattr(self, tag)
 
769
        except AttributeError:
 
770
            raise Errors.MiscError, "Unknown header tag %s" % tag
 
771
 
 
772
    def __getattr__(self, thing):
 
773
        return self.hdr[thing]
 
774
 
 
775
    def doepoch(self):
 
776
        tmpepoch = self.hdr['epoch']
 
777
        if tmpepoch is None:
 
778
            epoch = '0'
 
779
        else:
 
780
            epoch = str(tmpepoch)
 
781
        
 
782
        return epoch
 
783
 
 
784
    def returnLocalHeader(self):
 
785
        return self.hdr
 
786
    
 
787
 
 
788
    def _loadFiles(self):
 
789
        files = self.hdr['filenames']
 
790
        fileflags = self.hdr['fileflags']
 
791
        filemodes = self.hdr['filemodes']
 
792
        filetuple = zip(files, filemodes, fileflags)
 
793
        if not self._loadedfiles:
 
794
            for (fn, mode, flag) in filetuple:
 
795
                #garbage checks
 
796
                if mode is None or mode == '':
 
797
                    if not self.files.has_key('file'):
 
798
                        self.files['file'] = []
 
799
                    self.files['file'].append(fn)
 
800
                    continue
 
801
                if not self.__mode_cache.has_key(mode):
 
802
                    self.__mode_cache[mode] = stat.S_ISDIR(mode)
 
803
          
 
804
                if self.__mode_cache[mode]:
 
805
                    if not self.files.has_key('dir'):
 
806
                        self.files['dir'] = []
 
807
                    self.files['dir'].append(fn)
 
808
                else:
 
809
                    if flag is None:
 
810
                        if not self.files.has_key('file'):
 
811
                            self.files['file'] = []
 
812
                        self.files['file'].append(fn)
 
813
                    else:
 
814
                        if (flag & 64):
 
815
                            if not self.files.has_key('ghost'):
 
816
                                self.files['ghost'] = []
 
817
                            self.files['ghost'].append(fn)
 
818
                            continue
 
819
                        if not self.files.has_key('file'):
 
820
                            self.files['file'] = []
 
821
                        self.files['file'].append(fn)
 
822
            self._loadedfiles = True
 
823
            
 
824
    def returnFileEntries(self, ftype='file'):
 
825
        """return list of files based on type"""
 
826
        self._loadFiles()
 
827
        return YumAvailablePackage.returnFileEntries(self,ftype)
 
828
    
 
829
    def returnChangelog(self):
 
830
        # note - if we think it is worth keeping changelogs in memory
 
831
        # then create a _loadChangelog() method to put them into the 
 
832
        # self._changelog attr
 
833
        if len(self.hdr['changelogname']) > 0:
 
834
            return zip(self.hdr['changelogtime'],
 
835
                       self.hdr['changelogname'],
 
836
                       self.hdr['changelogtext'])
 
837
        return []
 
838
 
 
839
class _CountedReadFile:
 
840
    """ Has just a read() method, and keeps a count so we can find out how much
 
841
        has been read. Implemented so we can get the real size of the file from
 
842
        prelink. """
 
843
    
 
844
    def __init__(self, fp):
 
845
        self.fp = fp
 
846
        self.read_size = 0
 
847
 
 
848
    def read(self, size):
 
849
        ret = self.fp.read(size)
 
850
        self.read_size += len(ret)
 
851
        return ret
 
852
 
 
853
class _PkgVerifyProb:
 
854
    """ Holder for each "problem" we find with a pkg.verify(). """
 
855
    
 
856
    def __init__(self, type, msg, ftypes, fake=False):
 
857
        self.type           = type
 
858
        self.message        = msg
 
859
        self.database_value = None
 
860
        self.disk_value     = None
 
861
        self.file_types     = ftypes
 
862
        self.fake           = fake
 
863
 
 
864
    def __cmp__(self, other):
 
865
        if other is None:
 
866
            return 1
 
867
        type2sort = {'type' :  1, 'symlink' : 2, 'checksum' : 3, 'size'    :  4,
 
868
                     'user' :  4, 'group'   : 5, 'mode' : 6, 'genchecksum' :  7,
 
869
                     'mtime' : 8, 'missing' : 9, 'permissions-missing'     : 10,
 
870
                     'state' : 11, 'missingok' : 12, 'ghost' : 13}
 
871
        ret = cmp(type2sort[self.type], type2sort[other.type])
 
872
        if not ret:
 
873
            for attr in ['disk_value', 'database_value', 'file_types']:
 
874
                x = getattr(self,  attr)
 
875
                y = getattr(other, attr)
 
876
                if x is None:
 
877
                    assert y is None
 
878
                    continue
 
879
                ret = cmp(x, y)
 
880
                if ret:
 
881
                    break
 
882
        return ret
 
883
 
 
884
# From: lib/rpmvf.h ... not in rpm *sigh*
 
885
_RPMVERIFY_MD5      = (1 << 0)
 
886
_RPMVERIFY_FILESIZE = (1 << 1)
 
887
_RPMVERIFY_LINKTO   = (1 << 2)
 
888
_RPMVERIFY_USER     = (1 << 3)
 
889
_RPMVERIFY_GROUP    = (1 << 4)
 
890
_RPMVERIFY_MTIME    = (1 << 5)
 
891
_RPMVERIFY_MODE     = (1 << 6)
 
892
_RPMVERIFY_RDEV     = (1 << 7)
 
893
 
 
894
_installed_repo = FakeRepository('installed')
 
895
_installed_repo.cost = 0
 
896
class YumInstalledPackage(YumHeaderPackage):
 
897
    """super class for dealing with packages in the rpmdb"""
 
898
    def __init__(self, hdr):
 
899
        fakerepo = _installed_repo
 
900
        YumHeaderPackage.__init__(self, fakerepo, hdr)
 
901
        
 
902
    def verify(self, patterns=[], deps=False, script=False,
 
903
               fake_problems=True, all=False):
 
904
        """verify that the installed files match the packaged checksum
 
905
           optionally verify they match only if they are in the 'pattern' list
 
906
           returns a tuple """
 
907
        def _ftype(mode):
 
908
            """ Given a "mode" return the name of the type of file. """
 
909
            if stat.S_ISREG(mode):  return "file"
 
910
            if stat.S_ISDIR(mode):  return "directory"
 
911
            if stat.S_ISLNK(mode):  return "symlink"
 
912
            if stat.S_ISFIFO(mode): return "fifo"
 
913
            if stat.S_ISCHR(mode):  return "character device"
 
914
            if stat.S_ISBLK(mode):  return "block device"
 
915
            return "<unknown>"
 
916
 
 
917
        statemap = {rpm.RPMFILE_STATE_REPLACED : 'replaced',
 
918
                    rpm.RPMFILE_STATE_NOTINSTALLED : 'not installed',
 
919
                    rpm.RPMFILE_STATE_WRONGCOLOR : 'wrong color',
 
920
                    rpm.RPMFILE_STATE_NETSHARED : 'netshared'}
 
921
 
 
922
        fi = self.hdr.fiFromHeader()
 
923
        results = {} # fn = problem_obj?
 
924
 
 
925
        # Use prelink_undo_cmd macro?
 
926
        prelink_cmd = "/usr/sbin/prelink"
 
927
        have_prelink = os.path.exists(prelink_cmd)
 
928
 
 
929
        for filetuple in fi:
 
930
            #tuple is: (filename, fsize, mode, mtime, flags, frdev?, inode, link,
 
931
            #           state, vflags?, user, group, md5sum(or none for dirs) 
 
932
            (fn, size, mode, mtime, flags, dev, inode, link, state, vflags, 
 
933
                       user, group, csum) = filetuple
 
934
            if patterns:
 
935
                matched = False
 
936
                for p in patterns:
 
937
                    if fnmatch.fnmatch(fn, p):
 
938
                        matched = True
 
939
                        break
 
940
                if not matched: 
 
941
                    continue
 
942
 
 
943
            ftypes = []
 
944
            if flags & rpm.RPMFILE_CONFIG:
 
945
                ftypes.append('configuration')
 
946
            if flags & rpm.RPMFILE_DOC:
 
947
                ftypes.append('documentation')
 
948
            if flags & rpm.RPMFILE_GHOST:
 
949
                ftypes.append('ghost')
 
950
            if flags & rpm.RPMFILE_LICENSE:
 
951
                ftypes.append('license')
 
952
            if flags & rpm.RPMFILE_PUBKEY:
 
953
                ftypes.append('public key')
 
954
            if flags & rpm.RPMFILE_README:
 
955
                ftypes.append('README')
 
956
            if flags & rpm.RPMFILE_MISSINGOK:
 
957
                ftypes.append('missing ok')
 
958
            # not in python rpm bindings yet
 
959
            # elif flags & rpm.RPMFILE_POLICY:
 
960
            #    ftypes.append('policy')
 
961
                
 
962
            if all:
 
963
                vflags = -1
 
964
 
 
965
            if state != rpm.RPMFILE_STATE_NORMAL:
 
966
                if state in statemap:
 
967
                    ftypes.append("state=" + statemap[state])
 
968
                else:
 
969
                    ftypes.append("state=<unknown>")
 
970
                if fake_problems:
 
971
                    results[fn] = [_PkgVerifyProb('state',
 
972
                                                  'state is not normal',
 
973
                                                  ftypes, fake=True)]
 
974
                continue
 
975
 
 
976
            if flags & rpm.RPMFILE_MISSINGOK and fake_problems:
 
977
                results[fn] = [_PkgVerifyProb('missingok', 'missing but ok',
 
978
                                              ftypes, fake=True)]
 
979
            if flags & rpm.RPMFILE_MISSINGOK and not all:
 
980
                continue # rpm just skips missing ok, so we do too
 
981
 
 
982
            if flags & rpm.RPMFILE_GHOST and fake_problems:
 
983
                results[fn] = [_PkgVerifyProb('ghost', 'ghost file', ftypes,
 
984
                                              fake=True)]
 
985
            if flags & rpm.RPMFILE_GHOST and not all:
 
986
                continue
 
987
 
 
988
            # do check of file status on system
 
989
            problems = []
 
990
            if os.path.exists(fn):
 
991
                # stat
 
992
                my_st = os.lstat(fn)
 
993
                my_st_size = my_st.st_size
 
994
                my_user  = pwd.getpwuid(my_st[stat.ST_UID])[0]
 
995
                my_group = grp.getgrgid(my_st[stat.ST_GID])[0]
 
996
 
 
997
                if mode < 0:
 
998
                    # Stupid rpm, should be unsigned value but is signed ...
 
999
                    # so we "fix" it via. this hack
 
1000
                    mode = (mode & 0xFFFF)
 
1001
 
 
1002
                ftype    = _ftype(mode)
 
1003
                my_ftype = _ftype(my_st.st_mode)
 
1004
 
 
1005
                if vflags & _RPMVERIFY_RDEV and ftype != my_ftype:
 
1006
                    prob = _PkgVerifyProb('type', 'file type does not match',
 
1007
                                          ftypes)
 
1008
                    prob.database_value = ftype
 
1009
                    prob.disk_value = my_ftype
 
1010
                    problems.append(prob)
 
1011
 
 
1012
                if (ftype == "symlink" and my_ftype == "symlink" and
 
1013
                    vflags & _RPMVERIFY_LINKTO):
 
1014
                    fnl    = fi.FLink() # fi.foo is magic, don't think about it
 
1015
                    my_fnl = os.readlink(fn)
 
1016
                    if my_fnl != fnl:
 
1017
                        prob = _PkgVerifyProb('symlink',
 
1018
                                              'symlink does not match', ftypes)
 
1019
                        prob.database_value = fnl
 
1020
                        prob.disk_value     = my_fnl
 
1021
                        problems.append(prob)
 
1022
 
 
1023
                check_content = True
 
1024
                if 'ghost' in ftypes:
 
1025
                    check_content = False
 
1026
                if my_ftype == "symlink" and ftype == "file":
 
1027
                    # Don't let things hide behind symlinks
 
1028
                    my_st_size = os.stat(fn).st_size
 
1029
                elif my_ftype != "file":
 
1030
                    check_content = False
 
1031
                check_perms = True
 
1032
                if my_ftype == "symlink":
 
1033
                    check_perms = False
 
1034
 
 
1035
                if (check_content and vflags & _RPMVERIFY_MTIME and
 
1036
                    my_st.st_mtime != mtime):
 
1037
                    prob = _PkgVerifyProb('mtime', 'mtime does not match',
 
1038
                                          ftypes)
 
1039
                    prob.database_value = mtime
 
1040
                    prob.disk_value     = my_st.st_mtime
 
1041
                    problems.append(prob)
 
1042
 
 
1043
                if check_perms and vflags & _RPMVERIFY_USER and my_user != user:
 
1044
                    prob = _PkgVerifyProb('user', 'user does not match', ftypes)
 
1045
                    prob.database_value = user
 
1046
                    prob.disk_value = my_user
 
1047
                    problems.append(prob)
 
1048
                if (check_perms and vflags & _RPMVERIFY_GROUP and
 
1049
                    my_group != group):
 
1050
                    prob = _PkgVerifyProb('group', 'group does not match',
 
1051
                                          ftypes)
 
1052
                    prob.database_value = group
 
1053
                    prob.disk_value     = my_group
 
1054
                    problems.append(prob)
 
1055
 
 
1056
                my_mode = my_st.st_mode
 
1057
                if 'ghost' in ftypes: # This is what rpm does
 
1058
                    my_mode &= 0777
 
1059
                    mode    &= 0777
 
1060
                if check_perms and vflags & _RPMVERIFY_MODE and my_mode != mode:
 
1061
                    prob = _PkgVerifyProb('mode', 'mode does not match', ftypes)
 
1062
                    prob.database_value = mode
 
1063
                    prob.disk_value     = my_st.st_mode
 
1064
                    problems.append(prob)
 
1065
 
 
1066
                # Note that because we might get the _size_ from prelink,
 
1067
                # we need to do the checksum, even if we just throw it away,
 
1068
                # just so we get the size correct.
 
1069
                if (check_content and
 
1070
                    ((have_prelink and vflags & _RPMVERIFY_FILESIZE) or
 
1071
                     (csum and vflags & _RPMVERIFY_MD5))):
 
1072
                    try:
 
1073
                        my_csum = misc.checksum('md5', fn)
 
1074
                        gen_csum = True
 
1075
                    except Errors.MiscError:
 
1076
                        # Don't have permission?
 
1077
                        gen_csum = False
 
1078
 
 
1079
                    if csum and vflags & _RPMVERIFY_MD5 and not gen_csum:
 
1080
                        prob = _PkgVerifyProb('genchecksum',
 
1081
                                              'checksum not available', ftypes)
 
1082
                        prob.database_value = csum
 
1083
                        prob.disk_value     = None
 
1084
                        problems.append(prob)
 
1085
                        
 
1086
                    if gen_csum and my_csum != csum and have_prelink:
 
1087
                        #  This is how rpm -V works, try and if that fails try
 
1088
                        # again with prelink.
 
1089
                        (ig, fp,er) = os.popen3([prelink_cmd, "-y", fn])
 
1090
                        # er.read(1024 * 1024) # Try and get most of the stderr
 
1091
                        fp = _CountedReadFile(fp)
 
1092
                        my_csum = misc.checksum('md5', fp)
 
1093
                        my_st_size = fp.read_size
 
1094
 
 
1095
                    if (csum and vflags & _RPMVERIFY_MD5 and gen_csum and
 
1096
                        my_csum != csum):
 
1097
                        prob = _PkgVerifyProb('checksum',
 
1098
                                              'checksum does not match', ftypes)
 
1099
                        prob.database_value = csum
 
1100
                        prob.disk_value     = my_csum
 
1101
                        problems.append(prob)
 
1102
 
 
1103
                # Size might be got from prelink ... *sigh*.
 
1104
                if (check_content and vflags & _RPMVERIFY_FILESIZE and
 
1105
                    my_st_size != size):
 
1106
                    prob = _PkgVerifyProb('size', 'size does not match', ftypes)
 
1107
                    prob.database_value = size
 
1108
                    prob.disk_value     = my_st.st_size
 
1109
                    problems.append(prob)
 
1110
 
 
1111
            else:
 
1112
                try:
 
1113
                    os.stat(fn)
 
1114
                    perms_ok = True # Shouldn't happen
 
1115
                except OSError, e:
 
1116
                    perms_ok = True
 
1117
                    if e.errno == errno.EACCES:
 
1118
                        perms_ok = False
 
1119
 
 
1120
                if perms_ok:
 
1121
                    prob = _PkgVerifyProb('missing', 'file is missing', ftypes)
 
1122
                else:
 
1123
                    prob = _PkgVerifyProb('permissions-missing',
 
1124
                                          'file is missing (Permission denied)',
 
1125
                                          ftypes)
 
1126
                problems.append(prob)
 
1127
 
 
1128
            if problems:
 
1129
                results[fn] = problems
 
1130
                
 
1131
        return results
 
1132
        
 
1133
                             
 
1134
class YumLocalPackage(YumHeaderPackage):
 
1135
    """Class to handle an arbitrary package from a file path
 
1136
       this inherits most things from YumInstalledPackage because
 
1137
       installed packages and an arbitrary package on disk act very
 
1138
       much alike. init takes a ts instance and a filename/path 
 
1139
       to the package."""
 
1140
 
 
1141
    def __init__(self, ts=None, filename=None):
 
1142
        if ts is None:
 
1143
            raise Errors.MiscError, \
 
1144
                 'No Transaction Set Instance for YumLocalPackage instance creation'
 
1145
        if filename is None:
 
1146
            raise Errors.MiscError, \
 
1147
                 'No Filename specified for YumLocalPackage instance creation'
 
1148
                 
 
1149
        self.pkgtype = 'local'
 
1150
        self.localpath = filename
 
1151
        self._checksum = None
 
1152
 
 
1153
        
 
1154
        try:
 
1155
            hdr = rpmUtils.miscutils.hdrFromPackage(ts, self.localpath)
 
1156
        except RpmUtilsError:
 
1157
            raise Errors.MiscError, \
 
1158
                'Could not open local rpm file: %s' % self.localpath
 
1159
        
 
1160
        fakerepo = FakeRepository(filename)
 
1161
        fakerepo.cost = 0
 
1162
        YumHeaderPackage.__init__(self, fakerepo, hdr)
 
1163
        self.id = self.pkgid
 
1164
        self._stat = os.stat(self.localpath)
 
1165
        self.filetime = str(self._stat[-1])
 
1166
        self.packagesize = str(self._stat[6])
 
1167
        
 
1168
    def localPkg(self):
 
1169
        return self.localpath
 
1170
    
 
1171
    def _do_checksum(self, checksum_type='sha'):
 
1172
        if not self._checksum:
 
1173
            self._checksum = misc.checksum(checksum_type, self.localpath)
 
1174
            
 
1175
        return self._checksum    
 
1176
 
 
1177
    checksum = property(fget=lambda self: self._do_checksum())    
 
1178
    
 
1179