xms/xmsweb.py

219 lines
7.1 KiB
Python
Executable File

#!/usr/bin/python3
import argparse
import cgi
from datetime import datetime
from http.server import BaseHTTPRequestHandler, HTTPServer
import json
import os
import re
from urllib.parse import parse_qs
from mswalletrpc import MultisigWallet
from web.urls import URLS
class WalletPool():
pool = {}
def add(self, name, wallet):
self.pool[name] = wallet
def get(self, name):
return self.pool.get(name)
def kill(self, name):
wallet = self.pool.get(name)
if not wallet:
return
wallet.rpc_stop()
del self.pool[name]
def __del__(self):
while self.pool:
self.pool.pop(0).delete()
class XMSRequestHandler(BaseHTTPRequestHandler):
settings = {
'daemon-address': 'localhost:18081',
'daemon-login': '',
'stagenet': False,
'testnet': False,
'proxy': '',
'timeout': 30,
}
def __init__(self, *args, **kwargs):
self.cd_default_dir()
try:
for key, value in json.loads(open('settings.json').read()).items():
if key in self.settings:
self.settings[key] = value
except Exception:
...
return super().__init__(*args, **kwargs)
def save_settings(self):
self.cd_default_dir()
open('settings.json', 'w').write(json.dumps(self.settings))
def cd_default_dir(self):
os.chdir(self.default_path)
def get_wallets(self):
self.cd_default_dir()
paths = []
if os.path.isdir('wallets'):
for name in sorted(os.listdir('wallets')):
if os.path.isdir(os.path.join('wallets', name)):
paths.append(name)
if not paths:
return
wallets = {}
for p in paths:
wallet = {'name': p}
if os.path.exists(os.path.join('wallets', p, 'wallet.password.txt')):
fn = os.path.join('wallets', p, 'ms.round1.txt')
if os.path.exists(fn):
key1 = open(fn).read().strip()
wallet['key1'] = key1
wallet['created_at'] = datetime.fromtimestamp(
os.lstat(fn).st_ctime
).strftime('%Y-%m-%d %H:%M:%S')
items = {
'address': 'wallet.address.txt',
'label': 'wallet.label.txt',
'key2': 'ms.round2.txt',
'key3': 'ms.round3.txt',
'finalized': 'ms.finalized.txt',
}
for name, file in items.items():
fn = os.path.join('wallets', p, file)
if os.path.exists(fn):
value = open(fn).read().strip()
if value:
wallet[name] = value
if wallet.get('finalized'):
wallet['state'] = 'finalized'
elif wallet.get('key3'):
wallet['state'] = 'round3'
elif wallet.get('key2'):
wallet['state'] = 'round2'
elif wallet.get('key1'):
wallet['state'] = 'round1'
else:
wallet['state'] = 'new'
wallets[p] = wallet
return wallets
def get_wallet(self, name):
wallet = self.pool.get(name)
if not wallet:
wallet = MultisigWallet(
self.get_wallets().get(name)['name'],
self.settings
)
self.pool.add(name, wallet)
return wallet
def dispatch(self, method='get'):
for url, cls in URLS.items():
match = re.search(url, self.path)
if not match:
continue
kwargs = match.groupdict() or {}
resp = None
try:
if method.lower() == 'get':
resp = cls().get(self, **kwargs)
elif method.lower() == 'post':
content_length = int(self.headers.get('Content-Length', 0))
ctype, pdict = cgi.parse_header(self.headers.get('Content-Type') or '')
if content_length:
if ctype == 'application/json':
data = json.loads(self.rfile.read(content_length))
else:
data = parse_qs(self.rfile.read(content_length).decode())
else:
data = {}
resp = cls().post(self, data=cls.get_form_data(data), **kwargs)
else:
return 405, 'Method not allowed'
if resp:
if isinstance(resp, (list, tuple)) and len(resp) == 2:
return resp
return 200, resp
except NotImplementedError:
return 405, 'Method not allowed'
return 404, 'Not found'
def do_method(self, method='get'):
code, resp = self.dispatch(method)
self.send_response(code)
if code == 301:
self.send_header('Location', resp)
self.end_headers()
else:
if self.path.endswith('.js'):
self.send_header('Content-type', 'text/javascript')
self.end_headers()
for r in resp:
self.wfile.write(bytes(r, 'utf-8'))
elif self.path.endswith('.css'):
self.send_header('Content-type', 'text/css')
self.end_headers()
for r in resp:
self.wfile.write(bytes(r, 'utf-8'))
elif isinstance(resp, str):
self.send_header('Content-type', 'text/html')
self.end_headers()
for r in resp:
self.wfile.write(bytes(r, 'utf-8'))
else:
self.send_header('Content-type', 'application/json')
self.end_headers()
self.wfile.write(bytes(json.dumps(resp), 'utf-8'))
def do_GET(self):
return self.do_method('get')
def do_POST(self):
return self.do_method('post')
def redirect(self, url):
return 301, url
def add_attrs(obj, **kwargs):
for name, value in kwargs.items():
setattr(obj, name, value)
return obj
if __name__ == '__main__':
parser = argparse.ArgumentParser(add_help=True)
parser.add_argument(
'-H', '--hostname', type=str, default='127.0.0.1',
help='Server hostname, default: 127.0.0.1 (closed for not local connections). 0.0.0.0 to allow connect anyone.'
)
parser.add_argument('-p', '--port', type=int, help='Server port, default: 7779', default=7779)
args = parser.parse_args()
pool = WalletPool()
webServer = HTTPServer((args.hostname, args.port), add_attrs(
XMSRequestHandler,
pool=pool,
default_path=os.path.abspath(os.curdir)
))
print('Server started http://%s:%s' % (args.hostname, args.port))
try:
webServer.serve_forever()
except KeyboardInterrupt:
pass
webServer.server_close()
print('Server stopped.')
del pool