aboutsummaryrefslogblamecommitdiff
path: root/cgi/modapi.py
blob: 6350b35a78b2b69981d896b2da770a5486c0a4fb (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11










                          

                           
                                                       






















                                                                                      






























                                                                                                                    

                                             

                                               
         



                                                                                
                              
                                                   

                                       
                                                                                                                                                                                                                                                             
                                                                                                                           

                                             
                                             
                                                                                                                                                                                                                                                                   
                                                                                                                           
                                      
                                 





                                                                     
                                             



                                                                                                                                                                                                  







                                                                                  
                                   





                                                                      






                                                  
                                                                      

                                            





































































                                                                                                         

























                                                                                                          



                          
# 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