# coding=utf-8
import json
import _mysql
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, e:
self.output = api_error("error", e.message)
except UserError, e:
self.output = api_error("failed", e.message)
except Exception, 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.environ["REMOTE_ADDR"]
t = time.time()
method = path_split[2]
values = {'state': 'success'}
validated = False
staff_account = None
token = formdata.get("token")
if token:
staff_account = validateSession(token)
if not staff_account:
self.output = api_error("error", "Session expired")
if staff_account:
validated = True
if 'session_id' in staff_account:
renewSession(staff_account['session_id'])
UpdateDb('UPDATE `staff` SET `lastactive` = ' + str(timestamp()
) + ' WHERE `id` = ' + staff_account['id'] + ' LIMIT 1')
if validated == False:
if method == 'login':
if 'username' in self.formdata and 'password' in self.formdata:
staff_account = verifyPasswd(
formdata.get("username"), formdata.get("password"))
if staff_account:
session_uuid = newSession(staff_account['id'])
values["token"] = session_uuid
UpdateDb('DELETE FROM `logs` WHERE `timestamp` < ' +
str(timestamp() - 604800)) # one week
else:
logAction('', 'Failed log-in. Username:'+_mysql.escape_string(
self.formdata['username'])+' IP:'+self.environ["REMOTE_ADDR"])
raise APIError, "Incorrect username/password."
else:
raise APIError, "Bad request"
else:
raise APIError, "Not authenticated"
else:
if method == 'news':
news = FetchAll(
"SELECT * FROM `news` WHERE type = 1 ORDER BY `timestamp` DESC")
values['news'] = news
elif method == 'post':
board = setBoard(formdata.get("board"))
if 'id' in formdata.keys():
id = formdata.get('id')
post = FetchOne("SELECT `id`, `boardid`, `parentid`,`timestamp`, `name`, `tripcode`, `email` ,`subject`,`message`,`file`,`thumb`, INET6_NTOA(`ip`) as ip,`IS_DELETED` AS `deleted`, `bumped`, `last`, `locked` FROM `posts` WHERE `id` = '" +
_mysql.escape_string(id) + "' AND `boardid` = '" + _mysql.escape_string(board["id"]) + "'")
values['post'] = post
if 'parentid' in formdata.keys():
id = formdata.get('parentid')
post = FetchAll("SELECT `id`, `boardid`, `parentid`,`timestamp`, `name`, `tripcode`, `email` ,`subject`,`message`,`file`,`thumb`, INET6_NTOA(`ip`) as ip,`IS_DELETED` AS `deleted`, `bumped`, `last`, `locked` FROM `posts` WHERE `parentid` = '" +
_mysql.escape_string(id) + "' AND `boardid` = '" + _mysql.escape_string(board["id"]) + "'")
values['posts'] = post
elif method == 'reports':
if len(path_split) > 3:
if path_split[3] == 'ignore':
report_id = formdata.get("id")
UpdateDb("DELETE FROM `reports` WHERE `id` = '" +
_mysql.escape_string(report_id)+"'")
else:
values['state'] = "error"
else:
reports = FetchAll(
"SELECT id, timestamp, timestamp_formatted, postid, parentid, link, board, INET6_NTOA(ip) AS ip, reason, INET6_NTOA(repip) AS repip FROM `reports` ORDER BY `timestamp` DESC")
values['reports'] = reports
elif method == 'logs':
logs = FetchAll("SELECT * FROM `logs` ORDER BY `timestamp` DESC")
values['logs'] = logs
elif method == 'staffPosts':
posts = FetchAll(
"SELECT * FROM `news` WHERE type = '0' ORDER BY `timestamp` DESC")
values['posts'] = posts
elif method == 'login':
values['token'] = token
elif method == 'members':
members = FetchAll(
"SELECT * FROM `staff` ORDER BY lastactive DESC")
values['members'] = members
elif method == 'stats':
report_count = FetchOne("SELECT COUNT(id) FROM `reports`")
try:
with open('stats.json', 'r') as f:
out = json.load(f)
values['stats'] = out
except ValueError:
values['stats'] = None
raise APIError, "Stats error"
values['stats']['reportCount'] = report_count['COUNT(id)']
else:
raise APIError, "Invalid method"
values['time'] = int(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)
def newSession(staff_id):
import uuid
session_uuid = uuid.uuid4().hex
param_session_id = _mysql.escape_string(session_uuid)
param_expires = timestamp() + Settings.SESSION_TIME
param_staff_id = int(staff_id)
InsertDb("INSERT INTO `session` (`session_id`, `expires`, `staff_id`) VALUES (UNHEX('%s'), %d, %d)" %
(param_session_id, param_expires, param_staff_id))
return session_uuid
def validateSession(session_id):
cleanSessions()
param_session_id = _mysql.escape_string(session_id)
param_now = timestamp()
session = FetchOne(
"SELECT HEX(session_id) as session_id, id, username, rights, added FROM `session` "
"INNER JOIN `staff` ON `session`.`staff_id` = `staff`.`id` "
"WHERE `session_id` = UNHEX('%s')" %
(param_session_id))
if session:
return session
return None
def renewSession(session_id):
param_session_id = _mysql.escape_string(session_id)
param_expires = timestamp() + Settings.SESSION_TIME
UpdateDb("UPDATE `session` SET expires = %d WHERE session_id = UNHEX('%s')" %
(param_expires, param_session_id))
def deleteSession(session_id):
param_session_id = _mysql.escape_string(session_id)
UpdateDb("DELETE FROM `session` WHERE session_id = UNHEX('%s')" %
param_session_id)
def cleanSessions():
param_now = timestamp()
UpdateDb("DELETE FROM `session` WHERE expires <= %d" % param_now)
def logAction(staff, action):
InsertDb("INSERT INTO `logs` (`timestamp`, `staff`, `action`) VALUES (" + str(timestamp()) +
", '" + _mysql.escape_string(staff) + "\', \' [API] " + _mysql.escape_string(action) + "\')")
def verifyPasswd(username, passwd):
import argon2
ph = argon2.PasswordHasher()
param_username = _mysql.escape_string(username)
staff_account = FetchOne(
"SELECT * FROM staff WHERE username = '%s'" % param_username)
if not staff_account:
return None
try:
ph.verify(staff_account['password'], passwd)
except argon2.exceptions.VerifyMismatchError:
return None
except argon2.exceptions.InvalidHash:
raise UserError, "Hash obsoleto o inválido. Por favor contacte al administrador."
if ph.check_needs_rehash(staff_account['password']):
param_new_hash = ph.hash(staff_acount['password'])
UpdateDb("UPDATE staff SET password = '%s' WHERE id = %s" %
(param_new_hash, staff_account['id']))
return staff_account
class APIError(Exception):
pass