2 Copyright (C) 2018-2019 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 as published by
8 the Free Software Foundation, either version 2 of the License, or
9 (at your option) any later version.
11 Pwmd is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with Pwmd. If not, see <http://www.gnu.org/licenses/>.
25 bulk_free_list (struct sexp_s
**list
)
29 for (i
= 0; list
&& list
[i
]; i
++)
31 struct bulk_cmd_s
*c
= list
[i
]->user
;
40 xfree (list
[i
]->data
);
41 bulk_free_list (list
[i
]->next
);
48 #define SKIP_WS(s, size) do { \
49 while (s && isspace (*s)) \
53 #define NUMBER(s, size, buf, len, rc) do { \
56 while (e && isdigit (*e)) \
58 if (len >= sizeof (buf)-1) \
59 rc = GPG_ERR_INV_LENGTH; \
61 memcpy (buf, s, len); \
64 rc = GPG_ERR_SYNTAX; \
69 len = strtoul (buf, (char **)&e, 10); \
71 rc = GPG_ERR_INV_LENGTH; \
76 /* Like a canonical s-expression but without an internal representation. Any
77 * character is allowed. The tags and data are NULL terminated but not
78 * considered in the length structure members. */
80 parse_sexp (struct sexp_s
***head
, const char *str
, size_t *size
)
86 for (p
= str
; p
&& *p
;)
88 struct sexp_s
**s
, *cur
;
90 char *tag
= NULL
, *data
= NULL
;
91 size_t taglen
, datalen
;
100 else if (isspace (*p
))
111 return GPG_ERR_INV_SEXP
;
117 rc
= parse_sexp (&cur
->next
, p
, size
);
128 rc
= GPG_ERR_SEXP_INV_LEN_SPEC
;
132 NUMBER (p
, size
, buf
, taglen
, rc
);
136 if (taglen
> *size
-1) // -1 for closing parentheses
138 rc
= GPG_ERR_INV_LENGTH
;
145 tag
= xmalloc ((taglen
+1) * sizeof (char));
152 memcpy (tag
, p
, taglen
);
158 NUMBER (p
, size
, buf
, datalen
, rc
);
165 if (datalen
> *size
-1)
168 rc
= GPG_ERR_INV_LENGTH
;
174 data
= xmalloc ((datalen
+1) * sizeof (char));
182 memcpy (data
, p
, datalen
);
188 cur
= xcalloc (1, sizeof (struct sexp_s
));
198 cur
->taglen
= taglen
;
200 cur
->datalen
= datalen
;
202 s
= xrealloc (*head
, (t
+2) * sizeof (struct sexp_s
*));
217 if (!rc
&& (!*p
|| (*p
&& *p
!= ')')))
224 validate_command (struct sexp_s
*cur
, struct command_table_s
**commands
)
228 for (i
= 0; commands
[i
]; i
++)
230 if (!strcasecmp (cur
->tag
, "BULK"))
231 return GPG_ERR_ASS_NESTED_COMMANDS
;
232 else if (!strcasecmp (cur
->tag
, "RESET")
233 || !strcasecmp (cur
->tag
, "BYE"))
234 return GPG_ERR_ASS_UNEXPECTED_CMD
;
236 if (!strcasecmp (cur
->tag
, commands
[i
]->name
))
238 struct bulk_cmd_s
*p
= xcalloc (1, sizeof (struct bulk_cmd_s
));
241 return GPG_ERR_ENOMEM
;
243 p
->cmd
= commands
[i
];
249 return GPG_ERR_UNKNOWN_COMMAND
;
252 /* Parse the s-expression using pwmd's syntax of:
254 * (2:id<I>:<id> <P>:<prot><D>:[<data>]
255 * [2:rc<R>:<code>[|<code>[...]](2:id...) | 2:id...])
257 * Where <I> is an integer specifying the length of the Id name <id>, <P> the
258 * length of the protocol command name <prot>, <D> is the length of <data>
259 * passed to <proto>, <R> is optional and the length of the numbered string
260 * <code> representing one or more pipe separated return codes of <prot>.
262 * When the return code of the command <prot> returns and matches <code>, the
263 * new command sequence in parentheses following <code> begins. You can specify
264 * multiple return codes and command branches for each command. Multiple return
265 * codes are separated with a pipe '|' to simulate an if-this-or-that
266 * expression for a command return value.
269 parse_cmds (struct sexp_s
**list
, struct command_table_s
**commands
)
274 for (i
= 0; list
&& list
[i
]; i
++)
276 struct sexp_s
*cur
= list
[i
];
278 if (!memcmp (cur
->tag
, "rc", cur
->taglen
))
282 if (!memcmp (cur
->tag
, "id", cur
->taglen
))
285 return GPG_ERR_SYNTAX
;
288 return GPG_ERR_SYNTAX
;
292 return GPG_ERR_SYNTAX
;
294 if (memcmp (cur
->tag
, "rc", cur
->taglen
))
296 rc
= validate_command (cur
, commands
);
302 if (!memcmp (cur
->tag
, "rc", cur
->taglen
))
307 return GPG_ERR_SYNTAX
;
309 if (*cur
->data
== '|' || cur
->data
[cur
->datalen
-1] == '|')
310 return GPG_ERR_SYNTAX
;
312 for (t
= cur
->data
; t
&& *t
; t
++)
314 if (!isdigit (*t
) && *t
!= '|')
315 return GPG_ERR_SYNTAX
;
318 rc
= parse_cmds (list
[i
]->next
, commands
);
325 return !list
? GPG_ERR_SYNTAX
: rc
;
329 bulk_parse_commands (struct sexp_s
***result
, const char *str
,
330 struct command_table_s
**commands
)
333 size_t n
= strlen (str
)-1;
336 return GPG_ERR_SYNTAX
;
338 rc
= parse_sexp (result
, str
+1, &n
);
342 rc
= parse_cmds (*result
, commands
);
345 bulk_free_list (*result
);
351 number_length (size_t n
, char **result
)
355 snprintf (buf
, sizeof (buf
), "%zu", n
);
357 *result
= str_dup (buf
);
363 recurse_bulk_build_result (struct sexp_s
**sexp
, char **str
, size_t *size
)
368 for (i
= 0; sexp
[i
]; i
++)
370 struct sexp_s
*id
= NULL
;
371 struct bulk_cmd_s
*cur
;
374 char *idlen
= NULL
, *rclen
= NULL
, *rcstr
= NULL
, *resultlen
= NULL
;
376 if (!memcmp (sexp
[i
]->tag
, "id", sexp
[i
]->taglen
))
378 else if (!memcmp (sexp
[i
]->tag
, "rc", sexp
[i
]->taglen
))
382 if (!cur
|| !cur
->ran
)
386 n
+= number_length (id
->datalen
, &idlen
);
388 n
+= number_length (cur
->rc
, &rclen
);
389 n
+= number_length (strlen (rclen
), &rcstr
);
390 n
+= number_length (cur
->result_len
, &resultlen
);
391 n
+= cur
->result_len
;
394 p
= xrealloc (*str
, (n
+1) * sizeof (char));
395 if (!p
|| !idlen
|| !rclen
|| !rcstr
|| !resultlen
)
408 memcpy (&p
[len
], "2:id", 4);
410 memcpy (&p
[len
], idlen
, strlen (idlen
));
411 len
+= strlen (idlen
);
413 memcpy (&p
[len
++], ":", 1);
414 memcpy (&p
[len
], id
->data
, id
->datalen
);
417 memcpy (&p
[len
], "2:rc", 4);
419 memcpy (&p
[len
], rcstr
, strlen (rcstr
));
420 len
+= strlen (rcstr
);
422 memcpy (&p
[len
++], ":", 1);
423 memcpy (&p
[len
], rclen
, strlen (rclen
));
424 len
+= strlen (rclen
);
427 memcpy (&p
[len
], resultlen
, strlen (resultlen
));
428 len
+= strlen (resultlen
);
430 memcpy (&p
[len
++], ":", 1);
432 memcpy (&p
[len
], cur
->result
, cur
->result_len
);
434 len
+= cur
->result_len
;
438 if (sexp
[i
+1] && sexp
[i
+1]->next
)
440 rc
= recurse_bulk_build_result (sexp
[i
+1]->next
, str
, size
);
449 /* Creates a string representing the result of a bulk command list. Syntax:
451 * (11:bulk-result 2:id<I>:<id> 2:rc<R>:<code> <D>:[<data>] [2:id...])
453 * Where <I> is the length of <id>, <R> is the length of the command result
454 * <code> and <D> is the length of the command result <data> if any.
457 bulk_build_result (struct sexp_s
**sexp
, char **result
)
462 *result
= str_dup ("(11:bulk-result");
464 return GPG_ERR_ENOMEM
;
466 size
= strlen (*result
);
467 rc
= recurse_bulk_build_result (sexp
, result
, &size
);
470 char *p
= xrealloc (*result
, (size
+2) * sizeof (char));
475 return GPG_ERR_ENOMEM
;