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}"
class Application
def call(env)
code = 404
type = "application/activity+json"
case env['REQUEST_METHOD']
when 'POST'
case env["REQUEST_URI"]
when "/inbox"
type = "text/plain"
signature_header = {}
if env["HTTP_SIGNATURE"].split(',')
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"))
input = JSON.parse(env["rack.input"].gets)
# p input
code = 200
response = "OK"
else
code = 401
response = 'Request signature could not be verified'
end
else
code = 401
response = 'Request signature could not be verified'
end
end
when 'GET'
case env["REQUEST_URI"]
when "/.well-known/webfinger?resource=acct:#{ACCOUNT}"
code = 200
type = "application/jrd+json"
response = {
"subject" => "acct:#{ACCOUNT}",
"links" => [
{
"rel" => "self",
"type" => "application/activity+json",
"href" => ACTOR
}
]
}
when "/#{USER}"
code = 200
response = {
"@context" => ["https://www.w3.org/ns/activitystreams"],
"id" => ACTOR,
"type" => "Person",
"preferredUsername" => USER,
"name" => USER,
"inbox" => URI.join(SOCIAL_URL, "inbox"),
"outbox" => URI.join(SOCIAL_URL, "outbox"),
"following" => URI.join(SOCIAL_URL, "following"),
"followers" => URI.join(SOCIAL_URL, "followers"),
"liked" => URI.join(SOCIAL_URL, "liked"),
"icon" => {
"type" => "Image",
"url" => "https://pdp8.info/pdp8.png"
},
"attachment": [
{
"type": "PropertyValue",
"name": "Web",
"value": "#{WWW_DOMAIN}"
},
{
"type": "PropertyValue",
"name": "Fediverse",
"value": "@#{ACCOUNT}"
},
{
"type": "PropertyValue",
"name": "Matrix",
"value": "#{MATRIX}"
}
],
"publicKey" => {
"@context" => "https://w3id.org/security/v1",
"@type" => "Key",
"id" => "#{ACTOR}#main-key",
"owner" => ACTOR,
"publicKeyPem" => File.read("public.pem")
}
}
when "/outbox"
code = 200
type = "application/activity+json"
response = {
"@context" => "https://www.w3.org/ns/activitystreams",
"summary" => "",
"type" => "OrderedCollection",
"totalItems" => 2,
# TODO generate items from src/www
"orderedItems" => [
{
"type" => "Note",
"name" => "A Simple Note",
"tag" => [
{
"type" => "Hashtag",
"name" => "#activitypub",
"href" => "https://s3lph.me/activitypub/tags/activitypub"
},
]
},
{
"type" => "Note",
"name" => "Another Simple Note"
}
]
}
when "/following"
when "/followers"
when "/liked"
end
end
[code, { "Content-Type" => type }, [response.to_json]]
end
end