2 Copyright (C) 2018 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); \
66 len = strtoul (buf, (char **)&e, 10); \
68 rc = GPG_ERR_INV_LENGTH; \
72 /* Like a canonical s-expression but without an internal representation. Any
73 * character is allowed. The tags and data are NULL terminated but not
74 * considered in the length structure members. */
76 parse_sexp (struct sexp_s
***head
, const char *str
, size_t *size
)
82 for (p
= str
; p
&& *p
;)
84 struct sexp_s
**s
, *cur
;
86 char *tag
= NULL
, *data
= NULL
;
87 size_t taglen
, datalen
;
94 else if (isspace (*p
))
105 return GPG_ERR_INV_SEXP
;
111 rc
= parse_sexp (&cur
->next
, p
, size
);
122 rc
= GPG_ERR_INV_LENGTH
;
126 NUMBER (p
, size
, buf
, taglen
, rc
);
130 if (taglen
> *size
-1) // -1 for closing parentheses
132 rc
= GPG_ERR_INV_LENGTH
;
139 tag
= xmalloc ((taglen
+1) * sizeof (char));
146 memcpy (tag
, p
, taglen
);
151 NUMBER (p
, size
, buf
, datalen
, rc
);
158 if (datalen
> *size
-1)
161 rc
= GPG_ERR_INV_LENGTH
;
167 data
= xmalloc ((datalen
+1) * sizeof (char));
175 memcpy (data
, p
, datalen
);
181 cur
= xcalloc (1, sizeof (struct sexp_s
));
191 cur
->taglen
= taglen
;
193 cur
->datalen
= datalen
;
195 s
= xrealloc (*head
, (t
+2) * sizeof (struct sexp_s
*));
214 count_c (const char *str
, const char c
)
219 for (p
= str
; p
&& *p
; p
++)
229 validate_command (struct sexp_s
*cur
, struct command_table_s
**commands
)
233 for (i
= 0; commands
[i
]; i
++)
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 * (7:command 2:id<I>:<id> <P>:<prot><D>:[<data>]
254 * [2:rc<R>:<code>(7:command ...)] [7:command ...])
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 a return code 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.
266 parse_cmds (struct sexp_s
**list
, struct command_table_s
**commands
)
271 for (i
= 0; list
&& list
[i
]; i
++)
273 struct sexp_s
*cur
= list
[i
];
275 if (!memcmp (cur
->tag
, "command", cur
->taglen
))
277 if (cur
->datalen
|| cur
->next
)
278 return GPG_ERR_SYNTAX
;
280 else if (!memcmp (cur
->tag
, "rc", cur
->taglen
))
284 if (cur
&& !memcmp (cur
->tag
, "id", cur
->taglen
))
287 return GPG_ERR_SYNTAX
;
290 return GPG_ERR_SYNTAX
;
294 return GPG_ERR_SYNTAX
;
296 if (cur
&& memcmp (cur
->tag
, "rc", cur
->taglen
))
298 rc
= validate_command (cur
, commands
);
304 if (cur
&& !memcmp (cur
->tag
, "rc", cur
->taglen
))
309 return GPG_ERR_SYNTAX
;
311 (void)strtoul (cur
->data
, &e
, 10);
313 return GPG_ERR_SYNTAX
;
315 rc
= parse_cmds (list
[i
]->next
, commands
);
322 return !list
? GPG_ERR_SYNTAX
: rc
;
326 bulk_parse_commands (struct sexp_s
***result
, const char *str
,
327 struct command_table_s
**commands
)
330 size_t n
= strlen (str
)-1;
332 if (count_c (str
, '(') != count_c (str
, ')'))
333 return GPG_ERR_SEXP_UNMATCHED_PAREN
;
335 rc
= parse_sexp (result
, str
+1, &n
);
337 rc
= parse_cmds (*result
, commands
);
340 bulk_free_list (*result
);
346 number_length (size_t n
, char **result
)
350 snprintf (buf
, sizeof (buf
), "%zu", n
);
352 *result
= str_dup (buf
);
358 recurse_bulk_build_result (struct sexp_s
**sexp
, char **str
, size_t *size
)
363 for (i
= 0; sexp
[i
]; i
++)
365 struct sexp_s
*id
= NULL
;
366 struct bulk_cmd_s
*cur
;
369 char *idlen
= NULL
, *rclen
= NULL
, *rcstr
= NULL
, *resultlen
= NULL
;
371 if (!memcmp (sexp
[i
]->tag
, "id", sexp
[i
]->taglen
))
375 if (!cur
|| !cur
->ran
)
379 n
+= number_length (id
->datalen
, &idlen
);
381 n
+= number_length (cur
->rc
, &rclen
);
382 n
+= number_length (strlen (rclen
), &rcstr
);
383 n
+= number_length (cur
->result_len
, &resultlen
);
384 n
+= cur
->result_len
;
387 p
= xrealloc (*str
, (n
+1) * sizeof (char));
388 if (!p
|| !idlen
|| !rclen
|| !rcstr
|| !resultlen
)
401 memcpy (&p
[len
], "2:id", 4);
403 memcpy (&p
[len
], idlen
, strlen (idlen
));
404 len
+= strlen (idlen
);
406 memcpy (&p
[len
++], ":", 1);
407 memcpy (&p
[len
], id
->data
, id
->datalen
);
410 memcpy (&p
[len
], "2:rc", 4);
412 memcpy (&p
[len
], rcstr
, strlen (rcstr
));
413 len
+= strlen (rcstr
);
415 memcpy (&p
[len
++], ":", 1);
416 memcpy (&p
[len
], rclen
, strlen (rclen
));
417 len
+= strlen (rclen
);
420 memcpy (&p
[len
], resultlen
, strlen (resultlen
));
421 len
+= strlen (resultlen
);
423 memcpy (&p
[len
++], ":", 1);
425 memcpy (&p
[len
], cur
->result
, cur
->result_len
);
427 len
+= cur
->result_len
;
430 if (sexp
[i
+1] && sexp
[i
+1]->next
)
432 rc
= recurse_bulk_build_result (sexp
[i
+1]->next
, str
, size
);
441 /* Creates a string representing the result of a bulk command list. Syntax:
443 * (11:bulk-result 2:id<I>:<id> 2:rc<R>:<code> <D>:[<data>] [2:id...])
445 * Where <I> is the length of <id>, <R> is the length of the command result
446 * <code> and <D> is the length of the command result <data> if any.
449 bulk_build_result (struct sexp_s
**sexp
, char **result
)
454 *result
= str_dup ("(11:bulk-result");
456 return GPG_ERR_ENOMEM
;
458 size
= strlen (*result
);
459 rc
= recurse_bulk_build_result (sexp
, result
, &size
);
462 char *p
= xrealloc (*result
, (size
+2) * sizeof (char));
467 return GPG_ERR_ENOMEM
;