#!/usr/bin/env python # index.py for a rudimentary key/value server by David Blume import os import sys import time import datetime import cgi, cgitb import filelock import tempfile import yaml # You may need to "pip install pyyaml" import texttime cgitb.enable(display=0, logdir="/tmp") store_name = 'store.txt' def read_file(full_pathname): """ The file must be a YAML file containing a dict. """ store = dict() if os.path.isfile(full_pathname): try: with filelock.FileLock(full_pathname): with open(full_pathname, 'r') as f: store = yaml.load(f) except filelock.FileLockException as e: raise return store def write_file(full_pathname, data): """ See http://david.dlma.com/blog/dads-project-in-the-garage """ try: with filelock.FileLock(full_pathname): with tempfile.NamedTemporaryFile(mode='w', dir=os.path.dirname(full_pathname), delete=False) as f: yaml.dump(data, f) # exiting this "with" flushes, but does not sync f.flush() os.fdatasync(f.fileno()) # Faster, Unix only tempname = f.name os.rename(tempname, full_pathname) # Atomic on Unix, not Windows except filelock.FileLockException as e: raise def map_ip_to_location(ip): if ip == '67.188.28.83': return 'home'.ljust(13) elif ip in ('50.224.7.233', '50.224.7.248'): return 'work'.ljust(13) return ip if __name__ == '__main__': localdir = os.path.abspath(os.path.dirname(sys.argv[0])) args = cgi.FieldStorage() key = None extra = None auth = None get_key = False auth_in_get = False doing_post = False # Find the parameters that are GET parameters # Could also check os.environ['REQUEST_METHOD'] == 'POST', # but that doesn't say which way individual params were sent. if os.environ['QUERY_STRING']: qs = os.environ['QUERY_STRING'].split('&') for pair in qs: k, v = pair.split('=') if k == 'k': get_key = True if k == 'auth': auth_in_get = True # Could be either GET or POST. Need to check QUERY_STRING above. if "k" in args: key = args["k"].value if os.environ['REQUEST_METHOD'] == 'POST': doing_post = True if not auth_in_get and "auth" in args: auth = args["auth"].value if "extra" in args: extra = args["extra"].value store = read_file(os.path.join(localdir, store_name)) if get_key: keys = key.split(',') if len(keys) > 1: devices = set() for k in store: d = store[k] if k in keys: devices.add((d['time'], key, d['value'])) if devices: # not empty print "Content-type: text/plain; charset=utf-8\n" sys.stdout.write(sorted(devices, reverse=True)[0][2]) else: print 'Status: 404 Not Found\n' elif key in store: print "Content-type: text/plain; charset=utf-8\n" sys.stdout.write(store[key]['value']) if extra is not None: sys.stdout.write('\n%s' % (store[key]['time'],)) else: print 'Status: 404 Not Found\n' else: wrote_response = False with open('auth.txt', 'r') as f: authorization = f.read().strip() if doing_post and auth == authorization: for new_key in args: if new_key not in ('auth', 'extra') and \ len(new_key) < 80 and len(args[new_key].value) < 140: if new_key in store and 'created' in store[new_key]: created_time = store[new_key]['created'] else: created_time = int(time.time()) store[new_key] = {'value': args[new_key].value, 'time': int(time.time()), 'created': created_time, 'origin': os.environ['REMOTE_ADDR']} write_file(os.path.join(localdir, store_name), store) wrote_response = True print "Content-type: text/plain; charset=utf-8\n" print "OK" break elif not os.environ['QUERY_STRING'] and not doing_post: # just the home page, then print "Content-type: text/plain; charset=UTF-8\n" wrote_response = True print "key \tvalue \tcreated \tupdated \torigin \tNotes" print "------------ \t------------- \t----------\t----------\t-------------\t---------------------" devices = { '1G': '(Au)', '5S': '(Br)', 'YW': '(FW)', 'YY': '(Da)', '2N': '(Li)', '1R': '(Ty)', 'YU': '(Lf)', 'YP': '(C4)', } for key in sorted(store, key=lambda k: store[k]['time'], reverse=True): if key[:2] in devices: device = devices[key[:2]] + ' ' else: device = '' d = store[key] td = texttime.stringify(datetime.timedelta(seconds=time.time()-d['time'])) if 'created' in d: created = time.strftime('%Y-%m-%d', time.localtime(d['created'])) else: created = " " if 'origin' in d: origin = map_ip_to_location(d['origin']) else: origin = " " print '\t'.join((key.ljust(12), d['value'].ljust(13), created, str(d['time']), origin, "%supdated %s ago" % (device, td))) print print 'Tip:' print 'sync_ip () { export ENV_VAR=$(curl -s "%s?k=2FN50L002036,2FN44N010911"); }' % os.environ['SCRIPT_URI'] print 'curl --data "`hostname -s`=`hostname -i`&auth=$AUTH" "%s"' % os.environ['SCRIPT_URI'] if not wrote_response: print 'Status: 400 Bad Request\n'