diff --git a/logs.py b/logs.py
index c73bf7d..fb56edd 100644
--- a/logs.py
+++ b/logs.py
@@ -29,8 +29,9 @@ class ResponsesHandler(TemplateHandler):
responses = Response.query().order(-Response.updated).fetch(20)
for r in responses:
- r.source, r.target = [util.pretty_link(url)
- for url in r.key.id().split(' ')]
+ r.source_link = util.pretty_link(r.source())
+ r.target_link = util.pretty_link(r.target())
+ # TODO: support inbound too
if r.direction == 'out' and r.updated >= VERSION_1_DEPLOYED:
r.log_url_path = '/log?' + urllib.urlencode({
'key': r.key.id(),
diff --git a/models.py b/models.py
index 9e0ff49..bd4a6c2 100644
--- a/models.py
+++ b/models.py
@@ -92,6 +92,12 @@ class Response(StringIdModel):
def get_or_create(cls, source=None, target=None, **kwargs):
return cls.get_or_insert(cls._id(source, target), **kwargs)
+ def source(self):
+ return self.key.id().split()[0]
+
+ def target(self):
+ return self.key.id().split()[1]
+
def proxy_url(self):
"""Returns the Bridgy Fed proxy URL to render this response as HTML."""
if self.source_mf2 or self.source_as2 or self.source_atom:
diff --git a/templates/responses.html b/templates/responses.html
index c0414aa..2882810 100644
--- a/templates/responses.html
+++ b/templates/responses.html
@@ -18,12 +18,11 @@
- {{ r.source|safe }} |
- {{ r.target|safe }} |
+ {{ r.source_link|safe }} |
+ {{ r.target_link|safe }} |
{{ r.protocol }} |
{{ r.status }} |
- {# TODO: support inbound too #}
{% if r.log_url_path %}{% endif %}
{{ r.updated.replace(microsecond=0) }}
{% if r.log_url_path %}{% endif %}
diff --git a/webmention.py b/webmention.py
index efe89e9..97fa6d5 100644
--- a/webmention.py
+++ b/webmention.py
@@ -34,15 +34,13 @@ class WebmentionHandler(webapp2.RequestHandler):
"""Handles inbound webmention, converts to ActivityPub or Salmon.
Instance attributes:
- response: Response
+ resp: Response
"""
def post(self):
- logging.info('Params: %s', self.request.params.items())
- source = util.get_required_param(self, 'source')
- target = util.get_required_param(self, 'target')
- logging.info('source target: %s %s', source, target)
+ logging.info('(Params: %s )', self.request.params.items())
+ source = util.get_required_param(self, 'source')
try:
msg = 'Bridgy Fed: new webmention from %s' % source
mail.send_mail(
@@ -52,6 +50,22 @@ class WebmentionHandler(webapp2.RequestHandler):
except BaseException:
logging.warning('Error sending email', exc_info=True)
+ self.resp = None
+ try:
+ self.try_activitypub()
+ if self.resp:
+ self.resp.status = 'complete'
+ except:
+ if self.resp:
+ self.resp.status = 'error'
+ raise
+ finally:
+ if self.resp:
+ self.resp.put()
+
+ def try_activitypub(self):
+ source = util.get_required_param(self, 'source')
+
# fetch source page, convert to ActivityStreams
source_resp = common.requests_get(source)
source_url = source_resp.url or source
@@ -71,18 +85,22 @@ class WebmentionHandler(webapp2.RequestHandler):
common.error(self, 'No u-in-reply-to, u-like-of, or u-repost-of '
'found in %s' % source_url)
+ logging.info('source target: %s %s', source, target)
try:
target_resp = common.get_as2(target)
except (requests.HTTPError, exc.HTTPBadGateway) as e:
if (e.response.status_code // 100 == 2 and
common.content_type(e.response).startswith('text/html')):
- return self.send_salmon(source_obj, source_mf2, target_resp=e.response)
+ self.resp = Response.get_or_create(
+ source=source_url, target=e.response.url or target,
+ direction='out', source_mf2=json.dumps(source_mf2))
+ return self.send_salmon(source_obj, target_resp=e.response)
raise
target_url = target_resp.url or target
- stored_response = Response.get_or_create(
+ self.resp = Response.get_or_create(
source=source_url, target=target_url, direction='out',
- source_mf2=json.dumps(source_mf2))
+ protocol='activitypub', source_mf2=json.dumps(source_mf2))
# find actor's inbox
target_obj = target_resp.json()
@@ -106,7 +124,7 @@ class WebmentionHandler(webapp2.RequestHandler):
# TODO: probably need a way to save errors like this so that we can
# return them if ostatus fails too.
# common.error(self, 'Target actor has no inbox')
- return self.send_salmon(source_obj, source_mf2, target_resp=target_resp)
+ return self.send_salmon(source_obj, target_resp=target_resp)
# convert to AS2
source_domain = urlparse.urlparse(source_url).netloc
@@ -114,7 +132,7 @@ class WebmentionHandler(webapp2.RequestHandler):
source_activity = common.postprocess_as2(
as2.from_as1(source_obj), target=target_obj, key=key)
- if stored_response.status == 'complete':
+ if self.resp.status == 'complete':
source_activity['type'] = 'Update'
# prepare HTTP Signature (required by Mastodon)
@@ -141,23 +159,17 @@ class WebmentionHandler(webapp2.RequestHandler):
'signature verification probably failed. :(\n')
self.response.write(resp.text)
- stored_response.status = 'complete'
- stored_response.protocol = 'activitypub'
- stored_response.put()
+ def send_salmon(self, source_obj, target_resp=None):
+ self.resp.protocol = 'ostatus'
- def send_salmon(self, source_obj, source_mf2, target_url=None, target_resp=None):
# fetch target HTML page, extract Atom rel-alternate link
- if target_url:
- assert not target_resp
- target_resp = common.requests_get(target_url)
- else:
- assert target_resp
- target_url = target_resp.url
+ if not target_resp:
+ target_resp = common.requests_get(self.resp.target())
parsed = BeautifulSoup(target_resp.content, from_encoding=target_resp.encoding)
atom_url = parsed.find('link', rel='alternate', type=common.CONTENT_TYPE_ATOM)
if not atom_url or not atom_url.get('href'):
- common.error(self, 'Target post %s has no Atom link' % target_url,
+ common.error(self, 'Target post %s has no Atom link' % self.resp.target(),
status=400)
# fetch Atom target post, extract and inject id into source object
@@ -190,7 +202,7 @@ class WebmentionHandler(webapp2.RequestHandler):
if not endpoint:
# try webfinger
- parsed = urlparse.urlparse(target_url)
+ parsed = urlparse.urlparse(self.resp.target())
# TODO: test missing email
email = entry.author_detail.get('email') or '@'.join(
(entry.author_detail.name, parsed.netloc))
@@ -208,7 +220,7 @@ class WebmentionHandler(webapp2.RequestHandler):
logging.info('Discovered Salmon endpoint %s', endpoint)
# construct reply Atom object
- source_url = self.request.get('source')
+ source_url = self.resp.source()
activity = (source_obj if source_obj.get('verb') in source.VERBS_WITH_OBJECT
else {'object': source_obj})
entry = atom.activity_to_atom(activity, xml_base=source_url)
@@ -226,10 +238,6 @@ class WebmentionHandler(webapp2.RequestHandler):
endpoint, data=common.XML_UTF8 + magic_envelope,
headers={'Content-Type': common.CONTENT_TYPE_MAGIC_ENVELOPE})
- Response(source=source_url, target=target_url, direction='out',
- protocol = 'ostatus', status = 'complete',
- source_mf2=json.dumps(source_mf2)).put()
-
app = webapp2.WSGIApplication([
('/webmention', WebmentionHandler),
|