def edit_handler(json, thread=None): try: target_id = json["post_id"] if not thread: thread = thread_load(json["thread_id"]) if not thread: return False, schema.error(7, "Requested thread does not exist.") if target_id == 1: target = thread else: target = thread["replies"][index_reply(thread["replies"], target_id)] if not user_is_admin(json["user"]): if json["user"] != target["author"]: return False, schema.error( 10, "non-admin attempt to edit another user's message") elif (time() - target["created"]) > 86400: return False, schema.error( 9, "message is too old to edit (24hr limit)") return True, target except IndexError: return False, schema.error( 3, "post_id out of bounds for requested thread")
def user_authcheck(auth_hash): if not auth_hash: return False, schema.error(3, "auth_hash may not be empty") elif len(auth_hash) != 64: return False, schema.error(4, "Client error: invalid SHA-256 hash.") return True, True
def user_quipcheck(quip): if not quip: return True, True elif contains_nonspaces(quip): return False, schema.error( 4, "Quip cannot contain whitespace chars besides spaces.") elif len(quip) > 120: return False, schema.error(4, "Quip is too long (max 120 chars)") return True, True
def user_namecheck(name): if not name: return False, schema.error(4, "Username may not be empty.") elif contains_nonspaces(name): return False, schema.error( 4, "Username cannot contain whitespace chars besides spaces.") elif not name.strip(): return False, schema.error( 4, "Username must contain at least one non-space character") elif len(name) > 24: return False, schema.error(4, "Username is too long (max 24 chars)") return True, True
def user_biocheck(bio): if not bio: return True, True elif len(bio) > 4096: return False, schema.error(4, "Bio is too long (max 4096 chars)") return True, True
def handle(self): try: request = json.loads(str(self.rfile.read(), "utf8")) endpoint = request.get("method") if endpoint not in endpoints.endpoints: return self.reply(schema.error(2, "Invalid endpoint")) # check to make sure all the arguments for endpoint are provided elif any( [key not in request for key in endpoints.endpoints[endpoint]]): return self.reply( schema.error( 3, "{} requires: {}".format( endpoint, ", ".join(endpoints.endpoints[endpoint])))) elif endpoint not in endpoints.authless: if not request.get("user"): return self.reply(schema.error(4, "No username provided.")) user = db.user_resolve(request["user"]) request["user"] = user if not user: return self.reply(schema.error(5, "User not registered")) elif endpoint != "check_auth" and not \ db.user_auth(user, request.get("auth_hash")): return self.reply(schema.error(6, "Authorization failed.")) # post_ids are always returned as integers, but for callers who # provide them as something else, try to convert them. if isinstance(request.get("post_id"), (float, str)): try: request["post_id"] = int(request["post_id"]) except Exception: return schema.error(3, "Non-numeric post_id") # exception handling is now passed to the endpoints; # anything unhandled beyond here is a code 1 self.reply(eval("endpoints." + endpoint)(request)) except json.decoder.JSONDecodeError as E: return self.reply(schema.error(0, str(E))) except Exception as E: return self.reply(schema.error(1, str(E)))
def thread_reply(ID, author, body): thread = thread_load(ID) if not thread: return schema.error(7, "Requested thread does not exist.") thread["reply_count"] += 1 thread["lastmod"] = time() if thread["replies"]: lastpost = thread["replies"][-1]["post_id"] else: lastpost = 1 reply = schema.reply(lastpost + 1, author, body) thread["replies"].append(reply) thread_dump(ID, thread) return reply
def user_register(auth_hash, name, quip, bio): if USERDB["namemap"].get(name): return schema.error(4, "Username taken.") for ok, error in [ user_namecheck(name), user_authcheck(auth_hash), user_quipcheck(quip), user_biocheck(bio) ]: if not ok: return error ID = uuid1().hex scheme = schema.user_internal(ID, auth_hash, name, quip, bio, False) USERDB.update({ID: scheme}) USERDB["namemap"].update({name: ID}) user_dbdump(USERDB) return scheme
def api_http_error(status, message, traceback, version): return json.dumps( schema.error(2, "HTTP error {}: {}".format(status, message)))
def wrapper(self, *args, **kwargs): response = None debug = app_config["debug"] try: connection = sqlite3.connect(dbname) # read in the body from the request to a string... if cherrypy.request.method == "POST": read_in = str(cherrypy.request.body.read(), "utf8") if not read_in: # the body may be empty, not all methods require input body = {} else: body = json.loads(read_in) if not isinstance(body, dict): raise BBJParameterError("Non-JSONObject input") # lowercase all of its top-level keys body = {key.lower(): value for key, value in body.items()} else: body = {} username = cherrypy.request.headers.get("User") auth = cherrypy.request.headers.get("Auth") if (username and not auth) or (auth and not username): raise BBJParameterError( "User or Auth was given without the other.") elif not username and not auth: user = db.anon else: user = db.user_resolve(connection, username) if not user: raise BBJUserError("User %s is not registered" % username) elif auth.lower() != user["auth_hash"].lower(): raise BBJException(5, "Invalid authorization key for user.") # api_methods may choose to bind a usermap into the thread_data # which will send it off with the response cherrypy.thread_data.usermap = {} value = function(self, body, connection, user) response = schema.response(value, cherrypy.thread_data.usermap) except BBJException as e: response = e.schema except json.JSONDecodeError as e: response = schema.error(0, str(e)) except Exception as e: error_id = uuid1().hex response = schema.error( 1, "Internal server error: code {} {}".format(error_id, repr(e))) with open("logs/exceptions/" + error_id, "a") as log: traceback.print_tb(e.__traceback__, file=log) log.write(repr(e)) print("logged code 1 exception " + error_id) finally: connection.close() return json.dumps(response)
def thread_load(json): thread = db.thread_load(json["thread_id"], not json.get("nomarkup")) if not thread: return schema.error(7, "Requested thread does not exist") return schema.response(thread, create_usermap(thread))
def __init__(self, code, description): self.schema = error(code, description) self.description = description self.code = code