From f70165ef6229a92bf8cfa6f16eff980a4a7491fe Mon Sep 17 00:00:00 2001 From: Guillaume Horel Date: Wed, 18 Jan 2012 12:27:37 -0500 Subject: Added setup.py and big reorg of the source tree this should fix tickets #10 and #11 --- server/README | 7 --- server/__init__.py | 3 - server/alias_plugin.py | 93 ---------------------------- server/component.py | 54 ----------------- server/config.ini.sample | 9 --- server/config.py | 18 ------ server/object.py | 151 ---------------------------------------------- server/permission.py | 9 --- server/server.py | 97 ----------------------------- server/user.py | 45 -------------- server/version.py | 5 -- server/xep_0077.py | 154 ----------------------------------------------- 12 files changed, 645 deletions(-) delete mode 100644 server/README delete mode 100644 server/__init__.py delete mode 100644 server/alias_plugin.py delete mode 100644 server/component.py delete mode 100644 server/config.ini.sample delete mode 100644 server/config.py delete mode 100644 server/object.py delete mode 100644 server/permission.py delete mode 100644 server/server.py delete mode 100644 server/user.py delete mode 100644 server/version.py delete mode 100644 server/xep_0077.py (limited to 'server') diff --git a/server/README b/server/README deleted file mode 100644 index 81ee68b..0000000 --- a/server/README +++ /dev/null @@ -1,7 +0,0 @@ -This is Alias server written in python. It depends on SleekXMPP library : - - git clone git://github.com/fritzy/SleekXMPP.git - cd SleekXMPP/ - sudo python setup.py install - -Make sure sleekxmpp is in your pythonpath before starting the server. diff --git a/server/__init__.py b/server/__init__.py deleted file mode 100644 index faaaf79..0000000 --- a/server/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -# -*- coding: utf-8 -*- - - diff --git a/server/alias_plugin.py b/server/alias_plugin.py deleted file mode 100644 index 77bcb5d..0000000 --- a/server/alias_plugin.py +++ /dev/null @@ -1,93 +0,0 @@ -import logging -logger = logging.getLogger(__name__) -import base64 -import hashlib -from xml.etree import cElementTree as ET - -from sleekxmpp.xmlstream.stanzabase import ElementBase, register_stanza_plugin -from sleekxmpp.plugins.base import base_plugin -from sleekxmpp.xmlstream.handler.callback import Callback -from sleekxmpp.xmlstream.matcher.xpath import MatchXPath -from sleekxmpp import Iq - -from object import ObjectReader, ObjectError -from permission import PermissionError -from config import config - -class AliasQuery(ElementBase): - namespace = 'alias:iq:object' - name = 'query' - plugin_attrib = 'alias' - interfaces = set(('node', 'type', 'content', 'permission', 'key')) - sub_interfaces = set(('content', 'permission', 'key')) - - def addItem(self, node, key, permission = None): - item = AliasItem(None, self) - item['node'] = node - item['key'] = key - if permission is not None: - item['permission'] = str(permission) - -class AliasItem(ElementBase): - namespace = 'alias:query' - name = 'item' - plugin_attrib = 'item' - interfaces = set(('node', 'permission', 'key')) - -class AliasPlugin(base_plugin): - - def plugin_init(self): - self.description = 'Plugin to handle alias queries' - register_stanza_plugin(Iq, AliasQuery) - query_parser = MatchXPath('{{{}}}iq/{{{}}}query'.format(self.xmpp.default_ns, - AliasQuery.namespace)) - self.xmpp.register_handler(Callback('Alias queries', query_parser, - self.handle_alias_query)) - - def post_init(self): - base_plugin.post_init(self) - self.xmpp.plugin['xep_0030'].add_feature("alias:query") - - def send_permission_error(self, iq, message): - node = iq['alias']['node'] - iq.reply() - iq['alias']['type'] = 'error' - iq['alias']['node'] = node - iq['alias']['permission'] = message - iq.send() - - def handle_alias_query(self, iq): - caller = iq['from'].bare - - try: - callee = base64.b64decode(iq['to'].user) - except TypeError: - logger.error("callee field not base64 encoded") - - node = iq['alias']['node'] - node = ObjectReader(node, callee) - if iq['alias']['type'] == 'get': - try: - content, key = node.get_content(caller) - except PermissionError: - self.send_permission_error(iq, 'Permission') - else: - iq.reply() - iq['alias']['type'] = 'get' - iq['alias']['node'] = node.hash - iq['alias']['content'] = content - iq['alias']['key'] = key - iq.send() - - if iq['alias']['type'] == 'list': - try: - list = node.get_children_list(caller) - except PermissionError: - self.send_permission_error(iq, 'Permission') - else: - iq.reply() - iq['alias']['type'] = 'content' - iq['alias']['node'] = node.hash - iq['alias']['content'] = content - iq['alias']['key'] = key - iq.send() \ No newline at end of file diff --git a/server/component.py b/server/component.py deleted file mode 100644 index 2ae12d7..0000000 --- a/server/component.py +++ /dev/null @@ -1,54 +0,0 @@ -import sys -import logging -logger = logging.getLogger(__name__) -from sleekxmpp.componentxmpp import ComponentXMPP -from sleekxmpp.xmlstream.xmlstream import XMLStream - -from user import UserHandler - -class ObjectComponent(ComponentXMPP): - - def __init__(self, jid, secret, server, port, root): - ComponentXMPP.__init__(self, jid, secret, server, port) - self.register_plugin('xep_0030') - self.register_plugin('xep_0077', module="xep_0077") - self.register_plugin("AliasPlugin", module = "alias_plugin", pconfig = {'root': root}) - self.add_event_handler("session_start", self.start) - #self.add_event_handler("presence_probe", self.presence_probe) - self.add_event_handler("message", self.message) - self.add_event_handler("changed_subscription", self.presence_subscription) - self.userHandler = UserHandler(root) - - def start(self, event): - for user in self.userHandler.get_user_list(): - self.send_presence(pto = user) - - def disconnect(self, reconnect = False): - for user in self.userHandler.get_user_list(): - self.send_presence(pto = user, ptype = "unavailable") - XMLStream.disconnect(self, reconnect) - logger.info('Component {} disconnected'.format(self.boundjid.bare)) - - def message(self, msg): - msg.reply("Thanks for sending\n{[body]}".format(msg)).send() - - def presence_subscription(self, subscription): - if subscription["type"] == "subscribe": - userJID = subscription["from"].full - if not self.userHandler.registered(userJID): - self.userHandler.register(userJID) - logger.info('registering user {}'.format(userJID)) - subscription.reply().send() - self.send_presence(pto = userJID) - self.send_presence_subscription(pto = userJID, ptype = "subscribe") - if subscription["type"] == "unsubscribe": - userJID = subscription["from"].full - if self.userHandler.registered(userJID): - self.userHandler.unregister(userJID) - logger.info('unregistering user {}'.format(userJID)) - - #def presence_probe(self, event): - # self.send_presence(pto = event["from"].full) - - - diff --git a/server/config.ini.sample b/server/config.ini.sample deleted file mode 100644 index 66dde3a..0000000 --- a/server/config.ini.sample +++ /dev/null @@ -1,9 +0,0 @@ -[component] -name = testg.alias.im -secret = Mvdujq06 -host = alias.im -port = 5349 -root = /var/lib/alias/ -background = True -logfile = /var/log/alias.log -pidfile = /var/run/alias.pid diff --git a/server/config.py b/server/config.py deleted file mode 100644 index 74befe2..0000000 --- a/server/config.py +++ /dev/null @@ -1,18 +0,0 @@ -import ConfigParser - -class AliasConfigParser(ConfigParser.SafeConfigParser): - - def read(self, filename): - ConfigParser.SafeConfigParser.read(self, filename) - self.name = self.get("component", "name") - self.root = self.get("component", "root") - self.host = self.get("component", "host") - self.secret = self.get("component", "secret") - self.port = self.getint("component", "port") - self.background = self.getboolean("component", "background") - if self.has_option("component", "logfile"): - self.logfile = self.get("component", "logfile") - if self.has_option("component", "pidfile"): - self.pidfile = self.get("component", "pidfile") - -config = AliasConfigParser() diff --git a/server/object.py b/server/object.py deleted file mode 100644 index 8dd91a2..0000000 --- a/server/object.py +++ /dev/null @@ -1,151 +0,0 @@ -# -*- coding: utf-8 -*- -#import StringIO -import hashlib -#import sys -#import os -import os.path -#import fileinput -import logging -logger = logging.getLogger(__name__) - -from permission import * -from config import config - -class ObjectError(Exception): - pass; - -class Object: - def __init__(self, name, owner, split_name = True): - self.hash = name - self.owner = owner - if split_name: - self.object_path = os.path.join(config.root, owner, name[:2], name[2:]) - else: - self.object_path = os.path.join(config.root, owner, name) - - def exists(self): - return os.path.exists(self.object_path) - -class ObjectReader(Object): - def __init__(self, hash, owner, split_name = True): - Object.__init__(self, hash, owner, split_name) - if not self.exists(): - logger.error("Object {} can't be found for user {}".format(self.hash, - self.owner)) - raise ObjectError - - def get_permission(self, user): - with open(os.path.join(self.object_path, 'permissions'), 'r') as file: - for line in file: - name, perm, key = line.split() - if name == user: - return int(perm) - return None - - def get_key(self, user): - with open(os.path.join(self.object_path, 'permissions'), 'r') as file: - for line in file: - name, perm, key = line.split() - if name == user: - return key - return None - - def get_permission_key(self, user): - with open(os.path.join(self.object_path, 'permissions'), 'r') as file: - for line in file: - name, perm, key = line.split() - if name == user: - return (int(perm), key) - return (None, None) - - def get_children_list(self, user): - perm = self.get_permission(user) - if not perm or (not perm & LIST): - logger.error("User {} doesn't have the list permission for object {}" - .format(user, self.hash)) - raise PermissionError - - file = open(os.path.join(self.object_path, 'childs'), 'r') - result = [] - for line in file: - name = line.rstrip('\n') - try: - child = ObjectReader(name, self.owner) - except ObjectError: - logger.error('Object {} doesn\'t exist'.format(name)) - else: - perm, key = child.get_permission_key(user) - if perm > 0: - result.append((name, perm, key)) - - file.close() - return result - - def get_content(self, user): - """Return object content and the user key to decrypt it.""" - perm, key = self.get_permission_key(user) - if not perm or (not perm & READ) : - logger.error("User {} doesn't have read access to object {}" - .format(user, self.hash)) - raise PermissionError - with open(os.path.join(self.object_path, 'object'), 'r') as file: - content = file.read() - - return content, key - -class ObjectWriter(ObjectReader): - - def __init__(self, hash, owner, split_name = True, key = None): - Object.__init__(self, hash, owner, split_name) - self.files = ('permissions', 'children', 'object') - self.__create_skeleton(key) - - def __create_skeleton(self, key): - #new object - if not self.exists(): - os.makedirs(self.object_path) - for filename in self.files: - file = open(os.path.join(self.object_path, filename), "w") - file.close() - #give all the permissions to the owner - ALLPERM = READ + MODIFY + APPEND + LIST - self.add_user(self.owner, ALLPERM, key) - - def write(self, user, content): - perm = self.get_permission(user) - if not perm or (not perm & MODIFY): - logger.error("User {} doesn't have the modify permission for object {}" - .format(user, self.hash)) - raise PermissionError - with open(os.path.join(self.object_path, 'object'), "w") as file: - file.write('{}'.format(content)) - - def append(self, user, content, parent): - parent_object = ObjectReader(parent, self.owner) - perm = parent_object.get_permission(user) - if not perm or (not perm & APPEND): - logger.error("User {} doesn't have the modify permission for object {}" - .format(user, parent)) - raise PermissionError - with open(os.path.join(self.object_path, 'object'), "w") as file: - for k, v in content: - file.write('{} {}\n'.format(k,v)) - #add the child hash to the parent - with open(os.path.join(parent_object.object_path, 'children'), "a") as file: - file.write('{} {}\n'.format(self.hash)) - - def add_user(self, user, perm, key = None): - with open(os.path.join(self.object_path, 'permissions'), "a") as file: - if key: - file.write('{} {} {}\n'.format(user, perm, key)) - else: - file.write('{} {} None\n'.format(user, perm, key)) - -if __name__ == '__main__': - jid = 'thrasibule@alias.im' - hash = hashlib.sha1(jid).hexdigest() - config.root = '/var/lib/alias' - print ObjectReader(hash, jid).get_content(jid) - - - diff --git a/server/permission.py b/server/permission.py deleted file mode 100644 index 3ce7323..0000000 --- a/server/permission.py +++ /dev/null @@ -1,9 +0,0 @@ -# -*- coding: utf-8 -*- - -READ = 1 << 0 -MODIFY = 1 << 1 -APPEND = 1 << 2 -LIST = 1 << 3 - -class PermissionError(Exception): - pass \ No newline at end of file diff --git a/server/server.py b/server/server.py deleted file mode 100644 index 4556fb8..0000000 --- a/server/server.py +++ /dev/null @@ -1,97 +0,0 @@ -#!/usr/bin/python2 -import logging -from argparse import ArgumentParser -from config import config -import daemon -import daemon.pidfile -from component import ObjectComponent -import os.path -import sys - -if sys.version_info < (3, 0): - reload(sys) - sys.setdefaultencoding('utf8') -else: - raw_input = input - -if __name__ == '__main__': - commandline = ArgumentParser(description = 'Connect the alias \ - component to a given server') - commandline.add_argument('-p', '--port', - help = 'Port to connect to', - type = int) - commandline.add_argument('-s', '--secret', - help = 'password') - commandline.add_argument('-n', '--name', - help = 'Name the component will have') - commandline.add_argument('-r', '--root', - help = 'Root directory of the user files') - commandline.add_argument('-c', '--config', - help = 'Name of the config file to use') - commandline.add_argument('-d', '--debug', - help = 'Set log level to DEBUG', - action = 'store_const', - const = logging.DEBUG, - default = logging.INFO) - commandline.add_argument('-o', '--host', - help = 'Host to connect to') - commandline.add_argument('-b', '--background', - help = 'run the server in the background', - action = 'store_true') - commandline.add_argument('--logfile', - help = 'location of the log file (default /var/log/${name}.log') - commandline.add_argument('--pidfile', - help = 'location of the pid file (default /var/run/${name}.pid') - args = commandline.parse_args() - - if args.config is None: - config.name = args.name - config.port = args.port - config.secret = args.secret - config.root = args.root - config.host = args.host - config.background = args.background - config.logfile = args.logfile - config.pidfile = args.pidfile - else: - filename = args.config - config.read(filename) - if config.logfile is None: - config.logfile = os.path.join('/var/log/', config.name + '.log') - if config.pidfile is None: - config.pidfile = os.path.join('/var/run/', config.name + '.pid') - - #set up the root logger - logging.getLogger('').setLevel(args.debug) - formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s") - if config.background: - #save logs in a file - fh = logging.FileHandler(config.logfile) - fh.setFormatter(formatter) - logging.getLogger('').addHandler(fh) - else: - #save logs to the console - ch = logging.StreamHandler() - ch.setFormatter(formatter) - logging.getLogger('').addHandler(ch) - - if config.background: - context = daemon.DaemonContext(detach_process = True, - pidfile = daemon.pidfile.TimeoutPIDLockFile(config.pidfile,10), - files_preserve=[fh.stream.fileno()], - working_directory=os.path.abspath(config.root), - stdout=sys.stdout, stderr=sys.stderr) - else: - context = daemon.DaemonContext(detach_process = False, - stdout=sys.stdout, stderr=sys.stderr, - working_directory=os.path.abspath(config.root)) - - with context: - component = ObjectComponent(config.name, config.secret, - config.host, config.port, - config.root) - if component.connect(): - logging.info('Component {} connected'.format(component.boundjid)) - component.process(block=False) - else: - logging.error("Component {} couldn't connect".format(component.boundjid)) diff --git a/server/user.py b/server/user.py deleted file mode 100644 index f6800f9..0000000 --- a/server/user.py +++ /dev/null @@ -1,45 +0,0 @@ -import os -import os.path -import shutil -import hashlib -import logging -logger = logging.getLogger(__name__) -from object import * -import base64 - -class User: - - def __init__(self, jid): - self.jid = jid - self.hash = hashlib.sha256(jid).hexdigest() - #self.hash = hashlib.md5(jid).hexdigest() - - def register(self, registration): - ObjectWriter('pubkey', self.jid, split_name = False).write(self.jid, registration['pubkey']) - #everybody can read the pubkey - ObjectWriter('pubkey', self.jid, split_name = False).add_user('*', READ) - ObjectWriter('privkey', self.jid, split_name = False).write(self.jid, registration['privkey']) - ObjectWriter(self.hash, self.jid) - - def get_registration(self): - registration = {} - registration['pubkey'], ignore = ObjectReader('pubkey',self.jid, split_name = False).get_content(self.jid) - registration['privkey'], ignore = ObjectReader('privkey',self.jid, split_name = False).get_content(self.jid) - return registration - - def is_registered(self): - return Object(self.hash, self.jid).exists() - - def unregister(self, jid): - pass - -class UserHandler: - - def __init__(self, root): - self.root = root - - def get_user_list(self): - return os.listdir(self.root) - -if __name__ == '__main__': - print UserHandler('/var/lib/alias').get_user_list() \ No newline at end of file diff --git a/server/version.py b/server/version.py deleted file mode 100644 index 306cebb..0000000 --- a/server/version.py +++ /dev/null @@ -1,5 +0,0 @@ -# -*- coding: utf-8 -*- -MAJOR=0 -MINOR=1 -TYPE='dev' -VERSION = str(MAJOR)+'.'+str(MINOR)+TYPE #TODO: add the commit number ? diff --git a/server/xep_0077.py b/server/xep_0077.py deleted file mode 100644 index 0608dc5..0000000 --- a/server/xep_0077.py +++ /dev/null @@ -1,154 +0,0 @@ -""" -Creating a SleekXMPP Plugin - -This is a minimal implementation of XEP-0077 to serve -as a tutorial for creating SleekXMPP plugins. -""" - -from sleekxmpp.plugins.base import base_plugin -from sleekxmpp.xmlstream.handler.callback import Callback -from sleekxmpp.xmlstream.matcher.xpath import MatchXPath -from sleekxmpp.xmlstream import ElementBase, ET, register_stanza_plugin -from sleekxmpp import Iq -from user import User -from config import config -from sleekxmpp.plugins.xep_0004 import Form - -import logging -logger = logging.getLogger(__name__) - -class Registration(ElementBase): - namespace = 'jabber:iq:register' - name = 'query' - plugin_attrib = 'register' - interfaces = set(('registered', 'remove', 'instructions', 'form')) - sub_interfaces = interfaces - subitem = (Form,) - - def get_registered(self): - present = self.xml.find('{%s}registered' % self.namespace) - return present is not None - - def get_remove(self): - present = self.xml.find('{%s}remove' % self.namespace) - return present is not None - - def set_registered(self, registered): - if registered: - self.add_field('registered') - else: - del self['registered'] - - def set_remove(self, remove): - if remove: - self.addField('remove') - else: - del self['remove'] - - def add_field(self, name): - itemXML = ET.Element('{%s}%s' % (self.namespace, name)) - self.xml.append(itemXML) - - def add_form(self): - aliasform = Form(None, self) - aliasform.addField(ftype = "hidden", var = "FORM_TYPE", value = "alias:register") - aliasform.addField(var = "pubkey", ftype = "text-single", label = "Public Key", required = True) - aliasform.addField(var = "privkey", ftype = "text-single", label = "Private Key", required = True) - - def get_form(self): - return Form(self.xml.find('{jabber:x:data}x')).getValues() - - def set_form(self, values): - Form(self.xml.find('{jabber:x:data}x')).setValues(values) - -class xep_0077(base_plugin): - """ - XEP-0077 In-Band Registration - """ - - def plugin_init(self): - self.description = "In-Band Registration" - self.xep = "0077" - self.form_fields = ("privkey", "pubkey") - self.form_instructions = "Please provide the following information to register\ - an alias account" - - self.xmpp.register_handler( - Callback('In-Band Registration', - MatchXPath('{%s}iq/{jabber:iq:register}query' % self.xmpp.default_ns), - self.__handle_registration)) - register_stanza_plugin(Iq, Registration) - - def post_init(self): - base_plugin.post_init(self) - self.xmpp['xep_0030'].add_feature("jabber:iq:register") - - def __handle_registration(self, iq): - registrant = User(iq['from'].bare) - logger.info('User {} sent registration iq'.format(iq['from'].bare)) - if iq['type'] == 'get': - # Registration form requested - self.send_registration_form(iq, registrant) - elif iq['type'] == 'set': - if iq['register']['remove']: - # Remove an account - registrant.unregister() - self.xmpp.event('unregistered_user', iq) - iq.reply().send() - return - - registration_info = iq['register']['form'] - for field in self.form_fields: - if not registration_info[field]: - # Incomplete Registration - self._send_error(iq, '406', 'modify', 'not-acceptable', - "Please fill in all fields.") - return - - try: - registrant.register(registration_info) - # Successful registration - #self.xmpp.event('registered_user', iq) - iq.reply().setPayload(iq['register'].xml) - iq.send() - except: - return - else: - # Conflicting registration - self._send_error(iq, '409', 'cancel', 'conflict', - "That username is already taken.") - - def setForm(self, *fields): - self.form_fields = fields - - def set_instructions(self, instructions): - self.form_instructions = instructions - - def send_registration_form(self, iq, registrant): - reg = iq['register'] - reg.add_form() - if self.form_instructions: - reg['instructions'] = self.form_instructions - if registrant.is_registered(): - reg['registered'] = True - reg['form'] = registrant.get_registration() - - iq.reply().setPayload(reg.xml) - iq.send() - - def _send_error(self, iq, code, error_type, name, text = ''): - iq.reply().setPayload(iq['register'].xml) - iq.error() - iq['error']['code'] = code - iq['error']['type'] = error_type - iq['error']['condition'] = name - iq['error']['text'] = text - iq.send() - -if __name__ == '__main__': - test = Registration() - test.add_form() - print '{}\n'.format(test['form']) - values = {'privkey': 'pomme', 'pubkey': 'poire', 'salt': 'abricot'} - test['form']=values - print test -- cgit v1.2.3-70-g09d2