aboutsummaryrefslogtreecommitdiffstats
path: root/server
diff options
context:
space:
mode:
Diffstat (limited to 'server')
-rw-r--r--server/README7
-rw-r--r--server/__init__.py3
-rw-r--r--server/config.ini.sample6
-rw-r--r--server/config.py15
-rw-r--r--server/crypto.py28
-rw-r--r--server/object.py211
-rw-r--r--server/permission.py6
-rw-r--r--server/plugin.py61
-rwxr-xr-xserver/server.py97
-rw-r--r--server/ssh_rsa_key_util.py209
-rw-r--r--server/user.py32
-rw-r--r--server/version.py5
12 files changed, 680 insertions, 0 deletions
diff --git a/server/README b/server/README
new file mode 100644
index 0000000..81ee68b
--- /dev/null
+++ b/server/README
@@ -0,0 +1,7 @@
+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
new file mode 100644
index 0000000..faaaf79
--- /dev/null
+++ b/server/__init__.py
@@ -0,0 +1,3 @@
+# -*- coding: utf-8 -*-
+
+
diff --git a/server/config.ini.sample b/server/config.ini.sample
new file mode 100644
index 0000000..b25b281
--- /dev/null
+++ b/server/config.ini.sample
@@ -0,0 +1,6 @@
+[component]
+name = object.alias.fr.nf
+secret = xxxx
+host = alias.fr.nf
+port = 5347
+root = users/
diff --git a/server/config.py b/server/config.py
new file mode 100644
index 0000000..d50d4d7
--- /dev/null
+++ b/server/config.py
@@ -0,0 +1,15 @@
+import ConfigParser
+
+filename = None
+
+class AliasConfigParser(ConfigParser.RawConfigParser):
+
+ def read(self, filenames):
+ ConfigParser.RawConfigParser.read(self, filenames)
+ 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")
+
+config = AliasConfigParser()
diff --git a/server/crypto.py b/server/crypto.py
new file mode 100644
index 0000000..f99001f
--- /dev/null
+++ b/server/crypto.py
@@ -0,0 +1,28 @@
+from keyczar import keys
+import ssh_rsa_key_util
+
+if __name__ == '__main__' :
+ #load asymmetric keys for 2 users. user1 key is generated,
+ #while user2 key is loaded from disk
+ keyuser1 = keys.RsaPrivateKey.Generate()
+ keyuser1public = keyuser1.public_key
+ ssh_keys_directory = '/home/guillaume/.ssh/'
+ keyuser2 = ssh_rsa_key_util.SshRsaPrivateKey.Read(ssh_keys_directory + 'id_rsa')
+ keyuser2public = keyuser2.public_key
+
+ #symmetric key to encrypt the data
+ key = keys.AesKey.Generate()
+ #encrypt symmetric key for user 1 and 2
+ key1 = keyuser1public.Encrypt(key.key_string)
+ key2 = keyuser2public.Encrypt(key.key_string)
+
+ msg = 'Guillaume is a genius!'
+ secretmsg = key.Encrypt(msg)
+
+ #msg decoded by user 1
+ newkey1 = keys.AesKey(keyuser1.Decrypt(key1), key.hmac_key)
+ print newkey1.Decrypt(secretmsg)
+
+ #msg decoded by user 2
+ newkey2 = keys.AesKey(keyuser1.Decrypt(key1), key.hmac_key)
+ print newkey2.Decrypt(secretmsg)
diff --git a/server/object.py b/server/object.py
new file mode 100644
index 0000000..866f7bb
--- /dev/null
+++ b/server/object.py
@@ -0,0 +1,211 @@
+# -*- coding: utf-8 -*-
+import StringIO
+import hashlib
+import sys
+import os
+import os.path
+import zlib
+import datetime
+import fileinput
+import logging
+
+from permission import *
+from config import config
+
+class Object:
+
+ def __get_path(self):
+ return self.name[:2] + '/' + self.name[2:]
+
+ def __create_dir(self):
+ if (not os.path.exists(self.name[:2])):
+ os.mkdir(self.name[:2])
+
+ if (not os.path.exists(self.path)):
+ os.mkdir(self.path)
+
+ ##
+ # Save the object on the disk.
+ def save(self):
+ self.__create_dir()
+
+ header = "author " + self.data['author'] + "\n"
+ header += "parent " + self.data['parent'] + "\n"
+ header += "created " + self.data['created'] + "\n"
+ header += "#\n" #end of header
+ store = header + self.data['content']
+
+ file = open(self.path + "/object", 'w')
+ file.write(zlib.compress(store))
+ file.close()
+ os.mknod(self.path + "/childs")
+ os.mknod(self.path + "/permissions")
+ self.saved = True
+
+ ##
+ # Class constructor.
+ def __init__(self, init):
+
+ if isinstance(init, dict):
+ if 'author' not in init:
+ init['author'] = 'None'
+ if 'parent' not in init:
+ init['parent'] = 'None'
+ if 'content' not in init:
+ init['content'] = 'None'
+
+ self.data = init
+ self.data['created'] = str(datetime.datetime.now())
+ self.name = hashlib.sha1(str(self.data)).hexdigest()
+ self.path = self.__get_path()
+ self.saved = False
+
+ elif isinstance(init, str):
+ self.name = init
+ self.path = self.__get_path()
+ if (os.path.exists(self.path)):
+ file = open(self.path + '/object', 'r')
+ contentStream = StringIO.StringIO(zlib.decompress(file.read()))
+ data = {}
+
+ for line in contentStream:
+ if (line == "#\n"):
+ data['content'] = contentStream.read()
+ break
+ else:
+ key, sep, value = line.rstrip('\n').partition(' ')
+ data[key] = value
+
+ self.data = data
+ file.close()
+ else:
+ logging.error("Object {} root doesn't exist".format(init))
+
+ def append_to(self, father):
+ father.append_child(self)
+
+ def append_child(self, child):
+ if not self.saved:
+ self.save()
+
+ file = open(self.path + "/childs", 'a')
+ file.write(child.name + "\n")
+ file.close()
+
+ @staticmethod
+ def get_permission_by_hash(hash, user):
+ path = hash[:2] + '/' + hash[2:]
+ try:
+ file = open(path + '/permissions', 'r')
+ except IOError:
+ print 'cannot open', path
+ else:
+ for line in file:
+ name, sep, perm = line.rstrip('\n').partition(' ')
+ if name == user:
+ return perm
+
+ return 0
+
+ def get_permission(self, user):
+ file = open(self.path + '/permissions', 'r')
+ for line in file:
+ name, sep, perm = line.rstrip('\n').partition(' ')
+ if name == user:
+ return perm
+
+ return 0
+
+ def set_permission(self, user, permission):
+ if not self.saved:
+ self.save()
+
+ sentinel = False
+ for line in fileinput.input(self.path + "/permissions", inplace = 1):
+ name, sep, perm = line.rstrip('\n').partition(' ')
+ if name == user:
+ sys.stdout.write(name + ' ' + str(permission) + '\n')
+ sentinel = True
+ else:
+ sys.stdout.write(line)
+
+ if not sentinel:
+ file = open(self.path + '/permissions', 'a')
+ file.write(user + ' ' + str(permission) + '\n')
+ file.close()
+
+class ObjectHandler:
+
+ def __init__(self, user):
+ self.user = user
+ self.root_object = hashlib.sha1(user).hexdigest()
+
+ if not os.path.exists(self.user):
+ logging.error("User {} root doesn't exist".format(self.user))
+
+ def get_object_directory(self, hash):
+ directory = self.user + '/' + hash[:2] + '/' + hash[2:]
+ if (os.path.exists(directory)):
+ return directory
+ else:
+ return None
+
+ def get_object(self, hash):
+ os.chdir(self.user)
+ temp = Object(hash)
+ os.chdir('..')
+ return temp
+
+ #return a list of hash,permission pairs
+ def get_child_list(self, hash, user):
+ directory = self.get_object_directory(hash)
+ if directory:
+ result = []
+ file = open(directory + "/childs", 'r')
+ for line in file :
+ name = line.rstrip('\n')
+ result.append(name)
+ file.close()
+ return result
+ else :
+ return None
+
+ def create_home_node(self):
+ pass
+
+ def get_home_node(self):
+ return self.root_object
+
+if __name__ == '__main__':
+ config.root = os.path.abspath('object_tests')
+ if not(os.path.exists(config.root)):
+ os.mkdir(config.root)
+ os.chdir(config.root)
+ if not(os.path.exists('guillaume')):
+ os.mkdir('guillaume')
+
+ guillaume_stuff = ObjectHandler('guillaume')
+ os.chdir('guillaume')
+ x = Object({'parent' : 'toto', 'author' : 'Zaran'})
+ x.set_permission("test", READ)
+ x.set_permission("toto", READ | MODIFY)
+ x.set_permission("toto", READ | MODIFY | APPEND)
+ x.set_permission("toto2", READ | MODIFY | APPEND)
+ print x.get_permission("toto")
+ print Object.get_permission_by_hash(x.name, "toto")
+ child = Object({'parent' : x.name, 'author' : 'Zaran'})
+ child.save()
+
+ x.append_child(child)
+ os.chdir(config.root)
+
+ y = guillaume_stuff.get_object(x.name)
+ pomme = guillaume_stuff.get_child_list(x.name, None)
+ print pomme
+ print y.data['author']
+ print y.data['parent']
+ print y.data['content']
+ print y.data['created']
+
+
+
diff --git a/server/permission.py b/server/permission.py
new file mode 100644
index 0000000..fe93928
--- /dev/null
+++ b/server/permission.py
@@ -0,0 +1,6 @@
+# -*- coding: utf-8 -*-
+
+READ = 1 << 0
+MODIFY = 1 << 1
+APPEND = 1 << 2
+ \ No newline at end of file
diff --git a/server/plugin.py b/server/plugin.py
new file mode 100644
index 0000000..df05908
--- /dev/null
+++ b/server/plugin.py
@@ -0,0 +1,61 @@
+import logging
+import base64
+from xml.etree import cElementTree as ET
+
+from sleekxmpp.xmlstream.stanzabase import ElementBase, register_stanza_plugin
+from sleekxmpp.plugins import base
+from sleekxmpp.xmlstream.handler.callback import Callback
+from sleekxmpp.xmlstream.matcher.xpath import MatchXPath
+from sleekxmpp.stanza.iq import Iq
+
+import object
+
+class AliasQuery(ElementBase):
+ namespace = 'alias:query'
+ name = 'query'
+ plugin_attrib = 'alias'
+ interfaces = set(('node', 'type', 'items', 'object'))
+ sub_interfaces = set(('items'))
+
+ def addItem(self, node, permission = None):
+ item = AliasItem(None, self)
+ item['node'] = node
+ if permission is not None:
+ item['permission'] = permission
+
+class AliasItem(ElementBase):
+ namespace = 'alias:query'
+ name = 'item'
+ plugin_attrib = 'item'
+ interfaces = set(('node', 'permission'))
+
+class AliasPlugin(base.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.base_plugin.post_init(self)
+ self.xmpp.plugin['xep_0030'].add_feature("alias:query")
+
+ def handle_alias_query(self, iq):
+ callee = base64.b64decode(iq['to'].user)
+ caller = iq['alias']['from']
+ handler = object.ObjectHandler(callee)
+ node = iq['alias']['node']
+ if not node:
+ node = handler.get_home_node()
+ if iq['alias']['type'] == 'items':
+ logging.debug('childs of {} requested'.format(node))
+ childs = handler.get_child_list(node, caller)
+ reply = AliasQuery()
+ reply['node'] = node
+ reply['type'] = 'items'
+ for child in childs:
+ reply.addItem(child, "test")
+ iq.reply().set_payload(reply).send() \ No newline at end of file
diff --git a/server/server.py b/server/server.py
new file mode 100755
index 0000000..d78552d
--- /dev/null
+++ b/server/server.py
@@ -0,0 +1,97 @@
+#!/usr/bin/python2
+import logging
+from argparse import ArgumentParser
+
+from sleekxmpp.componentxmpp import ComponentXMPP
+from sleekxmpp.xmlstream.xmlstream import XMLStream
+
+from user import UserHandler
+from config import filename, config
+
+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("AliasPlugin", module = "plugin")
+ 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)
+ logging.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
+ self.userHandler.register(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
+ self.userHandler.unregister(userJID)
+
+ def presence_probe(self, event):
+ self.send_presence(pto = event["from"].full)
+
+
+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')
+
+ args = commandline.parse_args()
+
+ if args.config is None:
+ logging.basicConfig(level = args.debug)
+ config.name = args.name
+ config.port = args.port
+ config.secret = args.secret
+ config.root = args.root
+ config.host = args.host
+ else:
+ filename = args.config
+ logging.basicConfig(level = args.debug)
+ config.read(filename)
+
+ component = ObjectComponent(config.name, config.secret,
+ config.host, config.port,
+ config.root)
+
+ if component.connect():
+ logging.info('Component {} connected'.format(component.boundjid))
+ component.process(False)
+ else :
+ print "Couldn't connect"
+
+
diff --git a/server/ssh_rsa_key_util.py b/server/ssh_rsa_key_util.py
new file mode 100644
index 0000000..c25a112
--- /dev/null
+++ b/server/ssh_rsa_key_util.py
@@ -0,0 +1,209 @@
+import base64
+import struct
+import filecmp
+from keyczar import util, keys
+from Crypto.PublicKey import RSA
+
+# need pyasn for DER parsing and generating
+from pyasn1.type import univ
+from pyasn1.codec.der import decoder, encoder
+
+def read_int(buffer, i):
+ "Read 32bit integer from buffer."
+
+ (l,) = struct.unpack('!I', buffer[i:i + 4])
+ i += 4
+ return (l, i)
+
+def read_chunk(buffer, i):
+ "Read chunk from buffer."
+
+ # first grab length of chunk
+ (l, i) = read_int(buffer, i)
+ if l > 1000000:
+ # just in case... if this happens, then something is way off
+ raise Exception("got chunk length of %d, that's certainly too long" % l)
+
+ # read chunk of length l
+ (s,) = struct.unpack('!{0:d}s'.format(l), buffer[i:i + l])
+ i += l
+ return (s, i)
+
+def read_rsa_pub(filename):
+ """Read RSA public key file. Structure:
+
+ ssh-rsa base64data user@host
+
+ base64data: [7]ssh-rsa[len][e-data][len][n-data]
+ """
+
+ [prefix, data, host] = file(filename, 'r').read().split()
+ raw = base64.b64decode(data)
+
+ # read type string
+ i = 0
+ (s, i) = read_chunk(raw, i)
+ if s != 'ssh-rsa':
+ raise Exception("expected string 'ssh-rsa' but got '%s'" % s)
+
+ # grab e
+ (s, i) = read_chunk(raw, i)
+ e = util.BytesToLong(s)
+ # grab n
+ (s, i) = read_chunk(raw, i)
+ n = util.BytesToLong(s)
+ return (n, e, host)
+
+def write_rsa_pub(filename, n, e, host):
+ """Write RSA public key file. Structure:
+
+ ssh-rsa base64data user@host
+
+ base64data: [7]ssh-rsa[len][e-data][len][n-data]
+ """
+ e_str = util.BigIntToBytes(e)
+ n_str = util.BigIntToBytes(n)
+
+ # pack e and n properly into the raw data
+ raw = struct.pack('!I7sI{0:d}sI{1:d}s'.format(len(e_str), len(n_str)), 7, 'ssh-rsa',
+ len(e_str), e_str, len(n_str), n_str)
+ # assemble file content and save it
+ content = "ssh-rsa {0!s} {1!s}\n".format(base64.b64encode(raw), host)
+ file(filename, 'w').write(content)
+
+def read_rsa_pri(filename):
+ """Read RSA private key file. Structure:
+
+ -----BEGIN RSA PRIVATE KEY-----
+ base64data
+ -----END RSA PRIVATE KEY-----
+
+ base64data DER structure:
+
+ RSAPrivateKey ::= SEQUENCE {
+ version Version,
+ modulus INTEGER, -- n
+ publicExponent INTEGER, -- e
+ privateExponent INTEGER, -- d
+ prime1 INTEGER, -- p
+ prime2 INTEGER, -- q
+ exponent1 INTEGER, -- d mod (p - 1)
+ exponent2 INTEGER, -- d mod (q - 1)
+ coefficient INTEGER -- q^-1 mod p
+ }
+ """
+
+ # grab only the lines between the --- * --- lines, glue them together
+ data = ''.join(filter(lambda x: x and x[0] != '-',
+ file(filename, 'r').read().split('\n')))
+ # decode from base64
+ raw = base64.b64decode(data)
+ # parse DER structure
+ der = decoder.decode(raw)
+ (version, n, e, d, p, q, e1, e2, c) = (int(x) for x in der[0])
+
+ return (n, e, d, p, q, e1, e2, c)
+
+def write_rsa_pri(filename, n, e, d, p, q, e1, e2, c):
+ """Write RSA private key file. Structure:
+
+ -----BEGIN RSA PRIVATE KEY-----
+ base64data
+ -----END RSA PRIVATE KEY-----
+
+ base64data DER structure:
+
+ RSAPrivateKey ::= SEQUENCE {
+ version Version,
+ modulus INTEGER, -- n
+ publicExponent INTEGER, -- e
+ privateExponent INTEGER, -- d
+ prime1 INTEGER, -- p
+ prime2 INTEGER, -- q
+ exponent1 INTEGER, -- d mod (p - 1)
+ exponent2 INTEGER, -- d mod (q - 1)
+ coefficient INTEGER -- q^-1 mod p
+ }
+ """
+
+ seq = (
+ univ.Integer(0),
+ univ.Integer(n),
+ univ.Integer(e),
+ univ.Integer(d),
+ univ.Integer(p),
+ univ.Integer(q),
+ univ.Integer(e1),
+ univ.Integer(e2),
+ univ.Integer(c),
+ )
+ struct = univ.Sequence()
+ for i in xrange(len(seq)):
+ struct.setComponentByPosition(i, seq[i])
+
+ # build DER structure
+ raw = encoder.encode(struct)
+ # encode to base64
+ data = base64.b64encode(raw)
+
+ # chop data up into lines of certain width
+ width = 64
+ chopped = [data[i:i + width] for i in xrange(0, len(data), width)]
+ # assemble file content
+ content = """-----BEGIN RSA PRIVATE KEY-----
+{0}
+-----END RSA PRIVATE KEY-----
+""".format('\n'.join(chopped))
+ file(filename, 'w').write(content)
+
+
+class SshRsaPublicKey(keys.RsaPublicKey):
+ @staticmethod
+ def Read(keyfile):
+ (n, e, host) = read_rsa_pub(keyfile)
+ params = {'modulus' : util.PadBytes(util.BigIntToBytes(n), 1),
+ 'publicExponent' : util.PadBytes(util.BigIntToBytes(e), 1)}
+ pubkey = RSA.construct((util.BytesToLong(params['modulus']),
+ util.BytesToLong(params['publicExponent'])))
+ return keys.RsaPublicKey(params, pubkey)
+
+class SshRsaPrivateKey(keys.RsaPrivateKey):
+ @staticmethod
+ def Read(keyfile):
+ (n, e, d, p, q, e1, e2, c) = read_rsa_pri(keyfile)
+ params = {'modulus' : util.PadBytes(util.BigIntToBytes(n), 1),
+ 'publicExponent' : util.PadBytes(util.BigIntToBytes(e), 1)}
+ pubkey = RSA.construct((util.BytesToLong(params['modulus']),
+ util.BytesToLong(params['publicExponent'])))
+ pub = keys.RsaPublicKey(params,pubkey)
+ params = {'privateExponent': util.PadBytes(util.BigIntToBytes(d),1),
+ 'primeP': util.PadBytes(util.BigIntToBytes(p),1),
+ 'primeQ': util.PadBytes(util.BigIntToBytes(q),1),
+ 'primeExponentP': util.PadBytes(util.BigIntToBytes(e1),1),
+ 'primeExponentQ': util.PadBytes(util.BigIntToBytes(e2),1),
+ 'crtCoefficient': util.PadBytes(util.BigIntToBytes(c),1),
+ }
+ key = RSA.construct((util.BytesToLong(pub.params['modulus']),
+ util.BytesToLong(pub.params['publicExponent']),
+ util.BytesToLong(params['privateExponent']),
+ util.BytesToLong(params['primeQ']),
+ util.BytesToLong(params['primeP']),
+ util.BytesToLong(params['crtCoefficient'])))
+ return keys.RsaPrivateKey(params, pub, key)
+
+if __name__ == '__main__' :
+ ssh_keys_directory='/home/guillaume/.ssh/'
+ print 'Testing public key reading...'
+ (n,e,host)=read_rsa_pub(ssh_keys_directory + 'id_rsa.pub')
+ test = SshRsaPublicKey.Read(ssh_keys_directory + 'id_rsa.pub')
+ write_rsa_pub(ssh_keys_directory + 'id_rsa_test.pub',n,e,host)
+ if filecmp.cmp(ssh_keys_directory + 'id_rsa.pub',ssh_keys_directory + 'id_rsa_test.pub'):
+ print 'test succesful'
+
+ print 'Testing private key reading...'
+ (n, e, d, p, q, e1, e2, c)=read_rsa_pri('/home/guillaume/.ssh/id_rsa')
+ write_rsa_pri('/home/guillaume/.ssh/id_rsa_test',n, e, d, p, q, e1, e2, c)
+ if filecmp.cmp(ssh_keys_directory + 'id_rsa',ssh_keys_directory + 'id_rsa_test'):
+ print 'test succesful'
+
+ \ No newline at end of file
diff --git a/server/user.py b/server/user.py
new file mode 100644
index 0000000..411ada6
--- /dev/null
+++ b/server/user.py
@@ -0,0 +1,32 @@
+import os
+import os.path
+import shutil
+import object
+import hashlib
+
+class UserHandler:
+
+ def __init__(self, root):
+ self.root = root
+
+ def register(self, name):
+ userDir = self.root + '/' + name
+ if not os.path.exists(userDir) :
+ os.mkdir(userDir)
+ handler = object.ObjectHandler(name)
+ handler.create_home_node()
+
+ def registered(self, name):
+ return os.path.exists(self.root + '/' + name)
+
+ def unregister(self, name):
+ shutil.rmtree(self.root + '/' + name)
+
+ def get_user_list(self):
+ return os.listdir(self.root)
+
+class User:
+
+ def __init__(self, name):
+ self.name = name
+ self.rootObject = hashlib.sha1(name).hexdigest()
diff --git a/server/version.py b/server/version.py
new file mode 100644
index 0000000..306cebb
--- /dev/null
+++ b/server/version.py
@@ -0,0 +1,5 @@
+# -*- coding: utf-8 -*-
+MAJOR=0
+MINOR=1
+TYPE='dev'
+VERSION = str(MAJOR)+'.'+str(MINOR)+TYPE #TODO: add the commit number ?