# coding=utf-8 import json import time from framework import * from database import * from post import * def api(self, path_split): if len(path_split) > 2: try: self.output = api_process(self, path_split) except APIError as e: self.output = api_error("error", str(e)) except UserError as e: self.output = api_error("failed", str(e)) except Exception as e: import sys import traceback exc_type, exc_value, exc_traceback = sys.exc_info() detail = ["%s : %s : %s : %s" % (os.path.basename( o[0]), o[1], o[2], o[3]) for o in traceback.extract_tb(exc_traceback)] self.output = api_error("exception", str(e), str(type(e)), detail) else: self.output = api_error("error", "No method specified") def api_process(self, path_split): formdata = self.formdata ip = self.ip t = time.time() method = path_split[2] #bans = ['181.72.116.62'] bans = [] if ip in bans: raise APIError("You have been blacklisted.") # with open('../api_log.txt', 'a') as f: # logstr = "[%s] %s: %s\n" % (formatTimestamp(t), ip, repr(path_split)) # f.write(logstr) values = {'state': 'success'} if method == 'boards': boards = FetchAll( 'SELECT dir, name, board_type, allow_images, allow_image_replies, maxsize FROM `boards` WHERE `secret`=0 ORDER BY `sort` ASC') values['boards'] = boards for board in values['boards']: board['board_type'] = board['board_type'] board['allow_images'] = board['allow_images'] board['allow_image_replies'] = board['allow_image_replies'] board['maxsize'] = board['maxsize'] elif method == 'last': data_limit = formdata.get('limit') data_since = formdata.get('since') limit = 10 since = 0 if data_limit: try: limit = int(data_limit) except ValueError: raise APIError("Limit must be numeric") if data_since: try: since = int(data_since) except ValueError: raise APIError("Since must be numeric") if limit > 50: raise APIError("Maximum limit is 50") sql = "SELECT posts.id, boards.dir, timestamp, timestamp_formatted, posts.name, tripcode, email, posts.subject, posts.message, file, file_size, image_height, image_width, thumb, thumb_width, thumb_height, parentid FROM posts INNER JOIN boards ON boardid = boards.id WHERE timestamp > %d AND IS_DELETED = 0 AND boards.secret = 0 ORDER BY timestamp DESC LIMIT %d" % ( since, limit) values['posts'] = FetchAll(sql) for post in values['posts']: post['id'] = post['id'] post['timestamp'] = post['timestamp'] post['parentid'] = post['parentid'] post['file_size'] = post['file_size'] post['image_width'] = post['image_width'] post['image_height'] = post['image_height'] post['thumb_width'] = post['thumb_width'] post['thumb_height'] = post['thumb_height'] post['message'] = post['message'] elif method == 'lastage': data_limit = formdata.get('limit') data_time = formdata.get('time', 0) if data_limit: try: limit = int(data_limit) except ValueError: raise APIError("Limit must be numeric") if limit > 30: raise APIError("Maximum limit is 30") threads = getLastAge(0, limit) threads += getLastAge(1, limit) threads = sorted(threads, key=lambda b:b['bumped'], reverse=True) if threads[0]['bumped'] > int(data_time): values['threads'] = threads else: values['threads'] = [] elif method == 'list': data_board = formdata.get('dir') data_offset = formdata.get('offset') data_limit = formdata.get('limit') data_replies = formdata.get('replies') offset = 0 limit = 10 numreplies = 2 if not data_board: raise APIError("Missing parameters") if data_limit: try: limit = int(data_limit) except ValueError: raise APIError("Limit must be numeric") if data_offset: try: offset = int(data_offset) except ValueError: raise APIError("Offset must be numeric") if data_replies: try: numreplies = int(data_replies) except ValueError: raise APIError("Replies must be numeric") if data_replies and limit > 30: raise APIError("Maximum limit is 30") board = setBoard(data_board) #sql = "SELECT id, timestamp, bumped, timestamp_formatted, name, tripcode, email, subject, message, file, thumb FROM posts WHERE boardid = %s AND parentid = 0 AND IS_DELETED = 0 ORDER BY bumped DESC LIMIT %d" % (board['id'], limit) sql = "SELECT p.id, p.timestamp, p.bumped, p.expires, p.expires_formatted, p.timestamp_formatted, p.name, p.tripcode, p.email, p.subject, p.message, p.file, p.file_size, p.image_width, p.image_height, p.thumb, p.thumb_height, p.thumb_width, p.locked, coalesce(x.count,0) AS total_replies, coalesce(x.files,0) AS total_files FROM `posts` AS p LEFT JOIN (SELECT parentid, count(1) as count, count(nullif(file, '')) as files FROM `posts` WHERE boardid = %(board)s GROUP BY parentid) AS x ON p.id=x.parentid WHERE p.parentid = 0 AND p.boardid = %(board)s AND p.IS_DELETED = 0 ORDER BY `bumped` DESC LIMIT %(limit)d OFFSET %(offset)d" % { 'board': board["id"], 'limit': limit, 'offset': offset} threads = FetchAll(sql) if numreplies: for thread in threads: lastreplies = FetchAll("SELECT id, timestamp, timestamp_formatted, name, tripcode, email, subject, message, file, file_size, image_height, image_width, thumb, thumb_width, thumb_height, IS_DELETED FROM `posts` WHERE parentid = %s AND boardid = %s ORDER BY `timestamp` DESC LIMIT %s", (thread['id'], board['id'], numreplies)) lastreplies = lastreplies[::-1] thread['id'] = thread['id'] thread['timestamp'] = thread['timestamp'] thread['bumped'] = thread['bumped'] thread['expires'] = thread['expires'] thread['total_replies'] = thread['total_replies'] thread['total_files'] = thread['total_files'] thread['file_size'] = thread['file_size'] thread['image_width'] = thread['image_width'] thread['image_height'] = thread['image_height'] thread['thumb_width'] = thread['thumb_width'] thread['thumb_height'] = thread['thumb_height'] thread['locked'] = thread['locked'] thread['replies'] = [] for post in lastreplies: post['IS_DELETED'] = post['IS_DELETED'] post['id'] = post['id'] post['timestamp'] = post['timestamp'] if post['IS_DELETED']: empty_post = {'id': post['id'], 'IS_DELETED': post['IS_DELETED'], 'timestamp': post['timestamp'], } thread['replies'].append(empty_post) else: post['file_size'] = post['file_size'] post['image_width'] = post['image_width'] post['image_height'] = post['image_height'] post['thumb_width'] = post['thumb_width'] post['thumb_height'] = post['thumb_height'] post['message'] = post['message'] thread['replies'].append(post) values['threads'] = threads elif method == 'thread': data_board = formdata.get('dir') data_threadid = formdata.get('id') data_threadts = formdata.get('ts') data_offset = formdata.get('offset') data_limit = formdata.get('limit') data_striphtml = formdata.get('nohtml') striphtml = False offset = 0 limit = 1000 if not data_board or (not data_threadid and not data_threadts): raise APIError("Missing parameters") if data_limit: try: limit = int(data_limit) except ValueError: raise APIError("Limit must be numeric") if data_offset: try: offset = int(data_offset) except ValueError: raise APIError("Offset must be numeric") if data_striphtml: if int(data_striphtml) == 1: striphtml = True board = setBoard(data_board) search_field = 'id' search_val = 0 try: search_val = int(data_threadid) except (ValueError, TypeError): pass try: search_val = int(data_threadts) search_field = 'timestamp' except (ValueError, TypeError): pass if not search_val: raise APIError("No thread ID") op_post = FetchOne("SELECT id, timestamp, subject, locked FROM posts WHERE `%s` = '%d' AND boardid = '%s' AND parentid = 0" % ( search_field, search_val, board["id"])) if not op_post: raise APIError("Not a thread") values['id'] = op_post['id'] values['timestamp'] = op_post['timestamp'] values['subject'] = op_post['subject'] values['locked'] = op_post['locked'] total_replies = FetchOne("SELECT COUNT(1) AS count FROM posts WHERE boardid = %s AND parentid = %s", (board["id"], values['id']))["count"] values['total_replies'] = total_replies sql = "SELECT id, parentid, timestamp, timestamp_formatted, name, tripcode, email, subject, message, file, file_size, image_width, image_height, thumb, thumb_width, thumb_height, IS_DELETED FROM posts WHERE boardid = %s AND (parentid = %s OR id = %s) ORDER BY id ASC LIMIT %s OFFSET %s" sqlv = (board['id'], values['id'], values['id'], limit, offset) posts = FetchAll(sql, sqlv) values['posts'] = [] for post in posts: post['IS_DELETED'] = int(post['IS_DELETED']) post['id'] = int(post['id']) post['parentid'] = int(post['parentid']) post['timestamp'] = int(post['timestamp']) if post['IS_DELETED']: empty_post = {'id': post['id'], 'IS_DELETED': post['IS_DELETED'], 'parentid': post['parentid'], 'timestamp': post['timestamp'], } values['posts'].append(empty_post) else: post['file_size'] = post['file_size'] post['image_width'] = post['image_width'] post['image_height'] = post['image_height'] post['thumb_width'] = post['thumb_width'] post['thumb_height'] = post['thumb_height'] post['message'] = post['message'] if striphtml: post['message'] = post['message'].replace("
", " ") post['message'] = re.compile( r"<[^>]*?>", re.DOTALL | re.IGNORECASE).sub("", post['message']) values['posts'].append(post) elif method == 'get': data_board = formdata.get('dir') data_parentid = formdata.get('thread') data_postid = formdata.get('id') data_postnum = formdata.get('num') if not data_board and (not data_postid or (not data_postnum and not data_parentid)): raise APIError("Missing parameters") board = setBoard(data_board) postid = 0 if data_postnum: data_postid = getID(data_parentid, data_postid) try: postid = int(data_postid) except ValueError: raise APIError("Post ID must be numeric") post = FetchOne("SELECT id, parentid, timestamp, timestamp_formatted, name, tripcode, email, subject, message, file, file_size, image_width, image_height, thumb, thumb_width, thumb_height, IS_DELETED FROM posts WHERE `id` = %s AND boardid = %s" (postid, board["id"])) if not post: raise APIError("Post ID cannot be found") values['posts'] = [] post['IS_DELETED'] = post['IS_DELETED'] post['id'] = post['id'] post['parentid'] = post['parentid'] post['timestamp'] = post['timestamp'] if post['IS_DELETED']: empty_post = {'id': post['id'], 'IS_DELETED': post['IS_DELETED'], 'parentid': post['parentid'], 'timestamp': post['timestamp'], } values['posts'].append(empty_post) else: post['file_size'] = post['file_size'] post['image_width'] = post['image_width'] post['image_height'] = post['image_height'] post['thumb_width'] = post['thumb_width'] post['thumb_height'] = post['thumb_height'] post['message'] = post['message'] values['posts'].append(post) elif method == 'delete': data_board = formdata.get('dir') data_postid = formdata.get('id') data_imageonly = formdata.get('imageonly') data_password = formdata.get('password') if not data_board or not data_postid or not data_password: raise APIError("Missing parameters") imageonly = False board = setBoard(data_board) try: postid = int(data_postid) except ValueError: raise APIError("Post ID must be numeric") if data_imageonly and data_imageonly == 1: imageonly = True deletePosts(board['dir'], postid, imageonly, data_password) elif method == 'post': boarddir = formdata.get('board') if not boarddir: raise APIError("Missing parameters") parent = formdata.get('parent') trap1 = formdata.get('name', '') trap2 = formdata.get('email', '') name = formdata.get('fielda', '') email = formdata.get('fieldb', '') subject = formdata.get('subject', '') message = formdata.get('message', '') file = formdata.get('file') file_original = formdata.get('file_original') spoil = formdata.get('spoil') oek_file = formdata.get('oek_file') password = formdata.get('password', '') noimage = formdata.get('noimage') mobile = ("mobile" in formdata) # call post function (post_url, ttaken, postid) = self.make_post(ip, boarddir, parent, trap1, trap2, name, email, subject, message, file, file_original, spoil, oek_file, password, noimage, mobile) values['post_url'] = post_url values['time_taken'] = ttaken values['post_id'] = postid elif method == 'styles': values['bbs_styles'] = Settings.TXT_STYLES values['ib_styles'] = Settings.STYLES values['bbs_styles_default'] = Settings.TXT_STYLES_DEFAULT values['ib_styles_default'] = Settings.STYLES_DEFAULT elif method == 'newThreads': data_limit = formdata.get('limit') limit = 30 if data_limit: try: limit = int(data_limit) except ValueError: raise APIError("Limit must be numeric") if limit > 30: raise APIError("Maximum limit is 30") threads = getNewThreads(limit) values['threads'] = threads elif method == "blotter": latest_news = FetchAll("SELECT `timestamp`, `message`, `timestamp_formatted` FROM `news` WHERE `type` = '2' ORDER BY `timestamp` DESC LIMIT %s", (Settings.HOME_NEWS,)) values["news"] = latest_news elif method == 'boardsExtra': boards = FetchAll('SELECT dir, name, longname, subname, postarea_desc, postarea_extra, anonymous, subject, message, disable_name, disable_subject, allow_spoilers, allow_oekaki, numthreads, board_type, allow_images, allow_image_replies, maxsize FROM `boards` WHERE `secret`=0 ORDER BY `sort` ASC') values['boards'] = boards for board in values['boards']: board['board_type'] = board['board_type'] board['allow_images'] = board['allow_images'] board['allow_image_replies'] = board['allow_image_replies'] board['disable_name'] = board['disable_name'] board['disable_subject'] = board['disable_subject'] board['allow_spoilers'] = board['allow_spoilers'] board['allow_oekaki'] = board['allow_oekaki'] board['numthreads'] = board['numthreads'] board['maxsize'] = board['maxsize'] else: raise APIError("Invalid method") values['time'] = int(t) #values['time_taken'] = time.time() - t return json.dumps(values, sort_keys=True, separators=(',', ':')) def api_error(errtype, msg, type=None, detail=None): values = {'state': errtype, 'message': msg} if type: values['type'] = type if detail: values['detail'] = detail return json.dumps(values) class APIError(Exception): pass