mirror of git://git.psyced.org/git/pypsyc
189 lines
7.4 KiB
Python
189 lines
7.4 KiB
Python
"""
|
|
pypsyc.server.person
|
|
~~~~~~~~~~~~~~~~~~~~
|
|
|
|
:copyright: 2010 by Manuel Jacob
|
|
:license: MIT
|
|
"""
|
|
import hmac
|
|
from hashlib import sha256
|
|
|
|
from pypsyc.server.multicast import ContextSlave
|
|
from pypsyc.protocol import (RoutingErrorRelaying, LinkingServer,
|
|
MessageRelaying, AuthenticationError, EntryDeniedError,
|
|
ContextMaster as ContextMasterProtocol, FriendshipPendingError,
|
|
FriendshipEstablishedError, FriendshipServer as FriendshipProtocol,
|
|
ClientInterfaceServer as ClientInterfaceProtocol)
|
|
from pypsyc.server import _TreeNode
|
|
from pypsyc.util import schedule
|
|
|
|
|
|
class Resource(_TreeNode):
|
|
def __init__(self, parent, resource, circuit):
|
|
_TreeNode.__init__(self, parent=parent, name=resource)
|
|
self.circuit = circuit
|
|
if hasattr(circuit, 'allowed_sources'):
|
|
circuit.allowed_sources.append(parent.uni.chain(resource))
|
|
|
|
def handle_packet(self, header, content):
|
|
self.circuit.send(header, content)
|
|
|
|
|
|
class Person(object):
|
|
def __init__(self, entity):
|
|
self.entity = entity
|
|
self.uni = entity.uni
|
|
self.routing_error_relaying = RoutingErrorRelaying(self)
|
|
entity.add_handler(self.routing_error_relaying)
|
|
entity.add_handler(LinkingServer(self))
|
|
self.message_relaying = MessageRelaying(self)
|
|
entity.add_handler(self.message_relaying)
|
|
entity.add_handler(ContextMasterProtocol(self))
|
|
self.context_slave = ContextSlave(self)
|
|
self.friendship_protocol = FriendshipProtocol(self)
|
|
entity.add_handler(self.friendship_protocol)
|
|
entity.add_handler(ClientInterfaceProtocol(self))
|
|
|
|
def register(self, password):
|
|
"""
|
|
Register this person entity with the given password. This method has to
|
|
be called after the entity was created.
|
|
"""
|
|
sql = ('CREATE TABLE IF NOT EXISTS passwords ('
|
|
'person TEXT PRIMARY KEY, password BLOB)')
|
|
self.entity.server.database.execute(sql)
|
|
sql = ('CREATE TABLE IF NOT EXISTS friendships ('
|
|
'person TEXT, uni TEXT, state TEXT, PRIMARY KEY (person, uni))')
|
|
self.entity.server.database.execute(sql)
|
|
sql = 'INSERT INTO passwords VALUES (?, ?)'
|
|
self.entity.server.database.execute(sql, self.uni, password)
|
|
|
|
def unknown_target_error(self, uni):
|
|
for i in self.entity.children:
|
|
self.routing_error_relaying.relay_unknown_target_error(
|
|
self.uni.chain(i), uni)
|
|
|
|
def delivery_failed_error(self, uni, message):
|
|
for i in self.entity.children:
|
|
self.routing_error_relaying.relay_delivery_failed_error(
|
|
self.uni.chain(i), uni, message)
|
|
|
|
def authenticate(self, pw, circuit, resource):
|
|
sql = 'SELECT password FROM passwords WHERE person = ?'
|
|
db_pass = self.entity.server.database.fetch(sql, self.uni)[0][0]
|
|
type_, req_pass, nonce = pw
|
|
assert type_ == 'hmac'
|
|
if req_pass != hmac.new(db_pass, nonce, sha256).digest():
|
|
raise AuthenticationError("Incorrect password.")
|
|
Resource(self.entity, resource, circuit)
|
|
self.friendship_protocol.cast_presence(7)
|
|
schedule(self._after_authentication, self.uni.chain(resource))
|
|
|
|
def _after_authentication(self, uni):
|
|
sql = 'SELECT uni, state FROM friendships WHERE person = ?'
|
|
rows = self.entity.server.database.fetch(sql, self.uni)
|
|
friendships = dict((i[0], {'state': i[1]}) for i in rows)
|
|
self.friendship_protocol.send_friendships(uni, friendships)
|
|
for context, state in rows:
|
|
if state == 'established':
|
|
self.context_slave.enter(context, uni)
|
|
|
|
def unlink(self, resource):
|
|
del self.entity.children[resource]
|
|
self.friendship_protocol.cast_presence(1)
|
|
self.context_slave.leave_all()
|
|
|
|
def private_message_relay(self, source, target, message):
|
|
if source.is_descendant_of(self.uni):
|
|
self.message_relaying.send_private_message(target, message)
|
|
|
|
def private_message(self, source, message):
|
|
for i in self.entity.children:
|
|
self.message_relaying.relay_private_message(
|
|
source, self.uni.chain(i), message)
|
|
|
|
def friendship_request(self, uni):
|
|
state = self._get_friendship_state(uni)
|
|
if state == 'none':
|
|
self._update_friendship(uni, 'offered', True)
|
|
return 'pending'
|
|
elif state == 'pending':
|
|
self._update_friendship(uni, 'established', False)
|
|
self.context_slave.enter(uni)
|
|
return 'established'
|
|
elif state == 'offered':
|
|
return 'pending'
|
|
elif state == 'established':
|
|
return 'established'
|
|
else:
|
|
raise Exception("unknown friendship state: " + state)
|
|
|
|
def friendship_cancel(self, uni):
|
|
self.context_slave.leave(uni)
|
|
self._update_friendship(uni, None)
|
|
|
|
def client_add_friend(self, uni):
|
|
state = self._get_friendship_state(uni)
|
|
if state == 'none':
|
|
self.friendship_protocol.establish(uni)
|
|
self._update_friendship(uni, 'pending', True)
|
|
elif state == 'pending':
|
|
raise FriendshipPendingError
|
|
elif state == 'offered':
|
|
self.friendship_protocol.establish(uni)
|
|
self._update_friendship(uni, 'established', False)
|
|
self.context_slave.enter(uni)
|
|
elif state == 'established':
|
|
raise FriendshipEstablishedError
|
|
else:
|
|
raise Exception("unknown friendship state: " + state)
|
|
|
|
def client_remove_friend(self, uni):
|
|
self.context_slave.leave(uni)
|
|
self.friendship_protocol.remove(uni)
|
|
self._update_friendship(uni, None)
|
|
|
|
def _get_friendship_state(self, uni):
|
|
sql = 'SELECT state FROM friendships WHERE person = ? AND uni = ?'
|
|
ret = self.entity.server.database.fetch(sql, self.uni, uni)
|
|
if ret:
|
|
return ret[0][0]
|
|
return 'none'
|
|
|
|
def _update_friendship(self, uni, state, insert=False):
|
|
if state is None:
|
|
sql = 'DELETE FROM friendships WHERE person = ? AND uni = ?'
|
|
self.entity.server.database.execute(sql, self.uni, uni)
|
|
elif insert:
|
|
sql = 'INSERT INTO friendships VALUES(?, ?, ?)'
|
|
self.entity.server.database.execute(sql, self.uni, uni, state)
|
|
else:
|
|
sql = ('UPDATE friendships SET state = ? '
|
|
'WHERE person = ? AND uni = ?')
|
|
self.entity.server.database.execute(sql, state, self.uni, uni)
|
|
for resource in self.entity.children:
|
|
if state:
|
|
self.friendship_protocol.send_updated_friendship(
|
|
self.uni.chain(resource), uni, {'state': state})
|
|
else:
|
|
self.friendship_protocol.send_removed_friendship(
|
|
self.uni.chain(resource), uni)
|
|
|
|
def enter_request(self, uni):
|
|
if self._get_friendship_state(uni) == 'established':
|
|
return self.entity.context_master.add_member(uni)
|
|
else:
|
|
raise EntryDeniedError("You are not my friend!")
|
|
|
|
def leave_context(self, uni):
|
|
self.entity.context_master.remove_member(uni)
|
|
|
|
def client_presence(self, availability):
|
|
self.friendship_protocol.cast_presence(availability)
|
|
|
|
def client_subscribe(self, uni, resource_uni):
|
|
self.context_slave.enter(uni, resource_uni)
|
|
|
|
def client_unsubscribe(self, uni, resource_uni):
|
|
self.context_slave.leave(uni, resource_uni)
|