import os, sha, datetime from google.appengine.ext import webapp from google.appengine.ext.webapp import template from django.utils import simplejson import models from utils import slugify, hashpassword, getsessionkey, \ youtube_search, youtube_one class RQ(webapp.RequestHandler): def flash(self, message): pass # FIXME def check_valid(self, name): "Call at the top of all functions that take name, to check no URL fuckery" if name != slugify(name): self.flash("FIXME") self.redirect("/") return def fail_if_name_not_exist(self, name): persons = models.Person.all().filter("name =", name) if persons.count() == 0: raise "name does not exist" return return persons[0] def fail_if_name_exists(self, name): persons = models.Person.all().filter("name =", name) if persons.count() != 0: self.redirect("/%s/edit" % name) return def render(self, tmpl, context={}, cookies={}, session_cookies={}): path = os.path.join(os.path.dirname(__file__), "templates", tmpl + ".html") self.response.out.write(template.render(path, context)) class FrontPage(RQ): def get(self): self.render("FrontPage") class Bounce(RQ): def get(self): name = self.request.get("n", "") if not name: self.redirect("/") return name = slugify(name) # does this name exist? persons = models.Person.all().filter("name =", name) if persons.count() == 0: self.redirect("/%s/new" % name) return # they do: do they have any videos? person = persons[0] videos = models.Video.all().filter("person =", person) # no they don't if videos.count() == 0: self.redirect("/%s/edit" % name) return # yes they do self.redirect("/%s" % name) class Count(RQ): def get(self): name = self.request.get("n", "") if not name: self.response.out.write(simplejson.dumps({"name":"(none)","count":"0"})) return name = slugify(name) # does this name exist? persons = models.Person.all().filter("name =", name) if persons.count() == 0: self.response.out.write(simplejson.dumps({"name":name,"count":"new"})) return # they do: do they have any videos? person = persons[0] videos = models.Video.all().filter("person =", person) self.response.out.write(simplejson.dumps({"name":name,"count":videos.count()})) return class Player(RQ): def get(self, name): self.check_valid(name) person = self.fail_if_name_not_exist(name) self.render("Player", {"person": person}) class Edit(RQ): def get(self, name): self.check_valid(name) person = self.fail_if_name_not_exist(name) self.render("ListLocked", {"person": person, "loggedin": False}) def post(self, name): self.check_valid(name) person = self.fail_if_name_not_exist(name) # lots of things post to here. they are: # 1. unlocking the edit form (action=unlock) # params: pass # action = self.request.get("action", "") if not action: # fail. this shouldn't happen self.redirect("/%s/edit" % name) return if action == "unlock": return self.unlock(person) elif action == "search": return self.search(person) elif action == "add": return self.add(person) elif action == "delete": return self.delete(person) else: # fail. this shouldn't happen self.redirect("/%s/edit" % name) return def unlock(self, person): password = self.request.get("pass", "") if hashpassword(password) != person.password: self.render("ListLocked", {"person": person, "error": "Your password was wrong.", "loggedin": False}) return # set a session key on the user person.session_key = getsessionkey() person.session_key_timeout = datetime.datetime.now() + datetime.timedelta(minutes=60) person.put() # and render an editable list self.render("ListUnlocked", { "person": person, "loggedin": True }) def search(self, person): # check session key sessionkey = self.request.get("sessionkey","") if sessionkey != person.session_key or \ datetime.datetime.now() > person.session_key_timeout: self.render("ListLocked", {"person": person, "error": "Please log in again.", "loggedin": False}) return query = self.request.get("q","") if query: results = youtube_search(query) else: results = [] self.render("ListSearch", {"person": person, "results": results, "loggedin": True }) def add(self, person): # check session key sessionkey = self.request.get("sessionkey","") if sessionkey != person.session_key: self.render("ListLocked", {"person": person, "error": "Please log in again (for security)", "loggedin": False}) return if datetime.datetime.now() > person.session_key_timeout: self.render("ListLocked", {"person": person, "error": "Please log in again (it has been a while since you did it)", "loggedin": False}) return youtube = self.request.get("youtube", "") if not youtube: self.redirect("/") return # do they already have this video on the list? match = models.Video.all().filter("youtube =", youtube) if match.count() == 0: ytdata = youtube_one(youtube) video = models.Video(name=ytdata["name"], youtube=youtube, person=person) video.put() self.render("ListUnlocked", { "person": person, "loggedin": True }) def delete(self, person): # check session key sessionkey = self.request.get("sessionkey","") if sessionkey != person.session_key or \ datetime.datetime.now() > person.session_key_timeout: self.render("ListLocked", {"person": person, "error": "Please log in again.", "loggedin": False}) return youtube = self.request.get("youtube", "") if not youtube: self.redirect("/") return # do they already have this video on the list? match = models.Video.all().filter("youtube =", youtube) if match.count() != 0: match[0].delete() self.render("ListUnlocked", { "person": person, "loggedin": True }) class New(RQ): def get(self, name): self.check_valid(name) self.fail_if_name_exists(name) # generate random password import random from wordlist import wordlist suggested = "%s %s %s" % ( random.choice(wordlist),random.choice(wordlist),random.choice(wordlist) ) self.render("New", {"name": name, "suggested": suggested}) def post(self, name): # the form from GET does a POST back to here self.check_valid(name) self.fail_if_name_exists(name) # generate random password import random from wordlist import wordlist suggested = "%s %s %s" % ( random.choice(wordlist),random.choice(wordlist),random.choice(wordlist) ) # name is new: check that passwords match pass1 = self.request.get("pass1","") pass2 = self.request.get("pass2","") if not pass1: self.render("New", {"name": name, "suggested": suggested, "error": "If you choose your own password, it can't be blank"}) return if pass1 != pass2: self.render("New", {"name": name, "suggested": suggested, "error": "When choosing your own password, please enter the same one twice"}) # all OK. Create account and forward to list page me = models.Person(name=name, password=hashpassword(pass1)) me.put() self.redirect("/%s/edit" % name) class Verify(RQ): def get(self): u = self.request.get("u","") p = self.request.get("hp","") self.response.headers["Content-Type"] = "application/json" if u and p: people = models.Person.all().filter("name =", u) if people.count() > 0: person = people[0] if person.password == p: self.response.out.write('{"verified":"yes"}\n') return self.response.out.write('{"verified":"no"}\n') class Upload(RQ): def json_error(self, message): self.response.clear() self.response.set_status(500) self.response.out.write(simplejson.dumps({"error": message}) + "\n") def post(self): self.response.headers["Content-Type"] = "application/json" # confirm it's valid JSON if not self.request.body: return self.json_error("No body specified") try: json = simplejson.loads(self.request.body) except: return self.json_error("Body was invalid JSON") if not isinstance(json, dict): return self.json_error("Supplied data was invalid (not a dict)") username = json.get("username", None) if not username: return self.json_error("Supplied data was invalid (no username)") if not isinstance(username, basestring): return self.json_error("Supplied data was invalid (username not a string)") password = json.get("hashed_password", None) if not password: return self.json_error("Supplied data was invalid (no hashed_password)") if not isinstance(password, basestring): return self.json_error("Supplied data was invalid (hashed_password not a string)") items = json.get("videos", None) if not isinstance(items, list): return self.json_error("Supplied data was invalid (no videos)") if [x for x in items if not isinstance(x, dict)]: return self.json_error("Supplied data was invalid (videos not a list of dicts)") if not items: # list is empty return self.json_error("Supplied data was invalid (no videos listed)") if len(items) > 100: return self.json_error("Supply no more than 100 videos at once") people = models.Person.all().filter("name =", username) if people.count() == 0: return self.json_error("No such person") person = people[0] if person.password != password: return self.json_error("Password invalid") video_objects = [] count = 0 for d in items: count += 1 try: name = d["name"] youtube = d["ytid"] # check doesn't already exist! matches = models.Video.all().filter( "youtube =",youtube).filter( "person =", person) if matches.count() == 0: # it doesn't; create it video_objects.append(models.Video(name=name, youtube=youtube, person=person)) except: return self.json_error("Supplied data was invalid (video %s had invalid data)" % count) for video in video_objects: video.put() self.response.out.write(simplejson.dumps({"ok": "ok"}))