2 Copyright (C) 2018-2021 Ben Kibbey <bjk@luxsci.net>
4 This file is part of pwmd.
6 Pwmd is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License version 2 as
8 published by the Free Software Foundation.
10 Pwmd is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with Pwmd. If not, see <http://www.gnu.org/licenses/>.
24 bulk_free_list (struct sexp_s
**list
)
28 for (i
= 0; list
&& list
[i
]; i
++)
30 struct bulk_cmd_s
*c
= list
[i
]->user
;
39 xfree (list
[i
]->data
);
40 bulk_free_list (list
[i
]->next
);
47 #define SKIP_WS(s, size) do { \
48 while (s && isspace (*s)) \
52 #define NUMBER(s, size, buf, len, rc) do { \
55 while (e && isdigit (*e)) \
57 if (len >= sizeof (buf)-1) \
58 rc = GPG_ERR_INV_LENGTH; \
60 memcpy (buf, s, len); \
63 rc = GPG_ERR_SYNTAX; \
68 len = strtoul (buf, (char **)&e, 10); \
70 rc = GPG_ERR_INV_LENGTH; \
75 /* Like a canonical s-expression but without an internal representation. Any
76 * character is allowed. The tags and data are NULL terminated but not
77 * considered in the length structure members. */
79 parse_sexp (struct sexp_s
***head
, const char *str
, size_t *size
)
85 for (p
= str
; p
&& *p
;)
87 struct sexp_s
**s
, *cur
;
89 char *tag
= NULL
, *data
= NULL
;
90 size_t taglen
, datalen
;
99 else if (isspace (*p
))
110 return GPG_ERR_INV_SEXP
;
116 rc
= parse_sexp (&cur
->next
, p
, size
);
127 rc
= GPG_ERR_SEXP_INV_LEN_SPEC
;
131 NUMBER (p
, size
, buf
, taglen
, rc
);
135 if (taglen
> *size
-1) // -1 for closing parentheses
137 rc
= GPG_ERR_INV_LENGTH
;
144 tag
= xmalloc ((taglen
+1) * sizeof (char));
151 memcpy (tag
, p
, taglen
);
157 NUMBER (p
, size
, buf
, datalen
, rc
);
164 if (datalen
> *size
-1)
167 rc
= GPG_ERR_INV_LENGTH
;
173 data
= xmalloc ((datalen
+1) * sizeof (char));
181 memcpy (data
, p
, datalen
);
187 cur
= xcalloc (1, sizeof (struct sexp_s
));
197 cur
->taglen
= taglen
;
199 cur
->datalen
= datalen
;
201 s
= xrealloc (*head
, (t
+2) * sizeof (struct sexp_s
*));
216 if (!rc
&& (!*p
|| (*p
&& *p
!= ')')))
223 validate_command (struct sexp_s
*cur
, struct command_table_s
**commands
)
227 for (i
= 0; commands
[i
]; i
++)
229 if (!strcasecmp (cur
->tag
, "BULK"))
230 return GPG_ERR_ASS_NESTED_COMMANDS
;
231 else if (!strcasecmp (cur
->tag
, "RESET")
232 || !strcasecmp (cur
->tag
, "BYE"))
233 return GPG_ERR_ASS_UNEXPECTED_CMD
;
235 if (!strcasecmp (cur
->tag
, commands
[i
]->name
))
237 struct bulk_cmd_s
*p
= xcalloc (1, sizeof (struct bulk_cmd_s
));
240 return GPG_ERR_ENOMEM
;
242 p
->cmd
= commands
[i
];
248 return GPG_ERR_UNKNOWN_COMMAND
;
251 /* Parse the s-expression using pwmd's syntax of:
253 * (2:id<I>:<id> <P>:<prot><D>:[<data>]
254 * [2:rc<R>:<code>[|<code>[...]](2:id...) | 2:id...])
256 * Where <I> is an integer specifying the length of the Id name <id>, <P> the
257 * length of the protocol command name <prot>, <D> is the length of <data>
258 * passed to <proto>, <R> is optional and the length of the numbered string
259 * <code> representing one or more pipe separated return codes of <prot>.
261 * When the return code of the command <prot> returns and matches <code>, the
262 * new command sequence in parentheses following <code> begins. You can specify
263 * multiple return codes and command branches for each command. Multiple return
264 * codes are separated with a pipe '|' to simulate an if-this-or-that
265 * expression for a command return value.
268 parse_cmds (struct sexp_s
**list
, struct command_table_s
**commands
)
273 for (i
= 0; list
&& list
[i
]; i
++)
275 struct sexp_s
*cur
= list
[i
];
277 if (!memcmp (cur
->tag
, "rc", cur
->taglen
))
281 if (!memcmp (cur
->tag
, "id", cur
->taglen
))
284 return GPG_ERR_SYNTAX
;
287 return GPG_ERR_SYNTAX
;
291 return GPG_ERR_SYNTAX
;
293 if (memcmp (cur
->tag
, "rc", cur
->taglen
))
295 rc
= validate_command (cur
, commands
);
301 if (!memcmp (cur
->tag
, "rc", cur
->taglen
))
306 return GPG_ERR_SYNTAX
;
308 if (*cur
->data
== '|' || cur
->data
[cur
->datalen
-1] == '|')
309 return GPG_ERR_SYNTAX
;
311 for (t
= cur
->data
; t
&& *t
; t
++)
313 if (!isdigit (*t
) && *t
!= '|')
314 return GPG_ERR_SYNTAX
;
317 rc
= parse_cmds (list
[i
]->next
, commands
);
324 return !list
? GPG_ERR_SYNTAX
: rc
;
328 bulk_parse_commands (struct sexp_s
***result
, const char *str
,
329 struct command_table_s
**commands
)
332 size_t n
= strlen (str
)-1;
335 return GPG_ERR_SYNTAX
;
337 rc
= parse_sexp (result
, str
+1, &n
);
341 rc
= parse_cmds (*result
, commands
);
344 bulk_free_list (*result
);
350 number_length (size_t n
, char **result
)
354 snprintf (buf
, sizeof (buf
), "%zu", n
);
356 *result
= str_dup (buf
);
362 recurse_bulk_build_result (struct sexp_s
**sexp
, char **str
, size_t *size
)
367 for (i
= 0; sexp
[i
]; i
++)
369 struct sexp_s
*id
= NULL
;
370 struct bulk_cmd_s
*cur
;
373 char *idlen
= NULL
, *rclen
= NULL
, *rcstr
= NULL
, *resultlen
= NULL
;
375 if (!memcmp (sexp
[i
]->tag
, "id", sexp
[i
]->taglen
))
377 else if (!memcmp (sexp
[i
]->tag
, "rc", sexp
[i
]->taglen
))
381 if (!cur
|| !cur
->ran
)
385 return GPG_ERR_SYNTAX
;
388 n
+= number_length (id
->datalen
, &idlen
);
390 n
+= number_length (cur
->rc
, &rclen
);
391 n
+= number_length (strlen (rclen
), &rcstr
);
392 n
+= number_length (cur
->result_len
, &resultlen
);
393 n
+= cur
->result_len
;
396 p
= xrealloc (*str
, (n
+1) * sizeof (char));
397 if (!p
|| !idlen
|| !rclen
|| !rcstr
|| !resultlen
)
410 memcpy (&p
[len
], "2:id", 4);
412 memcpy (&p
[len
], idlen
, strlen (idlen
));
413 len
+= strlen (idlen
);
415 memcpy (&p
[len
++], ":", 1);
416 memcpy (&p
[len
], id
->data
, id
->datalen
);
419 memcpy (&p
[len
], "2:rc", 4);
421 memcpy (&p
[len
], rcstr
, strlen (rcstr
));
422 len
+= strlen (rcstr
);
424 memcpy (&p
[len
++], ":", 1);
425 memcpy (&p
[len
], rclen
, strlen (rclen
));
426 len
+= strlen (rclen
);
429 memcpy (&p
[len
], resultlen
, strlen (resultlen
));
430 len
+= strlen (resultlen
);
432 memcpy (&p
[len
++], ":", 1);
434 memcpy (&p
[len
], cur
->result
, cur
->result_len
);
436 len
+= cur
->result_len
;
440 if (sexp
[i
+1] && sexp
[i
+1]->next
)
442 rc
= recurse_bulk_build_result (sexp
[i
+1]->next
, str
, size
);
451 /* Creates a string representing the result of a bulk command list. Syntax:
453 * (11:bulk-result 2:id<I>:<id> 2:rc<R>:<code> <D>:[<data>] [2:id...])
455 * Where <I> is the length of <id>, <R> is the length of the command result
456 * <code> and <D> is the length of the command result <data> if any.
459 bulk_build_result (struct sexp_s
**sexp
, char **result
)
464 *result
= str_dup ("(11:bulk-result");
466 return GPG_ERR_ENOMEM
;
468 size
= strlen (*result
);
469 rc
= recurse_bulk_build_result (sexp
, result
, &size
);
472 char *p
= xrealloc (*result
, (size
+2) * sizeof (char));
477 return GPG_ERR_ENOMEM
;