Code

Fix matching of incoming email addresses to the alternate_addresses
[roundup.git] / roundup / xmlrpc.py
index 45552872576c75a94f200567e1ca2f5d961d6d1b..0b85ab9d07e59347ee772105f8050f689d3227ef 100644 (file)
@@ -8,7 +8,9 @@ from roundup import hyperdb
 from roundup.cgi.exceptions import *
 from roundup.exceptions import UsageError
 from roundup.date import Date, Range, Interval
+from roundup import actions
 from SimpleXMLRPCServer import *
+from xmlrpclib import Binary
 
 def translate(value):
     """Translate value to becomes valid for XMLRPC transmission."""
@@ -31,12 +33,19 @@ def props_from_args(db, cl, args, itemid=None):
 
     props = {}
     for arg in args:
-        if arg.find('=') == -1:
+        if isinstance(arg, Binary):
+            arg = arg.data
+        try :
+            key, value = arg.split('=', 1)
+        except ValueError :
             raise UsageError, 'argument "%s" not propname=value'%arg
-        l = arg.split('=')
-        if len(l) < 2:
-            raise UsageError, 'argument "%s" not propname=value'%arg
-        key, value = l[0], '='.join(l[1:])
+        if isinstance(key, unicode):
+            try:
+                key = key.encode ('ascii')
+            except UnicodeEncodeError:
+                raise UsageError, 'argument %r is no valid ascii keyword'%key
+        if isinstance(value, unicode):
+            value = value.encode('utf-8')
         if value:
             try:
                 props[key] = hyperdb.rawToHyperdb(db, cl, itemid,
@@ -52,12 +61,20 @@ class RoundupInstance:
     """The RoundupInstance provides the interface accessible through
     the Python XMLRPC mapping."""
 
-    def __init__(self, db, translator):
+    def __init__(self, db, actions, translator):
 
         self.db = db
-        self.userid = db.getuid()
+        self.actions = actions
         self.translator = translator
 
+    def schema(self):
+        s = {}
+        for c in self.db.classes:
+            cls = self.db.classes[c]
+            props = [(n,repr(v)) for n,v in cls.properties.items()]
+            s[c] = props
+        return s
+
     def list(self, classname, propname=None):
         cl = self.db.getclass(classname)
         if not propname:
@@ -72,8 +89,24 @@ class RoundupInstance:
     def filter(self, classname, search_matches, filterspec,
                sort=[], group=[]):
         cl = self.db.getclass(classname)
+        uid = self.db.getuid()
+        security = self.db.security
+        filterspec = security.filterFilterspec (uid, classname, filterspec)
+        sort = security.filterSortspec (uid, classname, sort)
+        group = security.filterSortspec (uid, classname, group)
         result = cl.filter(search_matches, filterspec, sort=sort, group=group)
-        return result
+        check = security.hasPermission
+        x = [id for id in result if check('View', uid, classname, itemid=id)]
+        return x
+
+    def lookup(self, classname, key):
+        cl = self.db.getclass(classname)
+        uid = self.db.getuid()
+        prop = cl.getkey()
+        check = self.db.security.hasSearchPermission
+        if not check(uid, classname, 'id') or not check(uid, classname, prop):
+            raise Unauthorised('Permission to search %s denied'%classname)
+        return cl.lookup(key)
 
     def display(self, designator, *properties):
         classname, itemid = hyperdb.splitDesignator(designator)
@@ -89,6 +122,7 @@ class RoundupInstance:
         return dict(result)
 
     def create(self, classname, *args):
+        
         if not self.db.security.hasPermission('Create', self.db.getuid(), classname):
             raise Unauthorised('Permission to create %s denied'%classname)
 
@@ -102,9 +136,15 @@ class RoundupInstance:
         if key and not props.has_key(key):
             raise UsageError, 'you must provide the "%s" property.'%key
 
+        for key in props:
+            if not self.db.security.hasPermission('Create', self.db.getuid(),
+                classname, property=key):
+                raise Unauthorised('Permission to create %s.%s denied'%(classname, key))
+
         # do the actual create
         try:
             result = cl.create(**props)
+            self.db.commit()
         except (TypeError, IndexError, ValueError), message:
             raise UsageError, message
         return result
@@ -120,20 +160,42 @@ class RoundupInstance:
                 raise Unauthorised('Permission to edit %s of %s denied'%
                                    (p, designator))
         try:
-            return cl.set(itemid, **props)
+            result = cl.set(itemid, **props)
+            self.db.commit()
         except (TypeError, IndexError, ValueError), message:
             raise UsageError, message
+        return result
+
+
+    builtin_actions = {'retire': actions.Retire}
+
+    def action(self, name, *args):
+        """Execute a named action."""
+        
+        if name in self.actions:
+            action_type = self.actions[name]
+        elif name in self.builtin_actions:
+            action_type = self.builtin_actions[name]
+        else:
+            raise Exception('action "%s" is not supported %s' % (name, ','.join(self.actions.keys())))
+        action = action_type(self.db, self.translator)
+        return action.execute(*args)
 
 
 class RoundupDispatcher(SimpleXMLRPCDispatcher):
     """RoundupDispatcher bridges from cgi.client to RoundupInstance.
     It expects user authentication to be done."""
 
-    def __init__(self, db, userid, translator,
+    def __init__(self, db, actions, translator,
                  allow_none=False, encoding=None):
 
-        SimpleXMLRPCDispatcher.__init__(self, allow_none, encoding)
-        self.register_instance(RoundupInstance(db, userid, translator))
+        try:
+            # python2.5 and beyond
+            SimpleXMLRPCDispatcher.__init__(self, allow_none, encoding)
+        except TypeError:
+            # python2.4
+            SimpleXMLRPCDispatcher.__init__(self)
+        self.register_instance(RoundupInstance(db, actions, translator))
                  
 
     def dispatch(self, input):