add identifier sanitation for setting FSM instance ids

We often compose FSM instance IDs from context information, for example placing
an MSISDN string or IP:port information in the FSM instance id, using
osmo_fsm_inst_update_id_f(). This fails if any characters are contained that
don't pass osmo_identifier_valid(). Hence it is the task of the caller to make
sure only characters allowed in an FSM id are applied.

Provide API to trivially allow this by replacing illegal chars:
- osmo_identifier_sanitize_buf(), with access to the same set of illegal
  characters defined in utils.c,
- osmo_fsm_inst_update_id_f_sanitize() implicitly replaces non-identifier
  chars.

This makes it easy to add strings like '192.168.0.1:2342' or '+4987654321' to
an FSM instance id, without adding string mangling to each place that sets an
id; e.g. replacing with '-' to yield '192-168-0-1:2342' or '-4987654321'.

Change-Id: Ia40a6f3b2243c95fe428a080b938e11d8ab771a7
diff --git a/src/utils.c b/src/utils.c
index 6116d3a..896e917 100644
--- a/src/utils.c
+++ b/src/utils.c
@@ -553,6 +553,8 @@
 	return true;
 }
 
+static const char osmo_identifier_illegal_chars[] = "., {}[]()<>|~\\^`'\"?=;/+*&%$#!";
+
 /*! Determine if a given identifier is valid, i.e. doesn't contain illegal chars
  *  \param[in] str String to validate
  *  \param[in] sep_chars Permitted separation characters between identifiers.
@@ -561,7 +563,6 @@
 bool osmo_separated_identifiers_valid(const char *str, const char *sep_chars)
 {
 	/* characters that are illegal in names */
-	static const char illegal_chars[] = "., {}[]()<>|~\\^`'\"?=;/+*&%$#!";
 	unsigned int i;
 	size_t len;
 
@@ -578,7 +579,7 @@
 		if (!isprint((int)str[i]))
 			return false;
 		/* check for some explicit reserved control characters */
-		if (strchr(illegal_chars, str[i]))
+		if (strchr(osmo_identifier_illegal_chars, str[i]))
 			return false;
 	}
 
@@ -594,6 +595,25 @@
 	return osmo_separated_identifiers_valid(str, NULL);
 }
 
+/*! Replace characters in the given string buffer so that it is guaranteed to pass osmo_separated_identifiers_valid().
+ * To guarantee passing osmo_separated_identifiers_valid(), replace_with must not itself be an illegal character. If in
+ * doubt, use '-'.
+ * \param[inout] str  Identifier to sanitize, must be nul terminated and in a writable buffer.
+ * \param[in] sep_chars  Additional characters that are allowed besides osmo_identifier_illegal_chars.
+ * \param[in] replace_with  Replace any illegal characters with this character.
+ */
+void osmo_identifier_sanitize_buf(char *str, const char *sep_chars, char replace_with)
+{
+	char *pos;
+	if (!str)
+		return;
+	for (pos = str; *pos; pos++) {
+		if (strchr(osmo_identifier_illegal_chars, *pos)
+		    || (sep_chars && strchr(sep_chars, *pos)))
+			*pos = replace_with;
+	}
+}
+
 /*! Like osmo_escape_str_buf2, but with unusual ordering of arguments, and may sometimes return string constants instead
  * of writing to buf for error cases or empty input.
  * Most *_buf() functions have the buffer and size as first arguments, here the arguments are last.