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