1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
|
require 'json'
require 'net/http'
require 'uri'
require 'base64'
USER = "pdp8"
WWW_DOMAIN = "pdp8.info"
WWW_URL = "https://#{WWW_DOMAIN}"
SOCIAL_DOMAIN = "social.#{WWW_DOMAIN}"
ACCOUNT = "#{USER}@#{SOCIAL_DOMAIN}"
SOCIAL_URL = "https://#{SOCIAL_DOMAIN}"
ACTOR = URI.join(SOCIAL_URL, USER)
MATRIX = "@#{USER}:matrix.#{WWW_DOMAIN}"
OUTBOX = File.join(File.dirname(__FILE__), "outbox")
INBOX = File.join(File.dirname(__FILE__), "inbox")
class Application
def call(env)
code = 404
type = "application/activity+json"
case env['REQUEST_METHOD']
when 'POST'
case env["REQUEST_URI"]
when "/inbox" # receive from server
if verify(env)
input = JSON.parse(env["rack.input"].gets)
# p input["type"]
code = 200
response = "OK"
else
code = 401
response = "not verified"
end
when "/outbox"
input = JSON.parse(env["rack.input"].gets)
case input["type"]
when "Create"
end
end
when 'GET'
case env["REQUEST_URI"]
when "/.well-known/webfinger?resource=acct:#{ACCOUNT}"
type = "application/jrd+json"
response = File.read("webfinger")
code = 200
when "/#{USER}"
response = File.read("pdp8")
code = 200
when "/outbox"
response = ordered_collection OUTBOX
code = 200
when "/inbox"
type = "application/activity+json"
response = ordered_collection INBOX
code = 200
# when "/following"
# when "/followers"
# when "/liked"
end
end
[code, { "Content-Type" => type }, [response]]
end
def ordered_collection dir
posts = Dir[File.join(dir, "*.json")].sort.reverse.collect { |f| p f; JSON.parse(File.read f) }
{
"@context" => "https://www.w3.org/ns/activitystreams",
"summary" => "pdp8 outbox",
"type" => "OrderedCollection",
"totalItems" => posts.size,
"orderedItems" => posts,
}.to_json
end
def verify(env)
begin
signature_header = {}
env["HTTP_SIGNATURE"].split(',').each do |pair|
k, v = pair.split('=')
signature_header[k] = v.gsub('"', '')
end
key_id = signature_header['keyId']
headers = signature_header['headers']
signature = Base64.urlsafe_decode64(signature_header['signature'].encode("ascii-8bit"))
uri = URI(key_id)
res = Net::HTTP.get_response(uri)
actor = JSON.parse(res.body)
key = OpenSSL::PKey::RSA.new(actor['publicKey']['publicKeyPem'])
comparison_string = headers.split(' ').map do |signed_header_name|
if signed_header_name == '(request-target)'
'(request-target): post /inbox'
else
"#{signed_header_name}: #{env["HTTP_" + signed_header_name.upcase]}"
end
end.join("\n")
if key.verify(OpenSSL::Digest::SHA256.new, signature, comparison_string.encode("ascii-8bit"))
true
else
false
end
rescue
false
end
end
end
|