예제 #1
0
    def test_atom_to_activity_reply(self):
        expected = {
            'objectType': 'activity',
            'id': 'reply-url',
            'url': 'reply-url',
            'inReplyTo': [{
                'id': 'foo-id',
                'url': 'foo-url'
            }],
            'object': {
                'id': 'reply-url',
                'url': 'reply-url',
                'content': 'I hereby ☕ reply.',
                'inReplyTo': [{
                    'id': 'foo-id',
                    'url': 'foo-url'
                }],
            },
        }
        self.assert_equals(
            expected,
            atom.atom_to_activity(u"""\
<?xml version="1.0" encoding="UTF-8"?>
<entry xmlns="http://www.w3.org/2005/Atom"
       xmlns:thr="http://purl.org/syndication/thread/1.0">
<uri>reply-url</uri>
<thr:in-reply-to ref="foo-id" href="foo-url" />
<content>I hereby ☕ reply.</content>
</entry>
"""))
예제 #2
0
def render():
    """Fetches a stored Response and renders it as HTML."""
    source = flask_util.get_required_param('source')
    target = flask_util.get_required_param('target')

    id = f'{source} {target}'
    resp = Response.get_by_id(id)
    if not resp:
        error(f'No stored response for {id}', status=404)

    if resp.source_mf2:
        as1 = microformats2.json_to_object(json_loads(resp.source_mf2))
    elif resp.source_as2:
        as1 = as2.to_as1(json_loads(resp.source_as2))
    elif resp.source_atom:
        as1 = atom.atom_to_activity(resp.source_atom)
    else:
        error(f'Stored response for {id} has no data', status=404)

    # add HTML meta redirect to source page. should trigger for end users in
    # browsers but not for webmention receivers (hopefully).
    html = microformats2.activities_to_html([as1])
    utf8 = '<meta charset="utf-8">'
    refresh = f'<meta http-equiv="refresh" content="0;url={source}">'
    return html.replace(utf8, utf8 + '\n' + refresh)
예제 #3
0
    def get(self):
        source = util.get_required_param(self, 'source')
        target = util.get_required_param(self, 'target')

        id = '%s %s' % (source, target)
        resp = Response.get_by_id(id)
        if not resp:
            self.abort(404, 'No stored response for %s' % id)

        if resp.source_mf2:
            as1 = microformats2.json_to_object(json.loads(resp.source_mf2))
        elif resp.source_as2:
            as1 = as2.to_as1(json.loads(resp.source_as2))
        elif resp.source_atom:
            as1 = atom.atom_to_activity(resp.source_atom)
        else:
            self.abort(404, 'Stored response for %s has no data' % id)

        # add HTML meta redirect to source page. should trigger for end users in
        # browsers but not for webmention receivers (hopefully).
        html = microformats2.activities_to_html([as1])
        utf8 = '<meta charset="utf-8">'
        refresh = '<meta http-equiv="refresh" content="0;url=%s">' % source
        html = html.replace(utf8, utf8 + '\n' + refresh)

        self.response.write(html)
예제 #4
0
    def test_atom_to_activity_like(self):
        for atom_obj, as_obj in (
            ('foo', {
                'id': 'foo',
                'url': 'foo'
            }),
            ('<id>foo</id>', {
                'id': 'foo'
            }),
            ('<uri>foo</uri>', {
                'id': 'foo',
                'url': 'foo'
            }),
        ):
            self.assert_equals(
                {
                    'url': 'like-url',
                    'objectType': 'activity',
                    'verb': 'like',
                    'object': as_obj,
                },
                atom.atom_to_activity(u"""\
<?xml version="1.0" encoding="UTF-8"?>
<entry xmlns="http://www.w3.org/2005/Atom"
       xmlns:activity="http://activitystrea.ms/spec/1.0/">
<uri>like-url</uri>
<activity:verb>http://activitystrea.ms/schema/1.0/like</activity:verb>
<activity:object>%s</activity:object>
</entry>
""" % atom_obj))
예제 #5
0
def slap(acct):
    """Accepts POSTs to /[ACCT]/salmon and converts to outbound webmentions."""
    # TODO: unify with activitypub
    body = request.get_data(as_text=True)
    logging.info(f'Got: {body}')

    try:
        parsed = utils.parse_magic_envelope(body)
    except ParseError as e:
        error('Could not parse POST body as XML', exc_info=True)
    data = parsed['data']
    logging.info(f'Decoded: {data}')

    # check that we support this activity type
    try:
        activity = atom.atom_to_activity(data)
    except ParseError as e:
        error('Could not parse envelope data as XML', exc_info=True)

    verb = activity.get('verb')
    if verb and verb not in SUPPORTED_VERBS:
        error(f'Sorry, {verb} activities are not supported yet.', status=501)

    # verify author and signature
    author = util.get_url(activity.get('actor'))
    if ':' not in author:
        author = f'acct:{author}'
    elif not author.startswith('acct:'):
        error(f'Author URI {author} has unsupported scheme; expected acct:')

    logging.info(f'Fetching Salmon key for {author}')
    if not magicsigs.verify(data, parsed['sig'], author_uri=author):
        error('Could not verify magic signature.')
    logging.info('Verified magic signature.')

    # Verify that the timestamp is recent. Required by spec.
    # I get that this helps prevent spam, but in practice it's a bit silly,
    # and other major implementations don't (e.g. Mastodon), so forget it.
    #
    # updated = utils.parse_updated_from_atom(data)
    # if not utils.verify_timestamp(updated):
    #     error('Timestamp is more than 1h old.')

    # send webmentions to each target
    activity = atom.atom_to_activity(data)
    common.send_webmentions(activity, protocol='ostatus', source_atom=data)
    return ''
예제 #6
0
    def post(self, username, domain):
        logging.info('Got: %s', self.request.body)

        try:
            parsed = utils.parse_magic_envelope(self.request.body)
        except ParseError as e:
            self.error('Could not parse POST body as XML', exc_info=True)
        data = parsed['data']
        logging.info('Decoded: %s', data)

        # check that we support this activity type
        try:
            activity = atom.atom_to_activity(data)
        except ParseError as e:
            self.error('Could not parse envelope data as XML', exc_info=True)

        verb = activity.get('verb')
        if verb and verb not in SUPPORTED_VERBS:
            self.error('Sorry, %s activities are not supported yet.' % verb,
                       status=501)

        # verify author and signature
        author = util.get_url(activity.get('actor'))
        if ':' not in author:
            author = 'acct:%s' % author
        elif not author.startswith('acct:'):
            self.error('Author URI %s has unsupported scheme; expected acct:' %
                       author)

        logging.info('Fetching Salmon key for %s' % author)
        if not magicsigs.verify(author, data, parsed['sig']):
            self.error('Could not verify magic signature.')
        logging.info('Verified magic signature.')

        # Verify that the timestamp is recent. Required by spec.
        # I get that this helps prevent spam, but in practice it's a bit silly,
        # and other major implementations don't (e.g. Mastodon), so forget it.
        #
        # updated = utils.parse_updated_from_atom(data)
        # if not utils.verify_timestamp(updated):
        #     self.error('Timestamp is more than 1h old.')

        # send webmentions to each target
        activity = atom.atom_to_activity(data)
        self.send_webmentions(activity, protocol='ostatus', source_atom=data)
예제 #7
0
  def test_atom_to_activity_unicode_title(self):
    """Unicode smart quote in the <title> element."""
    self.assert_equals({
      'objectType': 'activity',
      'object': {
        'title': 'How quill’s editor looks',
      },
    }, atom.atom_to_activity(u"""\
<?xml version='1.0' encoding='UTF-8'?>
<entry xmlns='http://www.w3.org/2005/Atom'>
<title>How quill’s editor looks</title>
</entry>
"""))
예제 #8
0
  def test_atom_to_activity_in_reply_to_text(self):
    expected = {
      'objectType': 'activity',
      'inReplyTo': [{'id': 'my-inreplyto', 'url': 'my-inreplyto'}],
      'object': {
        'inReplyTo': [{'id': 'my-inreplyto', 'url': 'my-inreplyto'}],
      },
    }
    self.assert_equals(expected, atom.atom_to_activity(u"""\
<?xml version="1.0" encoding="UTF-8"?>
<entry xmlns:thr="http://purl.org/syndication/thread/1.0">
<thr:in-reply-to>my-inreplyto</thr:in-reply-to>
</entry>
"""))
예제 #9
0
    def test_atom_to_activity_unicode_title(self):
        """Unicode smart quote in the <title> element."""
        self.assert_equals(
            {
                'objectType': 'activity',
                'object': {
                    'title': 'How quill’s editor looks',
                },
            },
            atom.atom_to_activity(u"""\
<?xml version='1.0' encoding='UTF-8'?>
<entry xmlns='http://www.w3.org/2005/Atom'>
<title>How quill’s editor looks</title>
</entry>
"""))
예제 #10
0
    def get(self):
        source = util.get_required_param(self, 'source')
        target = util.get_required_param(self, 'target')

        id = '%s %s' % (source, target)
        resp = Response.get_by_id(id)
        if not resp:
            self.abort(404, 'No stored response for %s' % id)

        if resp.source_mf2:
            as1 = microformats2.json_to_object(json.loads(resp.source_mf2))
        elif resp.source_as2:
            as1 = as2.to_as1(json.loads(resp.source_as2))
        elif resp.source_atom:
            as1 = atom.atom_to_activity(resp.source_atom)
        else:
            self.abort(404, 'Stored response for %s has no data' % id)

        self.response.write(microformats2.activities_to_html([as1]))
예제 #11
0
  def test_atom_to_activity_like(self):
    for atom_obj, as_obj in (
        ('foo', {'id': 'foo', 'url': 'foo'}),
        ('<id>foo</id>', {'id': 'foo'}),
        ('<uri>foo</uri>', {'id': 'foo', 'url': 'foo'}),
      ):
      self.assert_equals({
        'url': 'like-url',
        'objectType': 'activity',
        'verb': 'like',
        'object': as_obj,
      }, atom.atom_to_activity(u"""\
<?xml version="1.0" encoding="UTF-8"?>
<entry xmlns="http://www.w3.org/2005/Atom"
       xmlns:activity="http://activitystrea.ms/spec/1.0/">
<uri>like-url</uri>
<activity:verb>http://activitystrea.ms/schema/1.0/like</activity:verb>
<activity:object>%s</activity:object>
</entry>
""" % atom_obj))
예제 #12
0
    def test_atom_to_activity_in_reply_to_text(self):
        expected = {
            'objectType': 'activity',
            'inReplyTo': [{
                'id': 'my-inreplyto',
                'url': 'my-inreplyto'
            }],
            'object': {
                'inReplyTo': [{
                    'id': 'my-inreplyto',
                    'url': 'my-inreplyto'
                }],
            },
        }
        self.assert_equals(
            expected,
            atom.atom_to_activity(u"""\
<?xml version="1.0" encoding="UTF-8"?>
<entry xmlns:thr="http://purl.org/syndication/thread/1.0">
<thr:in-reply-to>my-inreplyto</thr:in-reply-to>
</entry>
"""))
예제 #13
0
  def test_atom_to_activity_reply(self):
    expected = {
      'objectType': 'activity',
      'id': 'reply-url',
      'url': 'reply-url',
      'inReplyTo': [{'id': 'foo-id', 'url': 'foo-url'}],
      'object': {
        'id': 'reply-url',
        'url': 'reply-url',
        'content': 'I hereby ☕ reply.',
        'inReplyTo': [{'id': 'foo-id', 'url': 'foo-url'}],
      },
    }
    self.assert_equals(expected, atom.atom_to_activity(u"""\
<?xml version="1.0" encoding="UTF-8"?>
<entry xmlns="http://www.w3.org/2005/Atom"
       xmlns:thr="http://purl.org/syndication/thread/1.0">
<uri>reply-url</uri>
<thr:in-reply-to ref="foo-id" href="foo-url" />
<content>I hereby ☕ reply.</content>
</entry>
"""))
예제 #14
0
 def test_atom_to_activity(self):
     self.assert_equals(INSTAGRAM_ACTIVITY,
                        atom.atom_to_activity(INSTAGRAM_ENTRY))
예제 #15
0
 def test_atom_to_activity(self):
   self.assert_equals(INSTAGRAM_ACTIVITY,
                      atom.atom_to_activity(INSTAGRAM_ENTRY))