diff --git a/activitypub.py b/activitypub.py index b4fdbdc4..bc69c296 100644 --- a/activitypub.py +++ b/activitypub.py @@ -1,7 +1,9 @@ """Handles requests for ActivityPub endpoints: actors, inbox, etc. """ +import datetime import json import logging +import string import appengine_config @@ -12,13 +14,15 @@ from oauth_dropins.webutil import util import webapp2 import common -from models import MagicKey +from models import MagicKey, Response +from httpsig.requests_auth import HTTPSignatureAuth SUPPORTED_TYPES = ( 'Announce', 'Article', 'Audio', 'Create', + 'Follow', 'Image', 'Like', 'Note', @@ -83,6 +87,10 @@ class InboxHandler(webapp2.RequestHandler): # TODO: verify signature if there is one + if type == 'Follow': + self.accept_follow(activity) + return + # fetch actor if necessary so we have name, profile photo, etc if type in ('Like', 'Announce'): for elem in obj, activity: @@ -96,6 +104,49 @@ class InboxHandler(webapp2.RequestHandler): common.send_webmentions(self, as1, proxy=True, protocol='activitypub', source_as2=source_as2) + def accept_follow(activity): + logging.info('Sending Accept to inbox') + + accept = { + '@context': 'https://www.w3.org/ns/activitystreams', + 'id': activity['id'], + 'type': 'Accept', + 'actor': activity['object'], + 'object': { + 'type': 'Follow', + 'actor': activity['actor'], + 'object': activity['object'], + } + } + + # source domain - this is still wrong in so many ways ... :) + source_domain = string.replace(activity['object'], 'https://fed.brid.gy/r/', '') + source_domain = string.replace(source_domain, 'http://', '') + source_domain = string.replace(source_domain, 'https://', '') + logging.info('source domain ' + source_domain) + + # inbox url. + target = activity['actor'] + actor = common.get_as2(target).json() + inbox_url = actor.get('inbox') + logging.info('Inbox url ' + inbox_url) + + acct = 'acct:%s@%s' % (source_domain, source_domain) + key = MagicKey.get_or_create(source_domain) + signature = HTTPSignatureAuth(secret=key.private_pem(), key_id=acct, + algorithm='rsa-sha256') + + # deliver source object to target actor's inbox. + headers = { + 'Content-Type': common.CONTENT_TYPE_AS2, + # required for HTTP Signature + # https://tools.ietf.org/html/draft-cavage-http-signatures-07#section-2.1.3 + 'Date': datetime.datetime.utcnow().strftime('%a, %d %b %Y %H:%M:%S GMT'), + 'signature': signature, + } + resp = common.requests_post(inbox_url, json=activity_accept, headers=headers) + self.response.write(resp.text) + app = webapp2.WSGIApplication([ (r'/%s/?' % common.DOMAIN_RE, ActorHandler),