X-Git-Url: https://git.distorted.org.uk/~mdw/chopwood/blobdiff_plain/1e83d6d9ae0d486fc1205e70123a356220db3bff..0c58273e69f08d17e69d9af6b04734bdce302532:/service.py diff --git a/service.py b/service.py index 786ab92..b1f170c 100644 --- a/service.py +++ b/service.py @@ -348,8 +348,14 @@ class CommandRemoteService (BasicRemoteService): containing `%' placeholders, as follows: `%u' the user's name + `%f' a user record field (list-valued) `%%' a single `%' character + If a template word contains placeholders for list-valued arguments, then + one output word is produced for each element of each list, with the + rightmost placeholder varying fastest. If any list is empty then no output + words are produced. + On success, the commands should print a line `OK' to standard output, and on any kind of anticipated failure, they should print `ERR' followed by an HTTP status code and a message; in either case, the program should exit @@ -379,15 +385,60 @@ class CommandRemoteService (BasicRemoteService): """Description of the remote service.""" return "`%s' command service (%s)" % (me.name, ' '.join(me._default)) - def _subst(me, c, map): - """Return the substitution for the placeholder `%C'.""" - return map.get(c, c) + def _mkcmd(me, cmd, argmap): + """ + Construct the command to be executed, by substituting placeholders. + + The ARGMAP is a dictionary mapping placeholder letters to lists of + arguments. These are substituted cartesian-product style into the + command words. + """ - def _mkcmd(me, cmd, map): - """Construct the command to be executed, by substituting placeholders.""" - if map is None: return cmd - return [me.R_PAT.sub(lambda m: me._subst(m.group(1), map), arg) - for arg in cmd] + ## No command map, so assume someone's already done the hard word. + if argmap is None: return cmd + + ## Start on building a list of arguments. + ww = [] + + ## Work through each template argument in turn... + for w in cmd: + + ## Firstly, build a list of lists. We'll then take the cartesian + ## product of these, and concatenate each of the results. + pc = [] + last = 0 + for m in me.R_PAT.finditer(w): + start, end = m.start(0), m.end(0) + if start > last: pc.append([w[last:start]]) + ch = m.group(1) + if ch == '%': + pc.append(['%']) + else: + try: pc.append(argmap[m.group(1)]) + except KeyError: raise U.ExpectedError, ( + 500, "Unknown placeholder `%%%s' in command `%s'" % (ch, cmd)) + last = end + if last < len(w): pc.append([w[last:]]) + + ## If any of the components is empty then there's nothing to do for + ## this word. + if not all(pc): continue + + ## Now do all the substitutions. + ii = len(pc)*[0] + while True: + ww.append(''.join(map(lambda v, i: v[i], pc, ii))) + i = len(ii) - 1 + while i >= 0: + ii[i] += 1 + if ii[i] < len(pc[i]): break + ii[i] = 0 + i -= 1 + else: + break + + ## And finally we're done. + return ww def _dispatch(me, func, op, args, input = None): """ @@ -395,13 +446,14 @@ class CommandRemoteService (BasicRemoteService): Invoke FUNC, which works like `_run', with appropriate arguments. The OP is a remote command name; ARGS is a sequence of (C, ARG) pairs, where C - is a placeholder character and ARG is a string value; INPUT is the text - to provide to the command on standard input. + is a placeholder character and ARG is a list of string values; INPUT is + the text to provide to the command on standard input. """ try: cmd = me._opmap[op] except KeyError: - cmd = me._default + [op] + [v for k, v in args] + cmd = me._default + [op] + reduce(lambda x, y: x + y, + [v for k, v in args], []) map = None else: map = dict(args) @@ -409,19 +461,21 @@ class CommandRemoteService (BasicRemoteService): def setpasswd(me, user, passwd): """Service protocol: set the USER's password to PASSWD.""" - me._dispatch(me._run_noout, 'set', [('u', user)], passwd + '\n') + me._dispatch(me._run_noout, 'set', [('u', [user])], + input = passwd + '\n') def clearpasswd(me, user): """Service protocol: clear the USER's password.""" - me._dispatch(me._run_noout, 'clear', [('u', user)]) + me._dispatch(me._run_noout, 'clear', [('u', [user])]) def mkpwent(me, user, passwd, fields): """Service protocol: create a record for USER.""" - me._dispatch(me._run_noout, 'mkpwent', [('u', user)]) + me._dispatch(me._run_noout, 'mkpwent', [('u', [user]), ('f', fields)], + input = passwd + '\n') def rmpwent(me, user): """Service protocol: delete the record for USER.""" - me._dispatch(me._run_noout, 'rmpwent', [('u', user)]) + me._dispatch(me._run_noout, 'rmpwent', [('u', [user])]) CONF.export('CommandRemoteService')