""" This is the webhook server that interfaces with Google's WebSub (formerly pubsubhubbub) server. This is its own server, so it can live on a different host than the frontend. """ #TODO: fold update-websub.py into this? import time import sqlite3 from flask import Flask, request from urllib.parse import parse_qs, urlparse from common import * app = Flask(__name__) @app.route('/websub/v1////', methods=["GET"]) def websub(timestamp, nonce, subject, sig): mode = request.args.get('hub.mode', '') topic = request.args.get('hub.topic', '') challenge = request.args.get('hub.challenge', '') until = int(request.args.get('hub.lease_seconds', '0')) # extract the first query parameter, which is either channel_id or playlist_id: channel_id = next(iter(parse_qs(urlparse(topic).query).values()), [None])[0] if not channel_id: return '', 400 if time.time() - timestamp > int(cf['websub']['lease']): return '', 400 # TODO: implement hmac check: return '',400 unless hmac_sha1_hex("$timestamp/$nonce", $HMACKEY) eq $sig if mode != "subscribe": return '', 200 # Note: channels are not purged from the websub dbtable. with sqlite3.connect(cf['global']['database']) as conn: c = conn.cursor() c.execute(""" INSERT OR REPLACE INTO websub (channel_id, subscribed_until) VALUES (?, datetime(?, 'unixepoch')) """, (channel_id, time.time()+until)) return challenge, 200 @app.route('/websub/v1////', methods=["POST"]) def websub_post(timestamp, nonce, subject, sig): # TODO: implement hmac check: return '',400 unless hmac_sha1_hex("$timestamp/$nonce", $HMACKEY) eq $sig # todo: # say 400 and exit unless hmac_sha1_hex($body, $HMACKEY) eq $ENV{X-Hub-Signature } with sqlite3.connect(cf['global']['database']) as conn: c = conn.cursor() try: update_channel(conn, request.data) except: with open('/tmp/websub-subscriptions.err', 'a') as f: f.write(f"\n{xmlfeed}\n") return '', 200 if __name__ == '__main__': app.run(debug=True)