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
"""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):
"""
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)
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')