diff --git a/pisi/api.py b/pisi/api.py index aba70acf..2bc7c511 100644 --- a/pisi/api.py +++ b/pisi/api.py @@ -301,7 +301,7 @@ def index(dirs, output = 'pisi-index.xml', skip_sources=False): repo_dir = str(repo_dir) ctx.ui.info(_('* Building index of PISI files under %s') % repo_dir) index.index(repo_dir, skip_sources) - index.write(output, sha1sum=True, compress=File.xmill) + index.write(output, sha1sum=True, compress=File.xmill, sign=File.detached) ctx.ui.info(_('* Index file written')) def add_repo(name, indexuri): @@ -321,15 +321,22 @@ def update_repo(repo, force=False): ctx.ui.info(_('* Updating repository: %s') % repo) index = Index() if ctx.repodb.has_repo(repo): + repouri = ctx.repodb.get_repo(repo).indexuri.get_uri() try: - index.read_uri(ctx.repodb.get_repo(repo).indexuri.get_uri(), repo) - ctx.txn_proc(lambda txn : index.update_db(repo, txn=txn)) - ctx.ui.info(_('* Package database updated.')) + index.read_uri(repouri, repo) except pisi.file.AlreadyHaveException, e: ctx.ui.info(_('No updates available for repository %s.' % repo)) if force: ctx.ui.info(_('Updating database at any rate as requested')) - raise Error(_('OPTION NOT IMPLEMENTED YET')) + index.read_uri(repouri, repo, force = force) + else: + return + try: + index.check_signature(repouri, repo) + except pisi.file.NoSignatureFound, e: + ctx.ui.warning(e) + ctx.txn_proc(lambda txn : index.update_db(repo, txn=txn)) + ctx.ui.info(_('* Package database updated.')) else: raise Error(_('No repository named %s found.') % repo) diff --git a/pisi/cli/__init__.py b/pisi/cli/__init__.py index dabf83cc..021547d4 100644 --- a/pisi/cli/__init__.py +++ b/pisi/cli/__init__.py @@ -73,12 +73,14 @@ class CLI(UI): self.output( unicode(msg) + msgend, verbose=verbose) def warning(self, msg, verbose = False): + msg = unicode(msg) if ctx.get_option('no_color'): self.output(_('Warning: ') + msg + '\n', err=True, verbose=verbose) else: self.output(colorize(msg + '\n', 'purple'), err=True, verbose=verbose) def error(self, msg): + msg = unicode(msg) if ctx.get_option('no_color'): self.output(_('Error: ') + msg + '\n', err=True) else: @@ -86,6 +88,7 @@ class CLI(UI): def action(self, msg, verbose = False): #TODO: this seems quite redundant? + msg = unicode(msg) self.output(colorize(msg + '\n', 'green')) def choose(self, msg, opts): @@ -102,6 +105,7 @@ class CLI(UI): pass def confirm(self, msg): + msg = unicode(msg) if ctx.config.options and ctx.config.options.yes_all: return True while True: @@ -124,6 +128,7 @@ class CLI(UI): def status(self, msg = None): if msg: + msg = unicode(msg) self.output(colorize(msg + '\n', 'purple')) util.xterm_title(msg) diff --git a/pisi/file.py b/pisi/file.py index 0f600da1..0c67f13f 100644 --- a/pisi/file.py +++ b/pisi/file.py @@ -18,7 +18,7 @@ we are just encapsulating a common pattern in our program, nothing big. like all pisi classes, it has been programmed in a non-restricting way """ -import os.path +import os, os.path import types import gettext @@ -37,14 +37,26 @@ class AlreadyHaveException(pisi.Exception): self.url = url self.localfile = localfile +class NoSignatureFound(pisi.Exception): + def __init__(self, url): + pisi.Exception.__init__(self, "No signature found for %s" % url) + self.url = url + class Error(pisi.Error): pass +class InvalidSignature(pisi.Error): + def __init__(self, url): + pisi.Exception.__init__(self, " invalid for %s" % url) + self.url = url + class File: (read, write) = range(2) # modes (xmill, sevenzip) = range(2) # compress enums + (detached, whatelse) = range(2) + @staticmethod def make_uri(uri): "handle URI arg" @@ -70,6 +82,7 @@ class File: sha1sum = False, compress = None, sign = None): assert type(uri == URI) + if sha1sum: sha1filename = File.download(URI(uri.get_uri() + '.sha1sum'), transfer_dir) sha1f = file(sha1filename) @@ -106,8 +119,13 @@ class File: def __init__(self, uri, mode, transfer_dir = "/tmp", sha1sum = False, compress = None, sign = None): "it is pointless to open a file without a URI and a mode" + + self.transfer_dir = transfer_dir self.sha1sum = sha1sum self.compress = compress + self.sign = sign + + uri = File.make_uri(uri) if mode==File.read or mode==File.write: self.mode = mode @@ -137,18 +155,31 @@ class File: def close(self, delete_transfer = False): "this method must be called at the end of operation" self.__file__.close() - if self.compress == File.xmill: - pisi.util.run_batch('xcmill -9 -f ' + self.localfile) - self.localfile = self.localfile[:-4] + '.xmi' - elif self.compress == File.sevenzip: - raise Error(_("sevenzip compression not supported yet")) if self.mode == File.write: + if self.compress == File.xmill: + pisi.util.run_batch('xcmill -9 -f ' + self.localfile) + self.localfile = self.localfile[:-4] + '.xmi' + elif self.compress == File.sevenzip: + raise Error(_("sevenzip compression not supported yet")) if self.sha1sum: sha1 = pisi.util.sha1_file(self.localfile) cs = file(self.localfile + '.sha1sum', 'w') cs.write(sha1) cs.close() - + if self.sign==File.detached: + pisi.util.run_batch('gpg --detach-sig ' + self.localfile) + + @staticmethod + def check_signature(uri, transfer_dir, sign=detached): + if sign==File.detached: + try: + sigfilename = File.download(URI(uri + '.sig'), transfer_dir) + except: + raise NoSignatureFound(uri) + if os.system('gpg --verify ' + sigfilename) != 0: + raise InvalidSignature(uri) + # everything is all right here + def flush(self): self.__file__.flush() diff --git a/pisi/index.py b/pisi/index.py index dc920b3f..62f2a848 100644 --- a/pisi/index.py +++ b/pisi/index.py @@ -50,10 +50,16 @@ class Index(XmlFile): t_Packages = [ [metadata.Package], autoxml.optional, "Package"] t_Components = [ [component.Component], autoxml.optional, "Component"] - def read_uri(self, filename, repo = None): + # read index for a given repo, force means download even if remote not updated + def read_uri(self, filename, repo = None, force = False): """Read PSPEC file""" tmpdir = os.path.join(ctx.config.index_dir(), repo) - self.read(filename, tmpDir=tmpdir, sha1sum=True, compress=File.xmill) + self.read(filename, tmpDir=tmpdir, sha1sum=not force, + compress=File.xmill, sign=File.detached) + + def check_signature(self, filename, repo): + tmpdir = os.path.join(ctx.config.index_dir(), repo) + File.check_signature(filename, tmpdir) def index(self, repo_uri, skip_sources=False): self.repo_dir = repo_uri diff --git a/pisi/pxml/autoxml.py b/pisi/pxml/autoxml.py index 08f9a40a..9a603e63 100644 --- a/pisi/pxml/autoxml.py +++ b/pisi/pxml/autoxml.py @@ -429,9 +429,9 @@ class autoxml(oo.autosuper, oo.autoprop): if xmlfile_support: def read(self, uri, keepDoc = False, tmpDir = '/tmp', - sha1sum = False, compress = None): + sha1sum = False, compress = None, sign = None): "read XML file and decode it into a python object" - self.readxml(uri, tmpDir, sha1sum=sha1sum, compress=compress) + self.readxml(uri, tmpDir, sha1sum=sha1sum, compress=compress, sign=sign) errs = [] self.decode(self.rootNode(), errs) if hasattr(self, 'read_hook'): @@ -449,7 +449,7 @@ class autoxml(oo.autosuper, oo.autoprop): raise Error(*errs) def write(self, uri, keepDoc = False, tmpDir = '/tmp', - sha1sum = False, compress = None): + sha1sum = False, compress = None, sign = None): "encode the contents of the python object into an XML file" errs = self.errors() if errs: @@ -463,7 +463,7 @@ class autoxml(oo.autosuper, oo.autoprop): if errs: errs.append(_("autoxml.write: File encoding '%s' has errors") % uri) raise Error(*errs) - self.writexml(uri, tmpDir, sha1sum=sha1sum, compress=compress) + self.writexml(uri, tmpDir, sha1sum=sha1sum, compress=compress, sign=sign) if not keepDoc: self.unlink() # get rid of the tree diff --git a/pisi/pxml/xmlfilecdom.py b/pisi/pxml/xmlfilecdom.py index bbf78256..eceabd85 100644 --- a/pisi/pxml/xmlfilecdom.py +++ b/pisi/pxml/xmlfilecdom.py @@ -67,9 +67,9 @@ class XmlFile(object): """returns root document element""" return self.doc.documentElement - def readxml(self, uri, tmpDir='/tmp', sha1sum=False, compress=None): + def readxml(self, uri, tmpDir='/tmp', sha1sum=False, compress=None, sign=None): uri = File.make_uri(uri) - localpath = File.download(uri, tmpDir,sha1sum=sha1sum,compress=compress) + localpath = File.download(uri, tmpDir,sha1sum=sha1sum,compress=compress,sign=sign) try: self.doc = NoExtDtdReader.parseUri(Ft.Lib.Uri.OsPathToUri(localpath)) return self.doc.documentElement @@ -78,8 +78,8 @@ class XmlFile(object): except exceptions.ValueError, e: raise Error(_("File '%s' not found") % localpath ) - def writexml(self, uri, tmpDir = '/tmp', sha1sum=False, compress=None): - f = File(uri, File.write, sha1sum=sha1sum, compress=compress) + def writexml(self, uri, tmpDir = '/tmp', sha1sum=False, compress=None, sign=None): + f = File(uri, File.write, sha1sum=sha1sum, compress=compress, sign=sign) PrettyPrint(self.rootNode(), stream = f) f.close()