]> git.frykholm.com Git - friends.git/blame_incremental - friends/server.py
Ignore virtualenv
[friends.git] / friends / server.py
... / ...
CommitLineData
1#!/usr/bin/python3
2import tornado.ioloop
3import tornado.web
4import os
5import os.path
6import tornado.httpserver
7import tornado.httpclient as httpclient
8import sqlite3
9import arrow
10import datetime
11from rd import RD, Link
12import hmac
13from tornado.options import options, define
14import logging
15db = None
16# insert into user (name,email) values('mikael','mikael@frykholm.com');
17# insert into entry (userid,text) values (1,'My thoughts on ostatus');
18
19
20settings = {
21 "static_path": os.path.join(os.path.dirname(__file__), "static"),
22 "cookie_secret": "supersecret123",
23 "login_url": "/login",
24 "xsrf_cookies": False,
25 "domain":"https://ronin.frykholm.com",
26
27}
28
29#curl -v -k "https://ronin.frykholm.com/hub" -d "hub.callback=a" -d "hub.mode=b" -d "hub.topic=c" -d "hub.verify=d"
30
31class PushHandler(tornado.web.RequestHandler):
32 def post(self):
33 """ Someone wants to subscribe to hub_topic feed"""
34 hub_callback = self.get_argument('hub.callback')
35 hub_mode = self.get_argument('hub.mode')
36 hub_topic = self.get_argument('hub.topic')
37 hub_verify = self.get_argument('hub.verify')
38 hub_lease_seconds = self.get_argument('hub.lease_seconds','')
39 hub_secret = self.get_argument('hub.secret','')
40 hub_verify_token = self.get_argument('hub.verify_token','')
41 print(self.request.body)
42 if hub_mode == 'unsubscribe':
43 pass #FIXME
44 path = hub_topic.split(self.settings['domain'])[1]
45 user = path.split('user/')[1]
46 row = db.execute("select id from user where name=?",(user,)).fetchone()
47 expire = datetime.datetime.utcnow() + datetime.timedelta(seconds=int(hub_lease_seconds))
48 if row:
49 db.execute("INSERT into subscriptions (userid, expires, callback, secret, verified) "
50 "values (?,?,?,?,?)",(row['id'],expire,hub_callback,hub_secret,False))
51 db.commit()
52 self.set_status(202)
53 http_client = httpclient.HTTPClient()
54 try:
55 response = http_client.fetch(hub_callback+"?hub.mode={}&hub.topic={}&hub.secret".format(hub_mode,hub_topic,hub_secret))
56 print(response.body)
57 except httpclient.HTTPError as e:
58 # HTTPError is raised for non-200 responses; the response
59 # can be found in e.response.
60 print("Error: " + str(e))
61 except Exception as e:
62 # Other errors are possible, such as IOError.
63 print("Error: " + str(e))
64 http_client.close()
65 #TODO add secret to outgoing feeds with hmac
66
67class XrdHandler(tornado.web.RequestHandler):
68 def get(self):
69 self.render("templates/xrd.xml", hostname="ronin.frykholm.com", url=self.settings['domain'])
70
71class FingerHandler(tornado.web.RequestHandler):
72 def get(self):
73 user = self.get_argument('resource')
74 user = user.split('acct:')[1]
75 (user,domain) = user.split('@')
76 rows = db.execute("select id from user where user.name=?",(user,)).fetchone()
77 if not rows:
78 self.set_status(404)
79 self.write("Not found")
80 self.finish()
81 return
82 lnk = Link(rel='http://spec.example.net/photo/1.0',
83 type='image/jpeg',
84 href='{}/static/{}.jpg'.format(self.settings['domain'],user))
85 lnk.titles.append(('User Photo', 'en'))
86 lnk.titles.append(('Benutzerfoto', 'de'))
87 lnk.properties.append(('http://spec.example.net/created/1.0', '1970-01-01'))
88 lnk2 = Link(rel='http://schemas.google.com/g/2010#updates-from',
89 type='application/atom+xml',
90 href='{}/user/{}'.format(self.settings['domain'],user))
91
92 rd = RD(subject='{}/{}'.format(self.settings['domain'],user))
93 rd.properties.append('http://spec.example.net/type/person')
94 rd.links.append(lnk)
95 rd.links.append(lnk2)
96 self.write(rd.to_json())
97
98class UserHandler(tornado.web.RequestHandler):
99 def get(self, user):
100 entries = db.execute("select entry.id,text,ts from user,entry where user.id=entry.userid and user.name=?",(user,))
101 # import pdb;pdb.set_trace()
102 self.set_header("Content-Type", 'application/atom+xml')
103 out = self.render("templates/feed.xml",
104 user=user,
105 feed_url="{}/user/{}".format(self.settings['domain'], user),
106 hub_url="{}/hub".format(self.settings['domain']),
107 entries=entries,
108 arrow=arrow )
109 #digest = hmac.new()
110
111 def post(self, user):
112 entries = db.execute("select entry.id,text,ts from user,entry where user.id=entry.userid and user.name=?",(user,))
113
114 self.set_header("Content-Type", 'application/atom+xml')
115 out = self.render_string("templates/feed.xml",
116 user=user,
117 feed_url="{}/user/{}".format(self.settings['domain'], user),
118 hub_url="{}/hub".format(self.settings['domain']),
119 entries=entries,
120 arrow=arrow)
121 #import pdb;pdb.set_trace()
122 subscribers = db.execute("select callback, secret from subscriptions, user where user.id=subscriptions.userid and user.name=?",(user,))
123 for url,secret in subscribers:
124 digest = hmac.new(secret.encode('utf8'), out, digestmod='sha1').hexdigest()
125
126 req = httpclient.HTTPRequest(url=url, allow_nonstandard_methods=True,method='POST', body=out, headers={"X-Hub-Signature":"sha1={}".format(digest),"Content-Type": 'application/atom+xml',"Content-Length":len(out)})
127 apa = httpclient.HTTPClient()
128 apa.fetch(req)
129
130application = tornado.web.Application([
131 (r"/.well-known/host-meta", XrdHandler),
132 (r"/.well-known/webfinger", FingerHandler),
133 (r"/user/(.+)", UserHandler),
134 (r"/hub", PushHandler),
135 ],debug=True,**settings)
136srv = tornado.httpserver.HTTPServer(application, )
137
138def setup_db(path):
139 gen_log = logging.getLogger("tornado.general")
140 gen_log.warn("No db found, creating in {}".format(path))
141 con = sqlite3.connect(path)
142 con.execute(""" create table user (id integer primary key,
143 name varchar,
144 email varchar);
145 create table entry (id integer primary key,
146 userid INTEGER,
147 text varchar,
148 ts timestamp default current_timestamp,
149 FOREIGN KEY(userid) REFERENCES user(id));
150 create table subscriptions (id integer primary key,
151 userid integer,
152 expires datetime,
153 callback varchar,
154 secret varchar,
155 verified bool,
156 FOREIGN KEY(userid) REFERENCES user(id));""")
157 con.commit()
158
159
160options.define("config_file", default="/etc/friends/friends.conf", type=str)
161options.define("webroot", default="/srv/friends/", type=str)
162
163if __name__ == "__main__":
164 dbPath = 'friends.db'
165# options.log_file_prefix="/tmp/friends"
166 tornado.options.parse_config_file(options.config_file)
167 tornado.options.parse_command_line()
168 gen_log = logging.getLogger("tornado.general")
169 gen_log.info("Reading config from: %s", options.config_file,)
170 if not os.path.exists(dbPath):
171 setup_db(dbPath)
172 db = sqlite3.connect(dbPath, detect_types=sqlite3.PARSE_DECLTYPES|sqlite3.PARSE_COLNAMES)
173 db.row_factory = sqlite3.Row
174 srv.listen(80)
175 tornado.ioloop.IOLoop.instance().start()
176