Commits

pystew committed 0cdb4e3

setup

Comments (0)

Files changed (12)

+Copyright (C) 2010-2011 Louis RIVIERE
+
+Citadel-Python-Client is covered by the MIT license:
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without restriction,
+including without limitation the rights to use, copy, modify, merge,
+publish, distribute, sublicense, and/or sell copies of the Software,
+and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
+OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+LICENSE.txt
+README.txt
+setup.py
+citadel/__init__.py
+citadel/api.py
+citadel/citadel.py
+citadel/config.py
+citadel/tcp.py
+citadel/test.py
+citadel/tpl.py
+include LICENSE.txt
+Citadel-Python-Client.
+
+This is a library to access Citadel servers from Python using the Citadel Protocol.
+
+
+          Python : http://www.python.org
+         Citadel : http://www.citadel.org
+Citadel Protocol : http://www.citadel.org/doku.php/documentation:applicationprotocol
+
+ - tcp.py defines a fairly generic TCP client in the Client class.
+ - api.py implements the Citadel Protocol on top of tcp.Client.
+ - citadel.py is the high level Citadel interface.
+ - tpl.py contains templates needed by the Citadel client.
+
+ You shouldn't have to worry about tcp.py, api.Protocol is the class where you would implement missing parts of the low level Citadel Protocol and citadel.py should be the only module you need to import and adapt to your needs.
+
+ When you instenciate the Citadel class, you may pass it a config object (typically a module).
+ The config object may have the following attributes:
+  . server_adr  : the Citadel server adr
+  . server_prt  : the Citadel server port
+  . admin_login : admin login           
+  . admin_psw   : admin password       
+  . log         : log file            
+If server_adr is omitted localhost will be used, server_prt defaults to 504.
+If admin login or password are not specified, you will be asked for them as needed.
+If log is not specified (or evaluate to False) nothing will be logged.

citadel/__init__.py

Empty file added.
+import tcp
+
+class Protocol(tcp.Client):
+    def INFO(self): # get server infos
+        c, a = self.req('INFO')
+        if c=='100':
+            raws = list(self.readlines())
+            return ' - '.join(raws[i] for i in (4,2,3))
+    def NOOP(self): # NOP
+        c, a = self.req('NOOP')
+        assert c=='200'
+    def QUIT(self): # close cnx
+        c, a = self.req('QUIT')
+    def CREU(self, usr, psw): # create usr
+        c, a = self.req('CREU', usr, psw)
+    def VALI(self, usr, level): # validate usr
+        c, a = self.req('VALI', usr, level)
+        return c=='200'
+    def LOUT(self): # logout
+        c, a = self.req('LOUT')
+    def USER(self, username): # send login
+        c, a = self.req('USER', username)
+        return c=='300'
+    def PASS(self, password): # send psw
+        c, a = self.req('PASS',  password)
+        return c=='200'
+    def QUSR(self, usr, ignore=False): # chk usr
+        if ignore: ignore='570'
+        res = self.req('QUSR',  usr, ignore=ignore)
+        if res: return res[1]
+    def GOTO(self, room): # goto room
+        c, a = self.req('GOTO',  room)
+        return c=='200'
+    def REGI(self, name): # set usr info
+        c, a = self.req('REGI')
+        if c=='400':
+            lines = [name, 'adr', 'vil', 'st', '33333', 'tel', 'mail', 'fr']
+            self.writelines(lines)
+        else:
+            self.get()
+            self.writeline('000')
+    def RENU(self, old, new): # chg usr login
+        self.req('RENU', old, new)
+    def LIST(self): # list users
+        c, a = self.req('LIST')
+        for lin in self.readlines():
+            login, level, num, last, log, msg, psw, what = lin
+            yield login, level
+    def ASUP(self, usr, p, f, t, m, a, n, s, g): # set usr params
+        c, a = self.req('ASUP', usr, p, f, t, m, a, n , s, g)
+        return c=='200'
+    def LFLR(self): # list floors
+        c, a = self.req('LFLR')
+        assert c=='100'
+        for floor in self.readlines():
+            yield (
+                floor[0], # num
+                floor[1], # name
+                floor[2], # refcount
+            )
+    def LKRA(self, floor=-1): # known rooms
+        c, a = self.req('LKRA %d'%floor)
+        assert c=='100'
+        for room in self.readlines():
+            yield (
+                room[0],
+                room[1],
+                room[2],
+                room[3],
+                room[4],
+                room[5],
+            )
+    def ENT0(self, post='1', to='', anon='0', fmt='0', sbj='', usr='', confirm='0', cc='', bcc='', msg=''):
+        c, a = self.req('ENT0', post, to, anon, fmt, sbj, usr, confirm, cc, bcc)
+        if c=='400':
+            self.writelines(msg, log=True)
+        else:
+            print '? ? ?', c, a

citadel/citadel.py

+import api
+import tpl
+
+class Citadel(api.Protocol):
+    def __init__(self, config=None):
+        self.config = config
+        api.Protocol.__init__(self)
+    @property
+    def admin_login(self):
+        if not hasattr(self, '_login'):
+            if hasattr(self.config, 'admin_login'):
+                self._login = self.config.admin_login
+            else:
+                self._login = raw_input('Admin LOGIN :')
+        return self._login
+    @property
+    def admin_psw(self):
+        if not hasattr(self, '_psw'):
+            if hasattr(self.config, 'admin_psw'):
+                self._psw = self.config.admin_psw
+            else:
+                self._psw = raw_input('Admin PASSWORD :')
+        return self._psw
+    def admin(self):
+        return self.login(self.admin_login, self.admin_psw)
+    def close(self):
+        self.QUIT()
+        self.quit()
+    def login(self, usr, psw):
+        return self.USER(usr) and self.PASS(psw)
+    def chku(self, usr, ignore=False):
+        return self.QUSR(usr, ignore=ignore)==usr
+    def creu(self, usr, psw, lvl='4'):
+        if self.chku(usr, ignore=True):  return True
+        self.admin()
+        self.CREU(usr, psw)
+        valid = self.VALI(usr, lvl)
+        self.LOUT()
+        return valid and self.chku(usr)
+    def delu(self, usr):
+        self.admin()
+        c = self.ASUP(usr, '0','0','0','0','0','0','0','0')
+        self.LOUT()
+        return c
+    def listu(self):
+        for usr, level in sorted(self.LIST()):
+            if not usr.startswith('SYS_') and level<'6':
+                yield usr
+    def mail(self, dst, sbj, txt): # post a mail
+        self.GOTO('mail')
+        msg = [' %s'%i for i in txt.split('\n')]
+        self.ENT0(to=dst, sbj=sbj, msg=msg)
+    def vcard(self, user, first, name, full): # post a vcard
+        msg = tpl.vcf%(user, user, name, first, full, user)
+        msg = msg.lstrip().split('\n')
+        self.ENT0(fmt='4', msg=msg)
+    def updusr(self, usr, psw, pre, nom, full): # update user info
+        self.login(usr, psw)
+        self.GOTO('My Citadel Config')
+        self.vcard(usr, pre, nom, full)
+        self.LOUT()
+    def creuser(self, login, psw, pre, nom, full):
+        if self.creu(login, psw):
+            self.updusr(login, psw, pre, nom, full)
+

citadel/config.py

+server_adr = '192.168.1.1'
+server_prt = 504
+admin_login = 'admin'
+admin_psw = 'admin'
+log = 'citadel.log'
+import socket
+
+class Client(object):
+    def __init__(self):
+        cfg = self.config
+        self.logfil = hasattr(cfg, 'log') and cfg.log
+        adr = hasattr(cfg, 'server_adr') and cfg.server_adr or '127.0.0.1'
+        prt = hasattr(cfg, 'server_prt') and cfg.server_prt or 504
+        self.sock = self.connect(adr, prt)
+        rf = self.sock.makefile('rb', -1)
+        wf = self.sock.makefile('wb', 0)
+        self.read = rf.readline
+        self.write = wf.write
+        c, a = self.get()
+        self.log(a[0], 'w')
+    def log(self, txt, mode='a'):
+        if hasattr(self, 'logfil'):
+            log = open(self.logf, mode)
+            log.write('%s\n'%txt)
+            log.close
+    def connect(self, adr, port):
+        for af, socktype, proto, cn, sa in socket.getaddrinfo(
+            adr , port , 0 , socket.SOCK_STREAM
+        ):
+            try:
+                sock = None
+                sock = socket.socket(af, socktype, proto)
+                sock.connect(sa)
+                break
+            except socket.error:
+                if sock: sock.close()
+        else:
+            raise socket.error
+        return sock
+    def quit(self):
+        self.sock.close()
+        self.sock = None
+    def readline(self, log=True):
+        line = self.read().rstrip('\n')
+        if log: self.log('<-   %s'%line)
+        return line
+    def readlines(self):
+        while True:
+            line = self.readline(log=False)
+            if line=='000': break
+            data =  line.split('|')
+            yield data[0] if len(data)==1 else data
+    def writeline(self, line, log=True):
+        if log: self.log('  -> %s'%line)
+        self.write(u'%s\n' % unicode(line))
+    def writelines(self, lines, log=False):
+        for line in lines:
+            self.writeline(line, log)
+        self.writeline('000', log)
+    def get(self):
+        code, args = self.readline().split(' ', 1)
+        return code, args.split('|')
+    def req(self, *arg, **kw):
+        ignore = kw.get('ignore')
+        cmd = '%s %s' % (arg[0], '|'.join(arg[1:]))
+        self.writeline(cmd)
+        c, a = self.get()
+        if len(a)==1: a=a[0]
+        if c.startswith('5') and c!=ignore:
+            print '\n! ! ! ERROR %s : %s ! ! !\n' % (c, a)
+            return None, None
+        else:
+            return c, a
+vcf = '''
+Content-type: text/x-vcard; charset=UTF-8
+
+begin:vcard
+VERSION:2.1
+FBURL;PREF:http://test/%s.vfb
+UID;charset=UTF-8:Citadel vCard: personal card for %s at test
+n:%s;%s;;;
+title:
+fn:%s
+org:
+adr:;;;;;;
+tel;home:
+tel;work:
+tel;fax:
+tel;cell:
+email;internet:%s
+end:vcard
+'''
+from distutils.core import setup
+setup(
+    name = 'citadel',
+    packages = ['citadel'],
+    version = '0.1',
+    description = 'Citadel high level client',
+    author='Louis RIVIERE',
+    author_email = "louis@pystew.net",
+    url = "http://chardet.feedparser.org/",
+    download_url = "http://chardet.feedparser.org/download/python3-chardet-1.0.1.tgz",
+    keywords = ["citadel", "client"],
+    classifiers = [
+        "Programming Language :: Python",
+        "Development Status :: 3 - Alpha",
+        "Intended Audience :: Developers",
+        "License :: OSI Approved :: MIT License",
+        "Operating System :: OS Independent",
+        "Topic :: Software Development :: Libraries :: Python Modules",
+        ],
+    )
+from citadel import Citadel
+
+import config
+
+citadel = Citadel(config)
+print citadel.INFO()
+print '\n'.join(citadel.listu())
+citadel.close()