146 lines
3.8 KiB
Python
146 lines
3.8 KiB
Python
import json, traceback
|
|
|
|
from izzylib import logging
|
|
from tldextract import extract
|
|
from urllib.parse import urlparse
|
|
|
|
from .database import db
|
|
from .functions import fetch, fetch_actor, get_inbox, push_message
|
|
from .messages import Message
|
|
|
|
|
|
relayed_objects = []
|
|
|
|
|
|
class ProcessData:
|
|
def __init__(self, request, response, data):
|
|
self.request = request
|
|
self.response = response
|
|
self.signature = request.signature
|
|
self.instance = request.instance
|
|
self.actor = request.actor
|
|
self.type = data.type.lower()
|
|
self.data = data
|
|
|
|
|
|
async def __call__(self):
|
|
return getattr(self, f'cmd_{self.type}')()
|
|
|
|
|
|
def valid_type(self):
|
|
if getattr(self, f'cmd_{self.type}', None):
|
|
return True
|
|
|
|
return False
|
|
|
|
|
|
def cmd_follow(self):
|
|
if self.actor.type.lower() != 'application':
|
|
#return self.response.json('No', status=403)
|
|
|
|
Message('reject', self.data.id, self.actor.id).send(self.actor.inbox)
|
|
logging.debug(f'Rejected non-application actor: {self.actor.id}')
|
|
return
|
|
|
|
with db.session as s:
|
|
req_app = s.get.config('require_approval')
|
|
|
|
if not (self.instance and not self.instance.followid):
|
|
data = [
|
|
get_inbox(self.actor),
|
|
self.actor.id
|
|
]
|
|
|
|
if req_app:
|
|
data.append(self.data.id)
|
|
|
|
self.instance = s.put.instance(*data)
|
|
|
|
if not self.instance:
|
|
logging.error(f'Something messed up when inserting "{self.signature.domain}" into the database')
|
|
return
|
|
|
|
message = Message('accept', self.data.id, self.instance.actor)
|
|
resp = message.send(self.instance.inbox)
|
|
|
|
if resp.status not in [200, 202]:
|
|
raise ValueError(f'Error when pushing to "{self.instance.inbox}"')
|
|
|
|
logging.verbose(f'Instance joined the relay: {self.instance.domain}')
|
|
|
|
|
|
def cmd_undo(self):
|
|
if self.actor.type.lower() != 'application':
|
|
return
|
|
|
|
object = fetch(self.data.object, sign=True) if isinstance(self.data.object, str) else self.data.object
|
|
|
|
if object.type != 'Follow':
|
|
return
|
|
|
|
with db.session as s:
|
|
s.delete.instance(self.signature.actor)
|
|
logging.debug(f'Removed instance from relay: {self.instance.domain}')
|
|
|
|
|
|
def cmd_announce(self):
|
|
if isinstance(self.data.object, dict):
|
|
object = self.data.object
|
|
object_id = self.data.object.id
|
|
|
|
else:
|
|
object = fetch(self.data.object, sign=True)
|
|
object_id = self.data.object
|
|
|
|
if not object:
|
|
obj_actor = None
|
|
logging.verbose('Failed to fetch object:', object_id)
|
|
|
|
else:
|
|
obj_actor = fetch(object.attributedTo, sign=True)
|
|
|
|
# I'm pretty sure object.attributedTo is a string, but leaving this here just in case
|
|
#obj_actor = fetch(object.attributedTo, sign=True) if isinstance(object.attributedTo, str) else object.attributedTo
|
|
|
|
if object.id in relayed_objects:
|
|
logging.verbose('Already relayed object:', object.id)
|
|
return
|
|
|
|
if obj_actor:
|
|
obj_handle = obj_actor.preferredUsername
|
|
obj_domain = urlparse(object.id).netloc
|
|
obj_domain_top = extract(obj_domain).registered_domain
|
|
|
|
with db.session as s:
|
|
if any(map(s.get.ban, [None], [obj_domain, obj_domain_top])) or s.get.ban(obj_handle, obj_domain):
|
|
logging.verbose('Refusing to relay object from banned domain or user:', object.id)
|
|
return
|
|
|
|
msg = Message('announce', object.id)
|
|
|
|
for instance in [row for row in s.get.instance_list() if row.domain not in [obj_domain, self.instance.domain]]:
|
|
try:
|
|
response = msg.send(instance.inbox)
|
|
except:
|
|
s.put.retry(instance.inbox, msg)
|
|
traceback.print_exc()
|
|
|
|
if not response or response.status not in [200, 202]:
|
|
logging.verbose(f'Failed to send announce object to {instance.domain}: {object.id}')
|
|
s.put.retry(instance.inbox, msg)
|
|
|
|
if response:
|
|
logging.debug(f'Server error {response.status}: {response.text}')
|
|
|
|
|
|
def cmd_create(self):
|
|
return self.cmd_announce()
|
|
|
|
|
|
def cmd_delete(self):
|
|
logging.verbose('Deletes not implemented yet')
|
|
|
|
|
|
def cmd_update(self):
|
|
logging.verbose('Updates not implemented yet')
|