import string, cgi, cgitb, os, Image, DataHandler, re, Cookie, random, time

"""
	www - presenterer et grensesnitt i html til en tilfeldig gitt browser
		  via html.

2002-05-23 - Mats Lindh
"""

##########
# class cgiApp
#	- selve applikasjonen som kjøres via cgi.
##########

class cgiApp:

	##########
	# __init__(form, katalog, webkatalog, handler, fil, auth)
	#	- selve applikasjonen som kjøres via cgi.
	#
	#	form:	get/post-informasjon
	#	katalog:	katalogen som bildene skal leses fra.
	#	webkatalog:	katalogen dette tilsvarer på web (f.eks. /pictures/)
	#	handler:	tekst-streng som indikerer hvilken handler som skal brukes i bakgrunnen, ('xml', 'inline', 'flat')
	#	fil:	hvilken fil som handleren skal bruke
	#	auth:	status for autentikasjon.
	##########

	def __init__(self, form, katalog, webkatalog, handler, fil, auth):
		# setter opp member-variabler..
		self.directory = katalog
		self.handler = handler
		self.handler_fil = fil
		self.web_directory = webkatalog
		self.authenticated = auth
		
		self.form = form
		
		# starter datahandleren for valgt oppsett..
		self.dh = DataHandler.DataHandler(self.handler, self.handler_fil)

	##########
	# printHeader()
	#	- skriver ut header for html-siden.
	##########
	
	def printHeader(self):
		print """<?xml version="1.0" encoding="iso-8859-1"?>
	<!DOCTYPE
	  html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
	  "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
	<html>
		<head>
			<title>"""

		# easteregg. ekke noe gøy når du leser dette da!		
		if not (self.form.getfirst("version", "unl33t") == "l33t"):
			print ("Thumbinator 2000")
		else:
			print ("Thumbinator Gold Pro Enterprise XP Edition 2000")
		
		
		print """</title>
			<link rel="stylesheet" type="text/css" href="/ml/eksamen/style.css" />
		</head>
		<!-- hey! hidden feature: try ?version=l33t or &version=l33t! -->
		<body>
			<table width='100%'>
				<tr>
					<td class='header'>"""

		# easteregg. ekke noe gøy når du leser dette da!		

		if not (self.form.getfirst("version", "unl33t") == "l33t"):
			print ("Thumbinator 2000")
		else:
			print ("Thumbinator Gold Pro Enterprise XP Edition 2000")

		
		print """</td>
				</tr>
			</table>
			<table width='100%'>
				<tr>
"""
		print ("					<td class='view'>Vis som: <a href='" + os.environ["SCRIPT_NAME"] + "?mode=thumb'>thumbnails</a> - <a href='" + os.environ["SCRIPT_NAME"] + "?mode=list'>liste</a> || Operasjoner: <a href='" + os.environ["SCRIPT_NAME"] + "?view=search'>s&oslash;k etter bilde</a></td>")

		print ("""				</tr>
			</table>
			<div class='images'>
	""")

	##########
	# printFooter()
	#	- skriver ut footer (slutten) for html-siden.
	##########
	
	def printFooter(self):
		print ("""		</div>
		<table width='100%'>
			<tr>
				<td class='login'>""")
		if not (self.authenticated):
			print ("				<form action='" + os.environ["SCRIPT_NAME"] + "' method='post'><p>brukernavn: <input type='text' name='username' class='login' /> passord: <input type='password' name='password' class='login' /> <input type='submit' value='logg inn' class='submit' /></p></form>")
		else:
			print ("<form method='get' action='" + os.environ["SCRIPT_NAME"] + "'><p>logget inn [<a href='" + os.environ["SCRIPT_NAME"] + "?logout=true'>logg ut</a>]</p></form>")
		
		print ("""
				</td>
			</tr>
		</table>
	</body>
</html>""")

	##########
	# printSearch()
	#	- gir soekeinterface og setter opp objektet som det skal filtreres mot.
	##########
	
	def printSearch(self):
		if (self.form.getfirst("sok", "no?") == "ohyess"):
			# noen har prøvd å søke .. så vi lager oss et objekt med det man har søkt etter..
			sok = DataHandler.ImageData(self.form.getfirst("filnavn", ""), self.form.getfirst("beskrivelse", ""), self.form.getfirst("kategori", ""), self.form.getfirst("sted", ""), self.form.getfirst("dato", ""), self.form.getfirst("fotograf", ""))
			# og skriver ut alt som treffer på vanlig måte..
			self.printImages(sok)
		
		# skriver ut selve formet..
		print ("<form method='get' action='" + os.environ["SCRIPT_NAME"] + "'>")
		print """
					<p>
						<input type='hidden' name='view' value='search' />
						<input type='hidden' name='sok' value='ohyess' />
					</p>
					<table class='search'>
					<tr>
						<td>Filnavn:</td><td><input type='text' name='filnavn' /></td>
					</tr>
					<tr>
						<td>Beskrivelse:</td><td><input type='text' name='beskrivelse' /></td>
					</tr>
					<tr>
						<td>Kategori:</td><td><input type='text' name='kategori' /></td>
					</tr>
					<tr>
						<td>Sted:</td><td><input type='text' name='sted' /></td>
					</tr>
					<tr>
						<td>Dato:</td><td><input type='text' name='dato' /></td>
					</tr>
					<tr>
						<td>Fotograf:</td><td><input type='text' name='fotograf' /></td>
					</tr>
					<tr>
						<td colspan='2'>
							Vis som:<br />
						</td>
					</tr>
					<tr>
						<td><input type='radio' name='mode' value='thumb' """
		
		# hvilken måte vil vi ha ting listet på? (slik at verdien blir stående fra gang til gang)
		method = string.lower(self.form.getfirst("mode", "thumb"))
		
		# er det thumbnails som er valgt?
		if (method == 'thumb'):
			print "checked='checked'"
		
		print (" /> thumbnails</td><td><input type='radio' name='mode' value='list' ")
		
		# eller er det vanlig liste?
		if (method == 'list'):
			print "checked='checked'"
			
		print (""" /> liste</td></tr>
					<tr>
						<td></td><td><input type='submit' name='sok' value='S&oslash;k' /></td>
					</tr>
					</table>
				</form>
				(regul&aelig;ruttrykk er tillatt i alle felt)
		""")
		
	##########
	# viewImage()
	#	- viser et enkelt bilde og evnt. tekstfelt hvor informasjonen om bildet kan redigeres (om man er logget inn)
	##########
	
	def viewImage(self):
		# henter ut informasjon om hvilken fil vi skal se ..
		file = self.form.getfirst("file", None)
		if not (file == None):
			# henter ut 'forrige' og 'neste' bilde..
			
			filer = os.listdir(self.directory)
			
			neste = ''
			forrige = ''
			
			jpg_filer = []
			
			# fjerner ting som ikke er jpg-bilder..
			for fil in filer:
				if (string.lower(fil[-4:]) == ".jpg"):
					jpg_filer.append(fil)
			
			for i in range(0,len(jpg_filer)):
				# hvis dette er filen som skal vises..
				if (jpg_filer[i] == file):
					# hvis det er siste fil .. så skal neste være den første..
					if (i == len(jpg_filer)-1):
						neste = jpg_filer[0]
						forrige = jpg_filer[i-1]
					else:
						# ellers bare enkel og grei slik (funker fint ettersom [-1] er siste element)
						forrige = jpg_filer[i-1]
						neste = jpg_filer[i+1]
					
			# la oss vise den også, ok?
			print ("			<table>")
			print ("				<tr>")
			print ("					<td class='stortbilde'>")
			print ("						<a href='" + os.environ["SCRIPT_NAME"] + "?view=view&amp;file=" + forrige + "'>forrige</a> - <a href='" + os.environ["SCRIPT_NAME"] + "?view=view&amp;file=" + neste + "'>neste</a><br /><br />")
			print ("						<img src='" + self.web_directory + "/" + self.directory + "/" + file + "' alt='" + file + "' /><br /><br />")
			
			# hvis vi skal oppdatere noe, så lagrer vi informasjonen..
			if not (self.form.getfirst("beskrivelse_set", None) == None):
				if (self.authenticated):
					self.dh.set_data(file, self.form.getfirst("beskrivelse_set", ""), self.form.getfirst("kategori_set", ""), self.form.getfirst("sted_set", ""), self.form.getfirst("dato_set", ""), self.form.getfirst("fotograf_set", ""))
					self.dh.write()
			
			# så henter vi ut informasjon fra handleren vår..
			info = self.dh.get_data(file)
			
			# hvis vi ikke er autentisert, så vil vi ikke at folk skal kunne redigere.
			if not (self.authenticated):
				if not (info == None):
					print ("<span style='font-weight:	bold'>" + info.sted + "</span> - <span style='font-weight:	bold'>" + info.dato + "</span><br />")
					print (info.beskrivelse)
					print ("<br /><br />(kategori: " + info.kategori + ", fotograf: " + info.fotograf + ")")
				else:
					print ("<span style='font-weight:	bold'>" + file + "</span><br />")
					print ("Ingen informasjon er lagret om denne filen.")
			else:
				# og hvis de er autentisert, så skal de få fine editbokser..
				if not (info == None):
					sted = info.sted
					dato = info.dato
					beskrivelse = info.beskrivelse
					kategori = info.kategori
					fotograf = info.fotograf
				else:
					sted = ''
					dato = ''
					beskrivelse = ''
					kategori = ''
					fotograf = ''
				
				print ("						<form method='post' action='" + os.environ["SCRIPT_NAME"] + "'>")
				print ("							<input type='hidden' name='file' value='" + file + "'>")
				print ("							<input type='hidden' name='view' value='view'>")
				print ("							sted: <input type='text' value='" + sted + "' name='sted_set' class='login' /> - tidspunkt / dato: <input type='text' value='" + dato + "' name='dato_set' class='login' /><br />")
				print ("							Beskrivelse:<br /><textarea name='beskrivelse_set' rows='5' cols='50' class='login'>" + beskrivelse + "</textarea><br />")
				print ("							kategori: <input type='text' value='" + kategori + "' name='kategori_set' class='login' />, fotograf: <input type='text' value='" + fotograf + "' name='fotograf_set' class='login' /><br /><br />")
				print ("							<input type='submit' value='oppdater / registrer' class='submit' />")
				print ("						</form>")
						
			print ("					</td>")
			print ("				</tr>")
			print ("			</table>")
	
	##########
	# printImages(sok=None)
	#	- viser en oversikt over alle bildene som er tilgjenglige, enten som liste eller som thumbnails.
	#     filtrerer i henhold til objektet som kommer fra printSearch(), dersom det skal filtreres.
	#
	#	sok:	objekt som det filtreres mot.
	##########
	
	
	def printImages(self, sok=None):
		# sjekker at det faktisk er en katalog vi leker med ..
		if (os.path.isdir(self.directory)):
			print ("			<table>")
			filer = os.listdir(self.directory)
			method = string.lower(self.form.getfirst("mode", "thumb"))

			numpic = 0
			
			# lister opp alle filene...
			for fil in filer:
				# bare interessert i jpeg-bilder..
				if (string.lower(fil[-4:]) == ".jpg"):
					# hvis vi skal liste thumbnails..
					if (method == 'thumb'):
						# så cacher vi thumbnails i 'thumbs'-katalogen..
						thumb = self.directory + "/thumbs/" + fil
						if not (os.path.isfile(thumb)):
							# lager thumbnail ..
							
							if not (os.path.isdir(self.directory + "/thumbs")):
								os.mkdir(self.directory + "/thumbs")
								
							image = Image.open(self.directory + "/" + fil)
							
							sx = 0
							sy = 0
							
							# sørger for riktig resizing (slik at ikke ratioen blir fscket opp)
							if (image.size[0] > image.size[1]):
								sx = 150
								sy = round(image.size[1] / (image.size[0] / 150))
							elif (image.size[1] > image.size[0]):
								sx = round(image.size[0] / (image.size[1] / 150))
								sy = 150
							else:
								sx = 150
								sy = 150
		
							# resizer og lagrer.
							image = image.resize((sx,sy), Image.BILINEAR)
							image.save(thumb)
												
					# henter ut informasjon om bildet..
					info = self.dh.get_data(self.directory + "/" + fil)

					# hvis vi ikke har beskrivelse, så setter vi beskrivelsen til filnavnet
					if (not info == None):
						beskrivelse = info.beskrivelse
					else:
						beskrivelse = fil

					skrivut = 0

					# hvis vi ikke søker, så skal vi skrive ut alt.
					if (sok == None):
						skrivut = 1
					else:
						# ellers skal vi bare skrive ut om det aktuelle bildet passer til kriteriene.
						if (info == None):
							if re.compile(string.lower(sok.filnavn)).search(string.lower(fil), 0):
								if ((sok.beskrivelse == "") and (sok.kategori == "") and (sok.sted == "") and (sok.dato == "") and (sok.fotograf == "")):
									skrivut = 1
						else:
							if re.compile(string.lower(sok.filnavn)).search(string.lower(info.filnavn), 0) and re.compile(string.lower(sok.beskrivelse)).search(string.lower(info.beskrivelse), 0) and re.compile(string.lower(sok.kategori)).search(string.lower(info.kategori), 0) and re.compile(string.lower(sok.sted)).search(string.lower(info.sted), 0) and re.compile(string.lower(sok.dato)).search(string.lower(info.dato), 0) and re.compile(string.lower(sok.fotograf)).search(string.lower(info.fotograf), 0):
								skrivut = 1
					
					if (skrivut == 1):
						# skriver ut i riktig form..
						if (method == 'thumb'):
							if (numpic == 0):
								print ("			<tr>\n")
							
							print ("					<td class='bilde'><table><tr><td><a href='" + os.environ["SCRIPT_NAME"] + "?view=view&amp;file=" + fil + "'><img src='/ml/eksamen/" + self.directory + "/thumbs/" + fil + "' alt='" + fil + "'/></a></td></tr><tr><td class='kommentar'>" + beskrivelse + "</td></tr></table></td>")
							
							numpic += 1
							
							# sørger for at vi får 4 bilder i hver rad..
							if (numpic == 4):
								numpic = 0
								print ("			</tr>")
						elif (method == 'list'):
							print ("<tr><td class='filnavn'><a href='" + os.environ["SCRIPT_NAME"] + "?view=view&amp;file=" + fil + "'>" + fil + "</a></td><td class='beskrivelse'>" + beskrivelse + "</td>")						
							print ("<td class='vis'><a href='" + os.environ["SCRIPT_NAME"] + "?view=view&amp;file=" + fil + "'>vis</a></td></tr>")
	
			if not (numpic == 0):
				print ("			</tr>")
			print ("			</table>");
										
		else:
			self.printError("Hejsann, hejsann, klarte ikke &aring; &aring;pne bildekatalogen som angitt!")


	##########
	# printError()
	#	- skriver ut stor, stygg feilmelding.
	#
	#	error:	selve feilmeldingen som skal skrives ut. 
	##########
	
	def printError(self, error):
		print ("<div class='error'>" + error + "</div>")


##########
# class loginHandler
#	- håndterer innloggingsrutiner og selve sessiondelen (slik at man forblir innlogget over flere sider).
##########
class loginHandler:
	##########
	# __init__(cookiefile, form)
	#	- initialiserer loginHandler
	#
	#	cookiefile:	hvilken fil handleren skal bruke for å lagre cookie-informasjon
	#	form:	data tilgjenglige via post/get via cgi.
	##########
	def __init__(self, cookiefile, form):
		self.auth = 0

		# initialiserer verdier..
		self.form = form
		self.cookies = []
		self.cookie_time = []

		# sjekker at vi faktisk har en cookiefil..
		if (os.path.isfile(cookiefile)):
			fil = open (cookiefile, "r")
			# og leser inn data fra denne filen..
			for line in fil.readlines():
				line = string.split(line, ":")
				# hvis cookien ikke har expiret (86400 sekunder == et døgn)
				if (float(line[0]) > (time.time() - 86400)):
					# så tar vi vare på den ..
					self.cookies.append(string.strip(line[1]))
					self.cookie_time.append(string.strip(line[0]))
					
			fil.close()

		# hvis browseren prøver å sende noen cookies til oss ..		
		if os.environ.has_key('HTTP_COOKIE'):
			# så henter vi de inn..
			c = Cookie.Cookie(os.environ["HTTP_COOKIE"])
			
			# hvis den finnes..
			if (c["sessionid"].value in self.cookies):
				# så er vi autentisert..
				self.auth = 1
				
				# hvis brukeren vil logge ut derimot..
				if not (self.form.getfirst("logout", None) == None):
					self.auth = 0
					temp = []
					for cookie in self.cookies:
						# så appender vi alle cookies som ikke passer.. 
						if not (cookie == c["sessionid"].value):
							temp.append(cookie)
							
					# og sørger for at vi får ny cookie-liste..
					self.cookies = temp
							
			else:
				self.auth = 0

		if (self.auth == 0):		
			# henter inn informasjon om brukernavn / passord..
			username = self.form.getfirst("username", None)
			password = self.form.getfirst("password", None)
			
			if ((username == None) or (password == None)):
				# hvis det ikke er angitt noe .. så nei, da er det ikke godkjent.
				self.auth = 0
				
			if ((username == "test") and (password == "test")):
				# hvis brukernavnet og passordet er 'test', så har vi noe på gang!
				self.auth = 1		
			
				sessionid = ""			
				
				# genererer en tilfeldig sessionid .. bestående av 20 tegn. (burde gi 26^20 forskjellige muligheter)..
				for i in xrange(0,20):
					sessionid = sessionid + chr(97 + random.randrange(0, 25))
								
				# lagrer cookien..
				c = Cookie.Cookie()
				c["sessionid"] = sessionid
				
				# sender cookie-headeren..
				print c
				
				# lagrer tiden som cookien ble opprettet på ..
				self.cookie_time.append(time.time())
				self.cookies.append(sessionid)
	
		# så lagrer vi cookiene våre også..
		fil = open (cookiefile, "w")

		# skal lagre fra begge, så jeg tenkte at dette var like greit. :)
		for i in xrange(0,len(self.cookies)):
			if not (string.strip(self.cookies[i]) == ""):
				fil.write(str(self.cookie_time[i]) + ":" + string.strip(self.cookies[i]) + "\n")
		
		# lukker filen
		fil.close()
	
	########
	# authenticated()
	#	- gir 0 dersom brukeren ikke er autentisert .. eller 1 dersom brukeren er autentisert (logget inn).
	########
	
	def authenticated(self):
		return self.auth

def init(form):
	# slår på fin debuggings-mode for CGI..
	cgitb.enable()

	# starter loginHandleren 
	session = loginHandler("cookies.txt", form)

	# fikser content-header (må sette cookies etc først)
	print "Content-Type: text/html\n"
	
	# starter selve applikasjonen vår.. (formdata, katalog som bildene ligger i lokalt, 
	# path til bildene paa web, handler som skal brukes, fil som handleren skal bruke, auth-status).
	app = cgiApp(form, "bilder", "/ml/eksamen", 'xml', 'test.xml', session.authenticated())
	
	# skriver ut html-headeren..
	app.printHeader()
	
	# henter ut 'view'-parameteret..
	view = string.lower(form.getfirst("view", "list"))
	
	# finner riktig ting etter hva view er satt til..
	if (view == 'list'):
		app.printImages()
	elif (view == 'search'):
		app.printSearch()
	elif (view == 'view'):
		app.viewImage()
	
	# skriver ut html-footeren..
	app.printFooter()	
	

if __name__ == "__main__":
	# hvis noen vil starte modulen rett fra scratch, så får de da lov til det også.. :-)
	init(cgi.FieldStorage())
