import sys sys.path.append('lib') import re import cgi import urllib import simplejson from google.appengine.ext import webapp from google.appengine.ext import db from math import floor from time import time # Amazon AWS S3 import import S3 # Signature validation required libraries import import base64 import hashlib import oauth from Crypto.PublicKey import RSA from Crypto.Util import number # Amazon AWS parameters AWS_ACCESS_KEY_ID = AWS_SECRET_ACCESS_KEY = BUCKET_NAME = ''.join([AWS_ACCESS_KEY_ID.lower(), '.photoboardrepo']) # Local port; change if another process is running on 8080 PORT = '8080' class User(db.Model): personId = db.StringProperty() container = db.StringProperty() class RootHandler(webapp.RequestHandler): def get(self): self.response.headers['Content-Type'] = 'text/html; charset=utf-8' self.response.out.write('') self.response.out.write('') self.response.out.write(''.join(['Upload photo'])) self.response.out.write('
') self.response.out.write(''.join(['Fetch user photos'])) self.response.out.write('
') self.response.out.write(''.join(['Fetch group photos'])) self.response.out.write('
') self.response.out.write(''.join(['Fetch user tags'])) self.response.out.write('
') self.response.out.write(''.join(['Add photo tag'])) self.response.out.write('') self.response.out.write('') class TagsHandler(webapp.RequestHandler): def get(self): if not _isValidSignature(self): self.response.out.write('SIGNATURE INVALID') return personId = self.request.get('opensocial_owner_id') container = self.request.get('oauth_consumer_key') tags = getTagsForUser(container, personId) self.response.headers['Content-Type'] = 'text/plain; charset=utf-8' self.response.out.write(simplejson.dumps({'tags': list(set(tags))})) class PhotosHandler(webapp.RequestHandler): def get(self): if not _isValidSignature(self): self.response.out.write('SIGNATURE INVALID') return personId = self.request.get('opensocial_owner_id') container = self.request.get('oauth_consumer_key') photos = getPhotos(container, personId) self.response.headers['Content-Type'] = 'text/plain; charset=utf-8' self.response.out.write(simplejson.dumps({'resultsSet': photos})) def post(self): form = cgi.FieldStorage() peopleIds = urllib.unquote(form.getfirst('people')).split(',') if not _isValidSignature(self): self.response.out.write('SIGNATURE INVALID') return personId = self.request.get('opensocial_owner_id') container = self.request.get('oauth_consumer_key') photoSetCollection = [] for id in peopleIds: photoSet = {} photoSet['name'] = id photoSet['photos'] = getPhotos(container, id) if len(photoSet['photos']) > 0: photoSetCollection.append(photoSet) self.response.headers['Content-Type'] = 'text/plain; charset=utf-8' self.response.out.write(simplejson.dumps({'resultsCollection': photoSetCollection})) class PhotoHandler(webapp.RequestHandler): def get(self): match = re.search(r'^http://.*/photo/([\w\.]*?):([\w\.]*?):([\w\.]*)', urllib.unquote(self.request.uri)) if match: photo = getPhoto(match.group(1), match.group(2), match.group(3)) if photo: self.response.headers['Content-Type'] = photo['contentType'] self.response.out.write(photo['content']) def post(self): form = cgi.FieldStorage() if not form.has_key('file'): if not _isValidSignature(self): self.response.out.write('SIGNATURE INVALID') return personId = self.request.get('opensocial_owner_id') container = self.request.get('oauth_consumer_key') if form.getfirst('text'): textTag = urllib.unquote(form.getfirst('text')) match = re.search(r'^http://.*/photo/([\w\.]*?):([\w\.]*?):([\w\.]*)', urllib.unquote(self.request.uri)) if match: photo = getPhoto(match.group(1), match.group(2), match.group(3)) if photo: self.response.headers['Content-Type'] = 'text/plain; charset=utf-8' if 'textTag' in locals(): addTextTagToPhoto(photo, textTag) self.response.out.write('Text tag added successfully') else: fileItem = form['file'] personId = form.getfirst('personId') container = form.getfirst('container') self.response.headers['Content-Type'] = 'text/html; charset=utf-8' photo = createPhoto(container, personId, fileItem) if photo: self.response.out.write('Photo added.
') self.response.out.write(''.join(['
'])) def getUser(container, personId): user = User.get_or_insert(''.join([container, personId]), container=container, personId=personId) return user def getPhoto(container, personId, photoName): key = ''.join([container, personId, '_', photoName]) conn = S3.AWSAuthConnection(AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, True) response = conn.get(BUCKET_NAME, key) photo = None if response.http_response.status_code==200: if response.http_response.headers.has_key('content-type'): photo = { 'contentType': response.http_response.headers['content-type'], 'content': response.http_response.content, 'name': photoName, 'key': key } if response.object.metadata.has_key('tags'): photo['tags'] = response.object.metadata['tags'] else: photo['tags'] = '' return photo def getPhotos(container, personId): retArray = [] photos = getPhotosForUser(container, personId) if photos: for objt in photos: photo = {} photo['url'] = ''.join(['http://localhost:', PORT, '/photo/', container, ':', personId, ':', objt['name']]) photo['tags'] = objt['tags'] retArray.append(photo) return retArray def getPhotosForUser(container, personId): retArray = [] conn = S3.AWSAuthConnection(AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, True) listResponse = conn.list_bucket(BUCKET_NAME, {'prefix': ''.join([container, personId, '_'])}) if listResponse.entries: for entry in listResponse.entries: match = re.search(r'^.*?_(.*)$', entry.key) if match: getResponse = conn.get(BUCKET_NAME, entry.key) if getResponse.http_response.status_code==200: photo = { 'name': match.group(1), 'tags': [] } if getResponse.object.metadata.has_key('tags'): tags = getResponse.object.metadata['tags'] photo['tags'] = tags.split('|') retArray.append(photo) return retArray def getTagsForUser(container, personId): tags = [] photos = getPhotosForUser(container, personId) if photos: for objt in photos: for tag in objt['tags']: tags.append(tag) return tags def addTextTagToPhoto(photo, textTag): headers = { 'Content-Type': photo['contentType'] } if not photo['tags'] == '': headers['x-amz-meta-tags'] = ''.join([photo['tags'], '|', textTag]) else: headers['x-amz-meta-tags'] = textTag conn = S3.AWSAuthConnection(AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, True) conn.put(BUCKET_NAME, photo['key'], photo['content'], headers) def createPhoto(container, personId, fileItem): user = getUser(container, personId) name = ''.join([str(int(floor(time()))), fileItem.filename]) key = ''.join([container, personId, '_', name]) headers = { 'x-amz-acl':'public-read', 'Content-Type': fileItem.type, 'x-amz-meta-tags': '' } conn = S3.AWSAuthConnection(AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, True) conn.put(BUCKET_NAME, key, fileItem.file.read(), headers) return {'name': name} def _isValidSignature(self): # Code lab hack: # If the container is 'appengine' (e.g. app is running on localhost), return True if self.request.get('oauth_consumer_key') == 'appengine': return True # Construct a RSA.pubkey object exponent = 65537 public_key_str = """0x\ 00b1e057678343866db89d7dec2518\ 99261bf2f5e0d95f5d868f81d600c9\ a101c9e6da20606290228308551ed3\ acf9921421dcd01ef1de35dd3275cd\ 4983c7be0be325ce8dfc3af6860f7a\ b0bf32742cd9fb2fcd1cd1756bbc40\ 0b743f73acefb45d26694caf4f26b9\ 765b9f65665245524de957e8c547c3\ 58781fdfb68ec056d1""" public_key_long = long(public_key_str, 16) public_key = RSA.construct((public_key_long, exponent)) # Rebuild the message hash locally oauth_request = oauth.OAuthRequest(http_method=self.request.method, http_url=self.request.url, parameters=self.request.params.mixed()) message = '&'.join((oauth.escape(oauth_request.get_normalized_http_method()), oauth.escape(oauth_request.get_normalized_http_url()), oauth.escape(oauth_request.get_normalized_parameters()),)) local_hash = hashlib.sha1(message).digest() # Apply the public key to the signature from the remote host sig = base64.decodestring(urllib.unquote(self.request.params.mixed()["oauth_signature"])) remote_hash = public_key.encrypt(sig, '')[0][-20:] # Verify that the locally-built value matches the value from the remote server. if local_hash==remote_hash: return True else: return False