# point/plugs/api.py
#
#
""" Object Copy API. """
## IMPORTS
from point.utils import run_thr, error, get_exception, list_files, j
from point import Object, kernel, __version__
# imports
import urllib.parse
import urllib
import logging
import select
import socket
import time
import http
import sys
import os
# http imports
from http.server import HTTPServer, BaseHTTPRequestHandler
## hostname
try: hostname = socket.getfqdn()
except: hostname = "localhost"
port = 10102
## APIServer class
[docs]class APIServer(HTTPServer, Object):
""" API server """
allow_reuse_address = True
daemon_thread = True
def __init__(p, *args, **kwargs):
""" start the API server. """
HTTPServer.__init__(p, *args, **kwargs)
Object.__init__(p)
logging.warn("running on http://%s:%s/" % args[0])
p.time_start = time.time()
p.time_in = time.time()
p.stop = False
p.running = False
p.handlers = {}
p.webmods = {}
p.state = Object()
p.state.register('whitelistenable', 0)
p.state.register('whitelist', [])
p.state.register('blacklist', [])
p.state.register('disable', [])
p.poll = select.poll()
p.poll.register(p)
[docs] def start(p, *args, **kwargs): p.serve()
[docs] def exit(p):
""" shutdown the API server. """
try: p.stop = True ; time.sleep(0.2) ; p.server_close()
except Exception as ex: error()
[docs] def serve(p):
""" serving loop. """
time.sleep(1)
while not p.stop:
p.running = True
try: got = p.poll.poll(100)
except Exception as ex: error()
if got and not p.stop:
try: p.handle_request()
except Exception as ex: error()
time.sleep(0.01)
p.running = False
[docs] def entrypoint(p, request):
""" check lists whether request should be allowed. """
ip = request.ip
if not p.whitelistenable() and ip in p.blacklist():
logging.warn('api - denied %s' % ip)
request.send_error(401)
return False
if p.whitelistenable() and ip not in p.whitelist():
logging.warn('api - denied %s' % ip)
request.send_error(401)
return False
return True
[docs] def whitelistenable(p):
""" enable whitelist? """
return p.state['whitelistenable']
[docs] def whitelist(p):
""" return the whitelist. """
return p.state['whitelist']
[docs] def blacklist(p):
""" return the black list. """
return p.state['blacklist']
[docs] def register(p, path, handler):
""" add a web handler """
logging.warn('api - %s handler added' % path)
path = urllib.parse.unquote_plus(path)
p.handlers[path] = handler
[docs] def enable(p, what):
""" enable an path. """
try:
p.state['disable'].remove(what)
logging.info('api - enabled %s' % str(what))
except ValueError: pass
[docs] def disable(p, what):
""" disable an path. """
p.state['disable'].append(what)
logging.info('api - disabled %s' % str(what))
[docs] def do(p, request):
""" do a request """
path = urllib.parse.unquote_plus(request.path.strip())
try: path, query = path.split('?')
except ValueError: query = ""
for i in p.state['disable']:
if i in path:
logging.warn('api - %s - denied disabled %s' % (request.ip, i))
request.send_error(404)
return
try:
splitted = path.split("/")
try: target = splitted[1]
except: target = path
if len(splitted) == 1: target = splitted[1]
else: target = splitted[1]
func = p.handlers[target]
result = func(p, request)
p.time_in = time.time()
return result
except KeyError: logging.info("api - missing handler %s" % str(target))
except: error()
[docs] def handle_error(p, request, addr):
""" log the error """
ex = get_exception()
logging.warn('api error %s - %s' % (addr, ex))
## RestReqeustHandler class
[docs]class APIHandler(BaseHTTPRequestHandler):
""" timeserver request handler class """
[docs] def setup(p):
""" called on each incoming request. """
BaseHTTPRequestHandler.setup(p)
p.ip = p.client_address[0]
p.name = p.ip
p.size = 0
[docs] def write_header(p, type='text/plain'):
""" write headers to the client. """
p.send_response(200)
p.send_header('Content-type', '%s; charset=%s ' % (type,sys.getdefaultencoding()))
p.send_header('Server', __version__)
p.end_headers()
[docs] def handle_request(p):
""" handle a REST request. """
try:
result = p.server.do(p)
if not result: return
p.size = len(result)
except Exception as ex:
error()
p.send_error(501)
return
p.wfile.close()
do_DELETE = do_PUT = do_GET = do_POST = handle_request
[docs] def log_request(p, code):
""" log the request """
try: ua = p.headers['user-agent']
except: ua = "-"
try: rf = p.headers['referer']
except: rf = "-"
logging.info('api - %s %s' % (p.address_string(), code))
server = None
[docs]def api_home(server, handler):
handler.wfile.write(bytes("""
POINT is a program that will allow you to register events that take place in
life, so you can reconstruct things that happen to you if memory is lost.
It is essential to be able to place the past in a proper context, so that
you can be free to live a real life, instead of moving away from what is
left behind.
""", "utf-8"))
handler.wfile.write(bytes("\n\nAPI INTERFACE\n\n", "UTF-8"))
for func in server.handlers:
handler.wfile.write(bytes("http://%s:%s/%s\n" % (hostname, port, str(func)), "utf-8"))
[docs]def api_all(server, handler):
handler.write_header()
o = Object()
nr = 0
for fn in list_files(kernel.cfg.workdir):
o.register(nr, "http://%s:%s/get/%s" % (hostname, port, fn.split(os.sep)[-1]))
nr += 1
handler.wfile.write(bytes(o.to_json(indent=2), "utf-8"))
[docs]def api_get(server, handler):
handler.write_header()
fn = handler.path.split("/")[2]
o = Object()
o.load_file(j(kernel.cfg.workdir, fn))
handler.wfile.write(bytes(o.to_json(indent=2), "utf-8"))
[docs]def api_favicon(server, handler):
ico = open("favicon.ico", "br").read()
handler.wfile.write(ico)
[docs]def init():
server = APIServer((hostname, port), APIHandler)
server.register("", api_home)
server.register("all", api_all)
server.register("get", api_get)
server.register("favicon.ico", api_favicon)
kernel.run["API"] = server
run_thr(server.start, ())