You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
153 lines
5.6 KiB
153 lines
5.6 KiB
#!/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()
|
|
|