2 from http.server import BaseHTTPRequestHandler
3 from http.cookies import SimpleCookie
7 class PlomException(Exception):
13 def __init__(self, db_name):
14 self.db_file = db_name
15 self.lock_file = db_name+ '.lock'
16 if os.path.exists(self.db_file):
17 with open(self.db_file, 'r') as f:
21 if os.path.exists(self.lock_file):
22 raise PlomException('Sorry, lock file!')
23 f = open(self.lock_file, 'w+')
27 os.remove(self.lock_file)
31 from datetime import datetime, timedelta
32 if not os.path.exists(self.db_file):
35 # collect modification times of numbered .bak files
36 # print('DEBUG BACKUP')
37 bak_prefix = f'{self.db_file}.bak.'
40 for path in [path for path in os.listdir(os.path.dirname(bak_prefix))
41 if path.startswith(os.path.basename(bak_prefix))]:
42 path = os.path.dirname(bak_prefix) + f'/{path}'
43 mod_time = os.path.getmtime(path)
44 # print(f'DEBUG pre-exists: {path} {mod_time}')
45 mtimes_to_paths[str(datetime.fromtimestamp(mod_time))] = path
47 # for mtime in sorted(mtimes_to_paths.keys()):
48 # print(f'DEBUG mtimes_to_paths: {mtime}:{mtimes_to_paths[mtime]}')
50 # collect what numbered .bak files to save: the older, the fewer; for each
51 # timedelta, keep the newest file that's older
52 ages_to_keep = [timedelta(minutes=4**i) for i in range(0, 8)]
53 # print(f'DEBUG ages_to_keep: {ages_to_keep}')
56 for age in ages_to_keep:
58 for mtime in reversed(sorted(mtimes_to_paths.keys())):
59 # print(f'DEBUG checking if {mtime} < {limit} ({now} - {age})')
61 mtime_test = mtime + '.000000'
64 if datetime.strptime(mtime_test, '%Y-%m-%d %H:%M:%S.%f') < limit:
65 # print('DEBUG it is, adding!')
66 to_save[mtime] = mtimes_to_paths[mtime]
69 for path in [path for path in mtimes_to_paths.values()
70 if path not in to_save.values()]:
71 # print(f'DEBUG removing {path} cause not in to_save')
75 for mtime in sorted(to_save.keys()):
76 source = to_save[mtime]
77 target = f'{bak_prefix}{i}'
78 # print(f'DEBUG to_save {source} -> {target}')
80 shutil.move(source, target)
83 # put copy of current state at end of bak list
84 # print(f'DEBUG saving current state to {bak_prefix}{i}')
85 shutil.copy(self.db_file, f'{bak_prefix}{i}')
87 def write_text_to_db(self, text, mode='w'):
90 with open(self.db_file, mode) as f:
95 class PlomHandler(BaseHTTPRequestHandler):
97 html_head = '<!DOCTYPE html>\n<html>\n<meta charset="UTF-8">'
98 html_foot = '</body>\n</html>'
100 def fail_400(self, e):
101 self.send_HTML(f'ERROR: {e}', 400)
103 def send_HTML(self, html, code=200):
104 self.send_code_and_headers(code, [('Content-type', 'text/html')])
105 self.wfile.write(bytes(f'{self.html_head}\n{html}\n{self.html_foot}', 'utf-8'))
107 def ensure_cookies_to_set(self):
108 if not hasattr(self, 'cookies_to_set'):
109 self.cookies_to_set = []
111 def set_cookie(self, cookie_name, cookie_path, cookie_db):
112 self.ensure_cookies_to_set()
113 cookie = SimpleCookie()
114 cookie[cookie_name] = json.dumps(cookie_db)
115 cookie[cookie_name]['path'] = cookie_path
116 self.cookies_to_set += [cookie]
118 def unset_cookie(self, cookie_name, cookie_path):
119 self.ensure_cookies_to_set()
120 cookie = SimpleCookie()
121 cookie[cookie_name] = ''
122 cookie[cookie_name]['path'] = cookie_path
123 cookie[cookie_name]['expires'] = 'Thu, 01 Jan 1970 00:00:00 GMT'
124 self.cookies_to_set += [cookie]
126 def get_cookie_db(self, cookie_name):
128 if 'Cookie' in self.headers:
129 cookie = SimpleCookie(self.headers['Cookie'])
130 if cookie_name in cookie:
131 cookie_db = json.loads(cookie[cookie_name].value)
134 def send_code_and_headers(self, code, headers=[]):
135 self.ensure_cookies_to_set()
136 self.send_response(code)
137 for cookie in self.cookies_to_set:
138 for morsel in cookie.values():
139 self.send_header('Set-Cookie', morsel.OutputString())
140 for fieldname, content in headers:
141 self.send_header(fieldname, content)
144 def redirect(self, url='/'):
145 self.send_code_and_headers(302, [('Location', url)])
147 def try_do(self, do_method):
150 except PlomException as e:
155 def run_server(port, handler_class):
156 from http.server import HTTPServer
157 webServer = HTTPServer(('localhost', port), handler_class)
158 print(f"Server started http://localhost:{port}")
160 webServer.serve_forever()
161 except KeyboardInterrupt:
163 webServer.server_close()
164 print("Server stopped.")