From 541bb378ddece2eab135a8066a16994e94436dea Mon Sep 17 00:00:00 2001
From: Giulio Cesare Solaroli <giulio.cesare@solaroli.it>
Date: Mon, 03 Oct 2011 16:04:12 +0000
Subject: Merge pull request #1 from gcsolaroli/master

First version of the restructured repository
---
(limited to 'backend/python/src')

diff --git a/backend/python/src/app.yaml b/backend/python/src/app.yaml
new file mode 100644
index 0000000..5e085a9
--- a/dev/null
+++ b/backend/python/src/app.yaml
@@ -0,0 +1,20 @@
+application: clipperz
+version: 1
+runtime: python
+api_version: 1
+
+handlers:
+- url: /json
+  script: clipperz.py
+
+- url: /css
+  static_dir: css
+
+- url: /js
+  static_dir: js
+
+- url: /images
+  static_dir: images
+
+- url: /.*
+  script: clipperz.py
diff --git a/backend/python/src/clipperz.py b/backend/python/src/clipperz.py
new file mode 100644
index 0000000..c8d91de
--- a/dev/null
+++ b/backend/python/src/clipperz.py
@@ -0,0 +1,708 @@
+#	
+#	Copyright 2008-2011 Clipperz Srl
+#	
+#	This file is part of Clipperz's Javascript Crypto Library.
+#	Javascript Crypto Library provides web developers with an extensive
+#	and efficient set of cryptographic functions. The library aims to
+#	obtain maximum execution speed while preserving modularity and
+#	reusability.
+#	For further information about its features and functionalities please
+#	refer to http://www.clipperz.com
+#	
+#	* Javascript Crypto Library is free software: you can redistribute
+#	  it and/or modify it under the terms of the GNU Affero General Public
+#	  License as published by the Free Software Foundation, either version
+#	  3 of the License, or (at your option) any later version.
+#	
+#	* Javascript Crypto Library is distributed in the hope that it will
+#	  be useful, but WITHOUT ANY WARRANTY; without even the implied
+#	  warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+#	  See the GNU Affero General Public License for more details.
+#	
+#	* You should have received a copy of the GNU Affero General Public
+#	  License along with Javascript Crypto Library.  If not, see
+#	  <http://www.gnu.org/licenses/>.
+#	
+
+import os
+import cgi
+import wsgiref.handlers
+
+import datetime
+import uuid
+import random
+import hashlib
+
+import logging
+
+from google.appengine.api import users
+from google.appengine.ext import webapp
+from google.appengine.ext import db
+from google.appengine.ext.webapp import template
+
+from django.utils import simplejson
+
+#==============================================================================
+
+sessionTimeout = datetime.timedelta(minutes=-2)
+
+def randomSeed():
+	return hex(random.getrandbits(32*8))[2:-1]
+
+def clipperzHash(aString):
+	#logging.info(">>> string: " + aString)
+	firstRound = hashlib.sha256()
+	firstRound.update(aString)
+	#logging.info("firstRound: " + firstRound.hexdigest() + " - " + firstRound.digest())
+	result = hashlib.sha256()
+	result.update(firstRound.digest())
+	#logging.info("<<< finalResul: " + result.hexdigest())
+
+	return result.hexdigest()
+	
+#==============================================================================
+
+class User(db.Model):
+	username	= db.StringProperty()
+	srp_s		= db.StringProperty()
+	srp_v		= db.StringProperty()
+	header		= db.TextProperty()
+	statistics	= db.TextProperty()
+	auth_version= db.StringProperty()
+	version		= db.StringProperty()
+	lock		= db.StringProperty()
+	
+	def updateCredentials(self, someCredentials):
+		self.username		= someCredentials['C']
+		self.srp_s			= someCredentials['s']
+		self.srp_v			= someCredentials['v']
+		self.auth_version	= someCredentials['version']
+
+	def update(self, someData):
+		self.header		= someData['header']
+		self.statistics	= someData['statistics']
+		self.version	= someData['version']
+		self.lock		= someData['lock']
+
+#------------------------------------------------------------------------------
+
+class Record(db.Model):
+	user			= db.ReferenceProperty(User)
+	reference		= db.StringProperty()
+	data			= db.TextProperty()
+	version			= db.StringProperty()
+	creation_date	= db.DateTimeProperty(auto_now_add=True)
+	update_date		= db.DateTimeProperty(auto_now_add=True)
+	access_date		= db.DateTimeProperty(auto_now_add=True)
+
+#------------------------------------------------------------------------------
+
+class RecordVersion(db.Model):
+	record				= db.ReferenceProperty(Record)
+	reference			= db.StringProperty()
+	header				= db.TextProperty()
+	data				= db.TextProperty()
+	version				= db.StringProperty()
+	previousVersionKey	= db.StringProperty()
+	previousVersion		= db.SelfReferenceProperty()
+	creation_date		= db.DateTimeProperty(auto_now_add=True)
+	update_date			= db.DateTimeProperty(auto_now_add=True)
+	access_date			= db.DateTimeProperty(auto_now_add=True)
+
+	def update(self, someData):
+		recordData = someData['record'];
+		self.parent().reference =	recordData['reference']
+		self.parent().data =		recordData['data']
+		self.parent().version =		recordData['version']
+		self.parent().update_date =	datetime.datetime.now()
+
+		recordVersionData = someData['currentRecordVersion'];
+		self.reference =			recordVersionData ['reference']
+		self.data =					recordVersionData ['data']
+		self.version =				recordVersionData ['version']
+		#self.previous_version	=	#recordVersionData ['previousVersion']
+		self.previous_version_key =	recordVersionData ['previousVersionKey']
+		self.update_date =			datetime.datetime.now()
+
+#------------------------------------------------------------------------------
+
+class OneTimePassword(db.Model):
+	user			= db.ReferenceProperty(User)
+	status			= db.StringProperty()
+	reference		= db.StringProperty()
+	keyValue		= db.StringProperty()
+	keyChecksum		= db.StringProperty()
+	data			= db.TextProperty()
+	version			= db.StringProperty()
+	creation_date	= db.DateTimeProperty(auto_now_add=True)
+	request_date	= db.DateTimeProperty()
+	usage_date		= db.DateTimeProperty()
+
+	def update(self, someParameters, aStatus):
+		self.reference =		someParameters['reference']
+		self.keyValue =			someParameters['key']
+		self.keyChecksum =		someParameters['keyChecksum']
+		self.data =				someParameters['data']
+		self.version =			someParameters['version']
+		self.status =			aStatus
+
+	def reset(self, aStatus):
+		self.data =		""
+		self.status =	aStatus
+
+		return self
+
+#------------------------------------------------------------------------------
+
+class Session(db.Expando):
+	sessionId	= db.StringProperty()
+	access_date	= db.DateTimeProperty()
+
+#==============================================================================
+
+class MainPage(webapp.RequestHandler):
+	def get(self):
+		path = os.path.join(os.path.dirname(__file__), 'static%s' % self.request.path)
+		self.response.out.write(template.render(path, {}))
+
+#==============================================================================
+
+class XHR(webapp.RequestHandler):
+
+	#==========================================================================
+
+	def get(self):
+		logging.info("self.request.path: " + self.request.path)
+		if self.request.path == "/dump":
+			session = self.getSession()
+			userData = {}
+			offline_data_placeholder = ""
+		
+			user = db.Query(User).filter('username =', session.C).get()
+		
+			userData['users'] = {
+				'catchAllUser': {
+					'__masterkey_test_value__': 'masterkey',
+					's': '112233445566778899aabbccddeeff00112233445566778899aabbccddeeff00',
+					'v': '112233445566778899aabbccddeeff00112233445566778899aabbccddeeff00'
+				}
+			}
+
+			records = {}
+			for currentRecord in db.Query(Record).ancestor(user):
+				versions = {}
+				for currentVersion in db.Query(RecordVersion).ancestor(currentRecord):
+					versions[currentVersion.reference] ={
+						'header':		currentVersion.header,
+						'data':			currentVersion.data,
+						'version':		currentVersion.version,
+						'creationDate':	str(currentVersion.creation_date),
+						'updateDate':	str(currentVersion.update_date),
+						'accessDate':	str(currentVersion.access_date)
+					}
+
+				records[currentRecord.reference] = {
+					'data':				currentRecord.data,
+					'version':			currentRecord.version,
+					'creationDate':		str(currentRecord.creation_date),
+					'updateDate':		str(currentRecord.update_date),
+					'accessDate':		str(currentRecord.access_date),
+					'currentVersion':	currentVersion.reference,
+					'versions':			versions
+				}
+
+			userData['users'][user.username] = {
+				's':					user.srp_s,
+				'v':					user.srp_v,
+				'version':				user.auth_version,
+				'maxNumberOfRecords':	'100',
+				'userDetails':			user.header,
+				'statistics':			user.statistics,
+				'userDetailsVersion':	user.version,
+				'records':				records
+			}
+
+			offline_data_placeholder = offline_data_placeholder + "_clipperz_dump_data_ = " + simplejson.dumps(userData, indent=4) + "\n"
+			offline_data_placeholder = offline_data_placeholder + "Clipperz.PM.Proxy.defaultProxy = new Clipperz.PM.Proxy.Offline();" + "\n"
+			offline_data_placeholder = offline_data_placeholder + "Clipperz.Crypto.PRNG.defaultRandomGenerator().fastEntropyAccumulationForTestingPurpose();" + "\n"
+
+			path = os.path.join(os.path.dirname(__file__), 'static/dump.html')
+
+			self.response.headers.add_header('Content-Type', 'text/html')
+			self.response.headers.add_header('Content-Disposition', 'attachment', filename='Clipperz.html')
+			self.response.out.write(template.render(path, {'offline_data_placeholder': offline_data_placeholder}))
+
+	#==========================================================================
+
+	def post(self):
+		method = self.request.get('method')
+		parameters = simplejson.loads(self.request.get('parameters'))
+		session = self.getSession()
+		result = {};
+
+		#----------------------------------------------------------------------
+
+		if method == 'registration':
+			message = parameters['message'];
+			
+			if message == 'completeRegistration':
+				user = User()
+			
+				user.updateCredentials(parameters['credentials'])
+				user.update(parameters['user'])
+				user.put()
+
+				result['lock'] = user.lock
+				result['result'] = "done"
+
+		#----------------------------------------------------------------------
+
+		elif method == 'handshake':
+			srp_g = 2L
+			srp_n = long("0x%s" % "115b8b692e0e045692cf280b436735c77a5a9e8a9e7ed56c965f87db5b2a2ece3", 16)
+
+			message = parameters['message'];
+
+			#------------------------------------------------------------------
+
+			if message == 'connect':
+				session.C = parameters['parameters']['C']
+				session.A = parameters['parameters']['A']
+
+				user = db.Query(User).filter('username =', session.C).get()
+
+				if user != None:
+					try:
+						optId = session.otpId
+
+						oneTimePassword = db.Query(OneTimePassword).filter('keyValue =', optId).get()
+						
+						if oneTimePassword.parent().username != user.username:
+							oneTimePassword.reset('DISABLED').put()
+							raise Exception, "User missmatch between the current session and 'One Time Password' user"
+						elif oneTimePassword.status != 'REQUESTED':
+							oneTimePassword.reset('DISABLED').put()
+							raise Exception, "Tring to use an 'One Time Password' in the wrong state"
+
+						oneTimePassword.reset("USED").put()
+
+						result['oneTimePassword'] = oneTimePassword.reference
+
+					except Exception, detail:
+						logging.error("connect.optId: " + str(detail))
+
+					session.s = user.srp_s
+					session.v = user.srp_v
+				else:
+					session.s = "112233445566778899aabbccddeeff00112233445566778899aabbccddeeff00"
+					session.v = "112233445566778899aabbccddeeff00112233445566778899aabbccddeeff00"
+
+				session.b = randomSeed()
+				session.B = hex(long("0x%s" % session.v, 16) + pow(srp_g, long("0x%s" %session.b, 16), srp_n))[2:-1]
+
+				result['s'] = session.s
+				result['B'] = session.B
+
+			#------------------------------------------------------------------
+
+			elif message == 'credentialCheck':
+				B = long("0x%s" % session.B, 16)
+				b = long("0x%s" % session.b, 16)
+				A = long("0x%s" % session.A, 16)
+				v = long("0x%s" % session.v, 16)
+				u = long("0x%s" % clipperzHash(str(B)), 16)
+				n = srp_n
+
+				S  = pow((A * pow(v, u, n)), b, n)
+				K  = clipperzHash(str(S))
+				M1 = clipperzHash(str(A) + str(B) + K)
+
+				if M1 == parameters['parameters']['M1']:
+					session.K = K
+					M2 = clipperzHash(str(A) + M1 + K)
+
+					result['M2'] = M2
+					result["connectionId"] = ""
+					result["loginInfo"] = {}
+					result["loginInfo"]["latest"] = {}
+					result["loginInfo"]["current"] = {}
+					result["offlineCopyNeeded"] = "false";
+					result["lock"] = "----";
+				else:
+					result['error'] = "?"
+
+			#------------------------------------------------------------------
+
+			elif message == 'oneTimePassword':
+				oneTimePassword = db.Query(OneTimePassword).filter("keyValue =", parameters["parameters"]["oneTimePasswordKey"]).get()
+
+				if oneTimePassword != None:
+					if oneTimePassword.status == 'ACTIVE':
+						if oneTimePassword.keyChecksum == parameters['parameters']['oneTimePasswordKeyChecksum']:
+							#session.userId =	str(oneTimePassword.parent().username)
+							session.otpId =		str(oneTimePassword.keyValue)
+							
+							result['data'] = oneTimePassword.data
+							result['version'] = oneTimePassword.version
+
+							oneTimePassword.reset('REQUESTED').put()
+
+						else:
+							oneTimePassword.reset('DISABLED').put()
+							raise Exception, "The requested One Time Password has been disabled, due to a wrong keyChecksum"
+					else:
+						raise Exception, "The requested One Time Password was not active"
+				else:
+					raise Exception, "The requested One Time Password has not been found"
+
+		#----------------------------------------------------------------------
+
+		elif method == 'message':
+			if parameters['srpSharedSecret'] == session.K:
+				message = parameters['message']
+
+				if message == 'getUserDetails':
+					#	{"message":"getUserDetails", "srpSharedSecret":"f18e5cf7c3a83b67d4db9444af813ee48c13daf4f8f6635397d593e52ba89a08", "parameters":{}}
+					user = db.Query(User).filter('username =', session.C).get()
+					
+					result['header'] =		user.header;
+					result['statistics'] =	user.statistics;
+					result['version'] =		user.version;
+
+				elif 	message == "addNewRecords":
+					user = db.Query(User).filter('username =', session.C).get()
+					result = db.run_in_transaction(self.addNewRecords, session, user, parameters)
+
+					"""
+					user = db.Query(User).filter('username =', session.C).get()
+					user.update(parameters['parameters']['user'])
+					
+					for recordParameter in parameters['parameters']['records']:
+						record = Record(parent=user)
+						record.put()
+						recordVersion = RecordVersion(parent=record)
+						recordVersion.put()
+						
+						recordVersion.update(recordParameter)
+
+						record.put()
+						recordVersion.put()
+
+					user.put();
+
+					result['lock'] = user.lock
+					result['result'] = 'done'
+					"""
+					
+				elif message == 'getRecordDetail':
+					record = db.Query(Record).ancestor(db.Query(User).filter('username =', session.C).get()).filter('reference =', parameters["parameters"]["reference"]).get()
+					recordVersion = db.Query(RecordVersion).ancestor(record).get()
+
+					result['currentVersion'] = {}
+					result['currentVersion']['reference'] =		recordVersion.reference
+					result['currentVersion']['data'] =			recordVersion.data
+					result['currentVersion']['header'] =		recordVersion.header
+					result['currentVersion']['version'] =		recordVersion.version
+					result['currentVersion']['creationDate'] =	str(recordVersion.creation_date)
+					result['currentVersion']['updateDate'] =	str(recordVersion.update_date)
+					result['currentVersion']['accessDate'] =	str(recordVersion.access_date)
+
+					result['reference'] =						record.reference
+					result['data'] =							record.data
+					result['version'] =							record.version
+					result['creationDate'] =					str(record.creation_date)
+					result['updateDate'] =						str(record.update_date)
+					result['accessDate'] =						str(record.access_date)
+					result['oldestUsedEncryptedVersion'] =		"---"
+
+				elif message == 'updateData':
+					user = db.Query(User).filter('username =', session.C).get()
+					user.update(parameters['parameters']['user'])
+
+					for recordParameter in parameters['parameters']['records']:
+						logging.info('reference =' + recordParameter['record']['reference'])
+						record = db.Query(Record).ancestor(user).filter('reference =', recordParameter['record']['reference']).get()
+						recordVersion = db.Query(RecordVersion).ancestor(record).get()
+						
+						recordVersion.update(recordParameter)
+
+						recordVersion.put()
+						recordVersion.parent().put()
+
+					user.put();
+
+					result['lock'] = user.lock
+					result['result'] = 'done'
+
+			 	elif message == 'deleteRecords':
+					user = db.Query(User).filter('username =', session.C).get()
+					user.update(parameters['parameters']['user'])
+
+					for recordReference in parameters['parameters']['recordReferences']:
+						record = db.Query(Record).ancestor(user).filter('reference =', recordReference).get()
+						#recordVersion = db.Query(RecordVersion).ancestor(record).get()
+						
+						db.delete(db.Query(RecordVersion).ancestor(record))
+						record.delete()
+						
+					user.put()
+
+					result['lock'] = user.lock
+					result['result'] = 'done'
+
+				elif message == 'deleteUser':
+					user = db.Query(User).filter('username =', session.C).get()
+					db.delete(db.Query(RecordVersion).ancestor(user))
+					db.delete(db.Query(Record).ancestor(user))
+					user.delete()
+
+				elif message == 'addNewOneTimePassword':
+					user = db.Query(User).filter('username =', session.C).get()
+					user.update(parameters['parameters']['user'])
+
+					oneTimePassword = OneTimePassword(parent=user)
+					oneTimePassword.update(parameters['parameters']['oneTimePassword'], "ACTIVE")
+					oneTimePassword.put()
+					
+					user.put()
+
+					result['lock'] = user.lock
+					result['result'] = 'done'
+
+				elif message == 'updateOneTimePasswords':
+					user = db.Query(User).filter('username =', session.C).get()
+					user.update(parameters['parameters']['user'])
+
+					validOtpReferences = parameters['parameters']['oneTimePasswords']
+					for currentOtp in db.Query(OneTimePassword).ancestor(user):
+						if currentOtp.reference in validOtpReferences:
+							pass
+						else:
+							currentOtp.delete()
+
+					user.put()
+
+					result['result'] = user.lock
+
+				elif message == 'getOneTimePasswordsDetails':
+					pass
+
+				elif message == 'getLoginHistory':
+					result["result"] = []
+
+				elif message == 'upgradeUserCredentials':
+					user = db.Query(User).filter('username =', session.C).get()
+
+					user.updateCredentials(parameters['parameters']['credentials'])
+					user.update(parameters['parameters']['user'])
+
+					for oneTimePasswordReference in parameters['parameters']['oneTimePasswords']:
+						oneTimePassword = db.Query(OneTimePassword).ancestor(user).filter("reference =", oneTimePasswordReference).get()
+						
+						if oneTimePassword != None:
+							oneTimePassword.data = parameters['parameters']['oneTimePasswords'][oneTimePasswordReference]
+							oneTimePassword.put()
+
+					user.put()
+					
+					result['lock'] = user.lock
+					result['result'] = 'done'
+					
+					"""
+					$user = new user();
+					$user->Get($_SESSION["userId"]);
+
+					$otp = new onetimepassword();
+					
+					updateUserCredentials($parameters["parameters"]["credentials"], $user);
+					updateUserData($parameters["parameters"]["user"], $user);
+
+					$otpList = $parameters["parameters"]["oneTimePasswords"];
+					foreach($otpList as $otpReference=>$otpData) {
+						$otpList = $otp->GetList(array(array("reference", "=", $otpReference)));
+						$currentOtp = $otpList[0];
+						$currentOtp->data = $otpData;
+						$currentOtp->Save();
+					}
+
+					$user->Save();
+
+					$result["lock"] = $user->lock;
+					$result["result"] = "done";
+					"""
+
+					#=============================================================
+
+					"""
+					java.util.Map	result;
+		
+					try {
+						java.util.Map	credentials;
+		
+						if (someParameters.get("credentials") != null) {
+							credentials = (java.util.Map)someParameters.get("credentials");
+						} else {
+							credentials = someParameters;
+						}
+
+						aUser.setUsername((java.lang.String)credentials.get("C"));
+						aUser.setSrpS((java.lang.String)credentials.get("s"));
+						aUser.setSrpV((java.lang.String)credentials.get("v"));
+						aUser.setVersion((java.lang.String)credentials.get("version"));
+
+						if (someParameters.get("user") != null) {
+							com.clipperz.dataModel.EncoderHelper.updateWithMap(aUser, (java.util.Map)someParameters.get("user"));
+						}
+
+						if (someParameters.get("oneTimePasswords") != null) {
+							java.util.Map	updatedOneTimePasswords;
+							java.util.List	usersOneTimePasswords;
+							int i,c;
+				
+							updatedOneTimePasswords = (java.util.Map)someParameters.get("oneTimePasswords");
+							usersOneTimePasswords = com.clipperz.dataModel.OneTimePassword.oneTimePasswordsForUser(this.user());
+							c = usersOneTimePasswords.size();
+							for (i=0; i<c; i++) {
+								com.clipperz.dataModel.OneTimePassword	currentOneTimePassword;
+					
+								currentOneTimePassword = (com.clipperz.dataModel.OneTimePassword)usersOneTimePasswords.get(i);
+					
+								if (updatedOneTimePasswords.get(currentOneTimePassword.getReference()) != null) {
+									currentOneTimePassword.setData((java.lang.String)updatedOneTimePasswords.get(currentOneTimePassword.getReference()));
+								}
+							}
+						}
+			
+						result = new java.util.Hashtable();
+						this.dataContext().commitChanges();
+						result.put("lock", this.user().getNewLock());
+						result.put("result", "done");
+					} catch(java.lang.Exception exception) {
+						this.dataContext().rollbackChanges();
+						logger.error(exception);
+						throw exception;
+					}
+		
+					return result;
+					"""
+
+				elif message == 'echo':
+					result['result'] = parameters;
+
+			else:
+				result['error'] = "Wrong shared secret!"
+
+		#----------------------------------------------------------------------
+
+		elif method == 'logout':
+			result['method'] = 'logout'
+
+		#----------------------------------------------------------------------
+
+		else:
+			result['method'] = 'PRRRRRR'
+
+		#----------------------------------------------------------------------
+
+		self.saveSession(session)
+		self.response.out.write(simplejson.dumps(result))
+
+	#==========================================================================
+
+	def addNewRecords (self, aSession, aUser, someParameters):
+		result = {}
+		
+		#user = db.Query(User).filter('username =', aSession.C).get()
+		aUser.update(someParameters['parameters']['user'])
+		
+		for recordParameter in someParameters['parameters']['records']:
+			record = Record(parent=aUser)
+			record.put()
+			recordVersion = RecordVersion(parent=record)
+			recordVersion.put()
+			
+			recordVersion.update(recordParameter)
+
+			record.put()
+			recordVersion.put()
+
+		aUser.put();
+
+		result['lock'] = aUser.lock
+		result['result'] = 'done'
+		
+		return result
+	
+	#==========================================================================
+	
+	def getSession(self):
+		#logging.info(">>> getSession (%d) => %s" % (db.Query(Session).count(), str(map(lambda v: v.sessionId, db.Query(Session).fetch(100)))) )
+		result = None
+		try:
+			sessionId = self.request.cookies['sessionId']
+		except:
+			sessionId = None
+
+		#logging.info("wannabe sessionId: " + str(sessionId))
+		
+		if sessionId != None:
+			#query = db.Query(Session)
+			#query.filter('sessionId =', sessionId)
+
+			#result = query.get()
+			
+			#result = db.Query(Session).filter('sessionId =', str(sessionId)).filter('access_date >', (datetime.datetime.utcnow() - sessionTimeout)).get()
+			result = db.Query(Session).filter('sessionId =', str(sessionId)).get()
+			#logging.info("searching session on datastore. Found: " + str(result))
+
+		if result == None:
+			sessionId = str(uuid.uuid4())
+			#logging.info("creating a new session with sessionId=" + str(sessionId))
+			result = Session(sessionId=sessionId)
+
+		result.access_date = datetime.datetime.utcnow()
+		result.put()
+		
+		#logging.info("<<< getSession (%d)" % db.Query(Session).count())
+		
+		return result
+
+	#==========================================================================
+
+	def saveSession(self, aSession):
+		#logging.info(">>> saveSession (%d)" % db.Query(Session).count())
+		#self.response.set_cookie('sessionId', aSession.sessionId, max_age=360, path='/', domain='example.org', secure=True)
+		aSession.put()
+		self.response.headers.add_header('Set-Cookie', 'sessionId=' + str(aSession.sessionId), path='/')
+		self.cleanOldSessions()
+		#logging.info("<<< saveSession (%d)" % db.Query(Session).count())
+
+	#==========================================================================
+
+	def cleanOldSessions(self):
+		query = db.Query(Session).filter('accessDate <', (datetime.datetime.utcnow() - sessionTimeout))
+
+		expiredSessions = query.count();
+		if expiredSessions != 0:
+			#logging.info("deleting %d sessions" % expiredSessions)
+			pass
+			
+		"""
+		try:
+			db.delete(query)
+		except Exception, exception:
+			logging.error("some issues raised while deleting the expired sessions")
+			logging.error("exception type: " + str(type(exception)))
+			logging.error("exception: " + str(exception))
+		"""
+		pass
+
+#==============================================================================
+
+def main():
+	application = webapp.WSGIApplication([('/xhr', XHR), ('/dump', XHR), ('/.*', MainPage)], debug=True)
+	wsgiref.handlers.CGIHandler().run(application)
+
+if __name__ == "__main__":
+	main()
+
--
cgit v0.9.0.2