# portage_checksum.py -- core Portage functionality
# Copyright 1998-2004 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2
# $Header: /var/cvsroot/gentoo-src/portage/pym/portage_checksum.py,v 1.10 2004/10/20 01:24:49 carpaski Exp $

from portage_const import PRIVATE_PATH,PRELINK_BINARY
import os
import shutil
import stat
import portage_exec
import portage_util
import portage_locks
import commands
import sha

prelink_capable = False
if os.path.exists(PRELINK_BINARY):
	results = commands.getstatusoutput(PRELINK_BINARY+" --version > /dev/null 2>&1")
	if (results[0] >> 8) == 0:
		prelink_capable=1
	del results

def perform_md5(x, calc_prelink=0):
	return perform_checksum(x, md5hash, calc_prelink)[0]

def perform_sha1(x, calc_prelink=0):
	return perform_checksum(x, sha1hash, calc_prelink)[0]

def perform_all(x, calc_prelink=0):
	mydict = {}
	mydict["SHA1"] = perform_sha1(x, calc_prelink)
	mydict["MD5"] = perform_md5(x, calc_prelink)
	return mydict

def get_valid_checksum_keys():
	return ["SHA1", "MD5"]

def verify_all(filename, mydict, calc_prelink=0, strict=0):
	# Dict relates to single file only.
	# returns: (passed,reason)
	file_is_ok = True
	reason     = "Reason unknown"
	try:
		if mydict["size"] != os.stat(filename)[stat.ST_SIZE]:
			return False,"Filesize does not match recorded size"
	except OSError, e:
		return False, str(e)
	for x in mydict.keys():
		if   x == "size":
			continue
		elif x == "SHA1":
			if mydict[x] != perform_sha1(filename, calc_prelink=calc_prelink):
				if strict:
					raise portage_exception.DigestException, "Failed to verify '$(file)s' on checksum type '%(type)s'" % {"file":filename, "type":x}
				else:
					file_is_ok = False
					reason     = "Failed on %s verification" % (x,)
					break
		elif x == "MD5":
			if mydict[x] != perform_md5(filename, calc_prelink=calc_prelink):
				if strict:
					raise portage_exception.DigestException, "Failed to verify '$(file)s' on checksum type '%(type)s'" % {"file":filename, "type":x}
				else:
					file_is_ok = False
					reason     = "Failed on %s verification" % (x,)
					break
	return file_is_ok,reason

# We _try_ to load this module. If it fails we do the slow fallback.
try:
	import fchksum
	
	def md5hash(filename):
		return fchksum.fmd5t(filename)

except ImportError:
	import md5
	def md5hash(filename):
		f = open(filename, 'rb')
		blocksize=32768
		data = f.read(blocksize)
		size = 0L
		sum = md5.new()
		while data:
			sum.update(data)
			size = size + len(data)
			data = f.read(blocksize)
		f.close()

		return (sum.hexdigest(),size)

def sha1hash(filename):
	f = open(filename, 'rb')
	blocksize=32768
	data = f.read(blocksize)
	size = 0L
	sum = sha.new()
	while data:
		sum.update(data)
		size = size + len(data)
		data = f.read(blocksize)
	f.close()

	return (sum.hexdigest(),size)

def perform_checksum(filename, hash_function=md5hash, calc_prelink=0):
	myfilename      = filename[:]
	prelink_tmpfile = PRIVATE_PATH+"/prelink-checksum.tmp."+str(os.getpid())
	mylock          = None
	
	if calc_prelink and prelink_capable:
		mylock = portage_locks.lockfile(prelink_tmpfile, wantnewlockfile=1)
		# Create non-prelinked temporary file to md5sum.
		# Raw data is returned on stdout, errors on stderr.
		# Non-prelinks are just returned.
		try:
			shutil.copy2(filename,prelink_tmpfile)
		except SystemExit, e:
			raise
		except Exception,e:
			portage_util.writemsg("!!! Unable to copy file '"+str(filename)+"'.\n")
			portage_util.writemsg("!!! "+str(e)+"\n")
			sys.exit(1)
		portage_exec.spawn(PRELINK_BINARY+" --undo "+prelink_tmpfile,fd_pipes={})
		myfilename=prelink_tmpfile

	myhash, mysize = hash_function(myfilename)

	if calc_prelink and prelink_capable:
		os.unlink(prelink_tmpfile)
		portage_locks.unlockfile(mylock)

	return (myhash,mysize)
