]> git.gir.st - subscriptionfeed.git/blob - app/webhooks/__init__.py
integrate anticaptcha into common and clean it up a bit
[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 from ..common.anticsrf import no_csrf_protection
14 from ..common.anticaptcha import solve_captcha
15
16 frontend = Blueprint('webhooks', __name__)
17
18 def app():
19 app = Flask(__name__)
20 app.register_blueprint(frontend)
21 return app
22
23 @frontend.route('/websub')
24 def index():
25 return '', 204 # No Content
26
27 @frontend.route('/websub/v1/<int:timestamp>/<nonce>/<subject>/<sig>', methods=["GET"])
28 def websub(timestamp, nonce, subject, sig):
29 hmackey = cf['websub']['hmac_key']
30 mode = request.args.get('hub.mode', '')
31 topic = request.args.get('hub.topic', '')
32 challenge = request.args.get('hub.challenge', '')
33 until = int(request.args.get('hub.lease_seconds', '0'))
34
35 if sig != websub_url_hmac(hmackey, subject, timestamp, nonce):
36 return '', 400
37
38 if time.time() - timestamp > int(cf['websub']['lease']):
39 return '', 400
40
41 if mode != "subscribe":
42 return '', 200
43 # Note: channels are not purged from the websub dbtable.
44
45 with sqlite3.connect(cf['global']['database']) as conn:
46 c = conn.cursor()
47 c.execute("""
48 INSERT OR REPLACE INTO websub (channel_id, subscribed_until)
49 VALUES (?, datetime(?, 'unixepoch'))
50 """, (subject, time.time()+until))
51 return challenge, 200
52
53 @frontend.route('/websub/v1/<int:timestamp>/<nonce>/<subject>/<sig>', methods=["POST"])
54 @no_csrf_protection
55 def websub_post(timestamp, nonce, subject, sig):
56 lease = cf['websub']['lease']
57 hmackey = cf['websub']['hmac_key']
58 hub_signature = request.headers.get('X-Hub-Signature').replace("sha1=", "")
59
60 if sig != websub_url_hmac(hmackey, subject, timestamp, nonce):
61 return '', 400
62 if hub_signature != websub_body_hmac(hmackey, request.data):
63 return '', 400
64
65 with sqlite3.connect(cf['global']['database']) as conn:
66 c = conn.cursor()
67 try:
68 update_channel(conn, request.data, from_webhook=True)
69 except Exception as e:
70 with open('/tmp/websub-subscriptions.err', 'ab') as f:
71 f.write(f"<!-- {time.ctime()} ({int(time.time())}) -->\n".encode('ascii'))
72 f.write(request.data + b"\n")
73 raise e
74 return '', 200
75
76 @frontend.route("/captcha_response/v1/<nonce>", methods=["POST"])
77 @no_csrf_protection
78 def captcha_response(nonce):
79 solve_captcha(nonce, request.json)
80 return "", 204
81
82 if __name__ == '__main__':
83 app().run(debug=True)
Imprint / Impressum