#!/usr/bin/python
# Copyright 2012 Google Inc. All Rights Reserved.
# Author: mrdmnd@ (Matt Redmond)
# Based off of code in //depot/google3/experimental/mobile_gwp
"""Code to transport profile data between a user's machine and the CWP servers.
Pages:
"/": the main page for the app, left blank so that users cannot access
the file upload but left in the code for debugging purposes
"/upload": Updates the datastore with a new file. the upload depends on
the format which is templated on the main page ("/")
input includes:
profile_data: the zipped file containing profile data
board: the architecture we ran on
chromeos_version: the chromeos_version
"/serve": Lists all of the files in the datastore. Each line is a new entry
in the datastore. The format is key~date, where key is the entry's
key in the datastore and date is the file upload time and date.
(Authentication Required)
"/serve/([^/]+)?": For downloading a file of profile data, ([^/]+)? means
any character sequence so to download the file go to
'/serve/$key' where $key is the datastore key of the file
you want to download.
(Authentication Required)
"/del/([^/]+)?": For deleting an entry in the datastore. To use go to
'/del/$key' where $key is the datastore key of the entry
you want to be deleted form the datastore.
(Authentication Required)
TODO: Add more extensive logging"""
import cgi
import logging
import md5
import urllib
from google.appengine.api import users
from google.appengine.ext import db
from google.appengine.ext import webapp
from google.appengine.ext.webapp.util import run_wsgi_app
logging.getLogger().setLevel(logging.DEBUG)
class FileEntry(db.Model):
profile_data = db.BlobProperty() # The profile data
date = db.DateTimeProperty(auto_now_add=True) # Date it was uploaded
data_md5 = db.ByteStringProperty() # md5 of the profile data
board = db.StringProperty() # board arch
chromeos_version = db.StringProperty() # ChromeOS version
class MainPage(webapp.RequestHandler):
"""Main page only used as the form template, not actually displayed."""
def get(self, response=''): # pylint: disable-msg=C6409
if response:
self.response.out.write('<html><body>')
self.response.out.write("""<br>
<form action="/upload" enctype="multipart/form-data" method="post">
<div><label>Profile Data:</label></div>
<div><input type="file" name="profile_data"/></div>
<div><label>Board</label></div>
<div><input type="text" name="board"/></div>
<div><label>ChromeOS Version</label></div>
<div><input type="text" name="chromeos_version"></div>
<div><input type="submit" value="send" name="submit"></div>
</form>
</body>
</html>""")
class Upload(webapp.RequestHandler):
"""Handler for uploading data to the datastore, accessible by anyone."""
def post(self): # pylint: disable-msg=C6409
"""Takes input based on the main page's form."""
getfile = FileEntry()
f1 = self.request.get('profile_data')
getfile.profile_data = db.Blob(f1)
getfile.data_md5 = md5.new(f1).hexdigest()
getfile.board = self.request.get('board')
getfile.chromeos_version = self.request.get('chromeos_version')
getfile.put()
self.response.out.write(getfile.key())
#self.redirect('/')
class ServeHandler(webapp.RequestHandler):
"""Given the entry's key in the database, output the profile data file. Only
accessible from @google.com accounts."""
def get(self, resource): # pylint: disable-msg=C6409
if Authenticate(self):
file_key = str(urllib.unquote(resource))
request = db.get(file_key)
self.response.out.write(request.profile_data)
class ListAll(webapp.RequestHandler):
"""Displays all files uploaded. Only accessible by @google.com accounts."""
def get(self): # pylint: disable-msg=C6409
"""Displays all information in FileEntry, ~ delimited."""
if Authenticate(self):
query_str = 'SELECT * FROM FileEntry ORDER BY date ASC'
query = db.GqlQuery(query_str)
delimiter = '~'
for item in query:
display_list = [item.key(), item.date, item.data_md5, item.board,
item.chromeos_version]
str_list = [cgi.escape(str(i)) for i in display_list]
self.response.out.write(delimiter.join(str_list) + '</br>')
class DelEntries(webapp.RequestHandler):
"""Deletes entries. Only accessible from @google.com accounts."""
def get(self, resource): # pylint: disable-msg=C6409
"""A specific entry is deleted, when the key is given."""
if Authenticate(self):
fkey = str(urllib.unquote(resource))
request = db.get(fkey)
if request:
db.delete(fkey)
def Authenticate(webpage):
"""Some urls are only accessible if logged in with a @google.com account."""
user = users.get_current_user()
if user is None:
webpage.redirect(users.create_login_url(webpage.request.uri))
elif user.email().endswith('@google.com'):
return True
else:
webpage.response.out.write('Not Authenticated')
return False
def main():
application = webapp.WSGIApplication(
[
('/', MainPage),
('/upload', Upload),
('/serve/([^/]+)?', ServeHandler),
('/serve', ListAll),
('/del/([^/]+)?', DelEntries),
],
debug=False)
run_wsgi_app(application)
if __name__ == '__main__':
main()