#Copyright ReportLab Europe Ltd. 2000-2004
#all rights reserved
#history www.reportlab.co.uk/rl-cgi/viewcvs.cgi/rlextra/utils/cgisupport.py
'''
CGI support utilities
'''
__version__=''' $Id: cgisupport.py 14925 2004-10-12 08:58:03Z rgbecker $ '''
import sys, string, os, cgi, random, struct, time

def quoteValue(v):
	"""Defense against people embedding Javascript in parameters.
	Script kiddies often hope that you will echo through some form
	parameter like a surname in a text field into HTML, so they
	can embed some huge chunk of their own HTMl code in it.
	This ensures any tags in input are escaped and thus deactivated"""

	from string import split, strip, replace, join
	# quote ampersands, but not twice
	as = split(v, "&")
	if len(as)>1:
		out = [as[0]]
		for frag in as[1:]:
			#print frag
			doquote = 1
			if ";" in frag:
				tosemi = split(frag, ";")[0]
				#print tosemi
				if len(split(tosemi))<=1:
					# this is an entity, don't quote it again
					doquote = 0
			if doquote:
				out.append("&amp;")
			else:
				out.append("&")
			out.append(frag)
		v = join(out, "")
	v = replace(v, "<", "&lt;")
	v = replace(v, ">", "&gt;")
	v = replace(v, "\"", "&quot;")
	v = replace(v, "'", "&apos;")
	return v


def makeCgiScript(cgiDir, scriptName, workDir, workModule, workFunc, verbose=0,
					kind='kfdserver', script_pfx=None):
	"""Generates a well formed CGI script in the given directory.
	Does nothing if one exists and if it would be identical to the
	one created.  Thus, programs can call this often or in setup
	scripts."""
	if kind=='kfdserver':
		text =	[
				'import os, sys',
				'os.chdir("%s")' % workDir,
				'sys.path.insert(0, os.getcwd())',
				'import %s' % workModule,
				'%s.%s()' % (workModule, workFunc),
				]
	elif kind=='ers':
		if not script_pfx: script_pfx = os.path.splitext(os.path.basename(script_name))[0]
		text =	[
				'if __name__=="__main__": #noruntests',
				'	from rlextra.ers.controller import main',
				'	main(1,APPDIR="%s",SCRIPT_PFX="%s")' % (workDir,script_pfx),
				]
	else:
		raise ValueError("Unknown script kind='%s'" % kind)

	text = string.join(['#!%s -u'%sys.executable]+text,'\n')
	filename = os.path.join(cgiDir, scriptName)
	if os.path.isfile(filename):
		currentText = open(filename, 'r').read()
		if currentText == text:
			if verbose:print 'cgi already correct, no change needed'
		else:
			open(filename, 'w').write(text)
			if verbose: print 'wrote %s' % filename
	else:
		open(filename, 'w').write(text)
		if verbose: print 'wrote %s' % filename
	# now check executable permissions. How could we detect mode?
	if sys.platform <> 'win32':
		os.chmod(filename, 0755)
		if verbose: print 'set to executable'

def getCgiParams():
	"""Returns dictionary of CGI parameters. Quoted for safety
	by default; this will not affects most real world things we do"""
	dictionary = {}
	import cgi
	form = cgi.FieldStorage()
	for name in form.keys():
		try:
			value = form[name].value
		except AttributeError:
			raise AttributeError('form["%s"]=%s'%(name,str(form[name])))
		dictionary[name] = quoteValue(value)
	return dictionary

def getCommandLineParams():
	"""Alternate syntax for getting key/value params.
	Use key=value on the command line"""
	dictionary = {}
	for arg in sys.argv[1:]:
		chunks = string.split(arg, '=')
		if len(chunks) == 2:
			key, value = chunks
			dictionary[key] = value
	return dictionary

def isCGI():
	"Attempt to guess if current process is running CGI"
	# use environment variables.  There are many candidates
	# and we should probably check ten of them and give
	# a fractional answer :-(
	return os.environ.has_key('GATEWAY_INTERFACE')

def getParams():
	"""Returns either CGI or command line parameters.

	Should be the generic handler, so we can always
	test a CGI app by doing the likes of
	  ./myapp.cgi action=request codename=wombat
	"""
	if isCGI():
		return getCgiParams()
	else:
		return getCommandLineParams()

if sys.hexversion<0x2030000:
	_n_random_bits_h0 = 4
	_n_random_bits_v0 = 0xffff
else:
	_n_random_bits_h0 = 8
	_n_random_bits_v0 = 0xffffffffL

try:
	pid = open('/dev/urandom','rb')
	dur = pid.read(4)
	pid.close()
	dur = struct.unpack('L',dur)[0]
except:
	dur = 0x7895L

try:
	pid = long(os.getpid())
except:
	pid = 0x1234L

random.seed(long(time.time()*0xfabcd13L)|(pid<<64)|(dur<<32))
del pid, dur
def n_random_bits16(n):
	'''return at least n random bits in hex'''
	n = int((n+3)/4)
	S = []
	h = _n_random_bits_h0
	v = _n_random_bits_v0
	while n>0:
		if h>1:
			while n<h:
				h >>= 1
				v >>= 4*h
		k, n = divmod(n,h)
		fmt = '%%%d.%dX' % (h,h)
		S += [fmt % random.randint(0,v) for x in xrange(k)]
	return ''.join(S)

def n_random_bits36(n):
	'''return at least n random bits in base36 0-9a-z'''
	import baseconvert
	return baseconvert.baseconvert(n_random_bits16(n),baseconvert.BASE16,baseconvert.BASE36)

class TimeStamp:
	def __init__(self,pfx=None,sfx=None,appName=None,hostname=None):
		import time, socket
		self.pfx = pfx
		self.sfx = sfx
		self.now = time.time()
		(self.year, self.month, self.day, self.hour, self.minute, self.second) = time.gmtime(self.now)[:6]
		self.currentDateString = "%s%02d%02d" % (self.year, self.month, self.day)
		self.timeStamp = "%s%02d%02d%02d" % (self.currentDateString, self.hour, self.minute, self.second)
		if hostname is None: hostname = socket.gethostname()
		self.hostname = hostname
		self.appName = appName
		self.getCurrentId()

	def getCurrentId(self):
		self.currentId = '-'.join(filter(None,(self.pfx, self.hostname, self.appName, self.timeStamp,  n_random_bits36(128), self.sfx)))
		return self.currentId

class BorgTimeStamp(TimeStamp):
	__borg = {}
	def __init__(self,pfx=None,sfx=None,appName=None,hostname=None):
		self.__dict__ = self.__borg
		if not self.__dict__:
			if not appName:
				appName = os.environ.get('SCRIPT_NAME','')
				if not appName:
					appName = sys.argv[0]
					i = 1
					while not appName:
						try:
							frame = sys._getframe(i)
							i += 1
						except:
							break
						upLocals = frame.f_locals
						for name in ('req','request'):
							req = upLocals.get(name,None)
							if req:
								appName = getattr(req,'url','')
								if appName: break
				if appName: appName=os.path.splitext(os.path.basename(appName))[0]
			if not appName: appName = 'unknownapp'
			TimeStamp.__init__(self,pfx=pfx,sfx=sfx,appName=appName)

	def reset(self,pfx=None,sfx=None,appName=None,hostname=None):
		if self.__dict__:
			pfx = pfx or self.pfx
			sfx = sfx or self.sfx
			appName = appName or self.appName
			hostname = hostname or self.appName
			self.clear()
			self.__init__(pfx=pfx,sfx=sfx,appName=appName,hostname=hostname)

	def clear(self):
		self.__dict__.clear()
