]> git.gir.st - subscriptionfeed.git/blob - app/webhooks/__init__.py
raise exception on webhooks errors
[subscriptionfeed.git] / app / webhooks / __init__.py
1 """
2 This is the webhook server that interfaces with Google's WebSub (formerly
3 pubsubhubbub) server. This can be run either as a module on the main frontend,
4 or as its own server, so it can live on a different host than the frontend.
5 """
6
7 import time
8 import sqlite3
9 from flask import Flask, Blueprint, request
10 from urllib.parse import parse_qs, urlparse
11
12 from ..common.common import *
13
14 frontend = Blueprint('webhooks', __name__)
15
16 def app():
17 app = Flask(__name__)
18 app.register_blueprint(frontend)
19 return app
20
21 @frontend.route('/websub')
22 def index():
23 return '', 204 # No Content
24
25 @frontend.route('/websub/v1/<int:timestamp>/<nonce>/<subject>/<sig>', methods=["GET"])
26 def websub(timestamp, nonce, subject, sig):
27 hmackey = cf['websub']['hmac_key']
28 mode = request.args.get('hub.mode', '')
29 topic = request.args.get('hub.topic', '')
30 challenge = request.args.get('hub.challenge', '')
31 until = int(request.args.get('hub.lease_seconds', '0'))
32
33 if sig != websub_url_hmac(hmackey, subject, timestamp, nonce):
34 return '', 400
35
36 if time.time() - timestamp > int(cf['websub']['lease']):
37 return '', 400
38
39 if mode != "subscribe":
40 return '', 200
41 # Note: channels are not purged from the websub dbtable.
42
43 with sqlite3.connect(cf['global']['database']) as conn:
44 c = conn.cursor()
45 c.execute("""
46 INSERT OR REPLACE INTO websub (channel_id, subscribed_until)
47 VALUES (?, datetime(?, 'unixepoch'))
48 """, (subject, time.time()+until))
49 return challenge, 200
50
51 @frontend.route('/websub/v1/<int:timestamp>/<nonce>/<subject>/<sig>', methods=["POST"])
52 def websub_post(timestamp, nonce, subject, sig):
53 lease = cf['websub']['lease']
54 hmackey = cf['websub']['hmac_key']
55 hub_signature = request.headers.get('X-Hub-Signature').replace("sha1=", "")
56
57 if sig != websub_url_hmac(hmackey, subject, timestamp, nonce):
58 return '', 400
59 if hub_signature != websub_body_hmac(hmackey, request.data):
60 return '', 400
61
62 with sqlite3.connect(cf['global']['database']) as conn:
63 c = conn.cursor()
64 try:
65 update_channel(conn, request.data, from_webhook=True)
66 except Exception as e:
67 with open('/tmp/websub-subscriptions.err', 'ab') as f:
68 f.write(f"<!-- {time.ctime()} ({int(time.time())}) -->\n".encode('ascii'))
69 f.write(request.data + b"\n")
70 raise e
71 return '', 200
72
73 if __name__ == '__main__':
74 app().run(debug=True)
Imprint / Impressum