55 if (!g_cache_initialized) {
61 HASH_ITER(hh, g_adjectives_cache, adj_entry, adj_tmp) {
62 HASH_DEL(g_adjectives_cache, adj_entry);
63 SAFE_FREE(adj_entry->
word);
66 g_adjectives_cache = NULL;
70 HASH_ITER(hh, g_nouns_cache, noun_entry, noun_tmp) {
71 HASH_DEL(g_nouns_cache, noun_entry);
72 SAFE_FREE(noun_entry->
word);
73 SAFE_FREE(noun_entry);
77 g_cache_initialized =
false;
78 log_dev(
"Session string word cache cleaned up");
193 if (!output || output_size < SESSION_STRING_BUFFER_SIZE) {
194 return SET_ERRNO(ERROR_INVALID_PARAM,
"output buffer must be at least %zu bytes",
195 (
size_t)SESSION_STRING_BUFFER_SIZE);
206 uint32_t noun1_idx = randombytes_uniform((uint32_t)
nouns_count);
207 uint32_t noun2_idx = randombytes_uniform((uint32_t)
nouns_count);
208 const char *noun1 =
nouns[noun1_idx];
209 const char *noun2 =
nouns[noun2_idx];
212 int written =
safe_snprintf(output, output_size,
"%s-%s-%s", adj, noun1, noun2);
213 if (written < 0 || (
size_t)written >= output_size) {
214 return SET_ERRNO(ERROR_BUFFER_OVERFLOW,
"Session string too long for buffer");
217 log_dev(
"Generated session string: %s", output);
222 if (!str || str[0] ==
'\0') {
223 SET_ERRNO(ERROR_INVALID_PARAM,
"Session string is NULL or empty");
227 size_t len = strlen(str);
230 if (len < 5 || len > 47) {
231 SET_ERRNO(ERROR_INVALID_PARAM,
"Session string length %zu outside valid range 5-47", len);
237 SET_ERRNO(ERROR_INVALID_PARAM,
"Session string contains non-ASCII characters");
242 pcre2_code *regex = session_format_regex_get();
244 SET_ERRNO(ERROR_INTERNAL,
"Session string PCRE2 regex failed to initialize");
249 pcre2_match_data *match_data = pcre2_match_data_create_from_pattern(regex, NULL);
251 log_error(
"Failed to allocate match data for session string regex");
252 SET_ERRNO(ERROR_MEMORY,
"Failed to allocate match data");
256 int match_result = pcre2_jit_match(regex, (PCRE2_SPTR8)str, len, 0, 0, match_data, NULL);
258 pcre2_match_data_free(match_data);
260 if (match_result < 0) {
262 SET_ERRNO(ERROR_INVALID_PARAM,
"Session string format does not match pattern");
268 const char *hyphen1 = strchr(str,
'-');
270 SET_ERRNO(ERROR_INVALID_PARAM,
"Session string missing first hyphen");
274 const char *hyphen2 = strchr(hyphen1 + 1,
'-');
276 SET_ERRNO(ERROR_INVALID_PARAM,
"Session string missing second hyphen");
281 size_t adj_len = hyphen1 - str;
282 size_t noun1_len = hyphen2 - hyphen1 - 1;
283 size_t noun2_len = len - (hyphen2 - str) - 1;
285 if (adj_len >= 32 || noun1_len >= 32 || noun2_len >= 32) {
286 SET_ERRNO(ERROR_INVALID_PARAM,
"Session string word length out of bounds");
290 char adj[32], noun1[32], noun2[32];
291 memcpy(adj, str, adj_len);
293 memcpy(noun1, hyphen1 + 1, noun1_len);
294 noun1[noun1_len] =
'\0';
295 memcpy(noun2, hyphen2 + 1, noun2_len);
296 noun2[noun2_len] =
'\0';
300 if (!g_cache_initialized) {
301 asciichat_error_t cache_err = build_validation_caches();
302 if (cache_err != ASCIICHAT_OK) {
303 log_warn(
"Failed to initialize session string cache; accepting format-valid string");
304 log_dev(
"Valid session string format (cache unavailable): %s", str);
311 HASH_FIND_STR(g_adjectives_cache, adj, adj_entry);
313 SET_ERRNO(ERROR_INVALID_PARAM,
"Session string first word '%s' is not a valid adjective", adj);
319 HASH_FIND_STR(g_nouns_cache, noun1, noun_entry1);
321 SET_ERRNO(ERROR_INVALID_PARAM,
"Session string second word '%s' is not a valid noun", noun1);
326 HASH_FIND_STR(g_nouns_cache, noun2, noun_entry2);
328 SET_ERRNO(ERROR_INVALID_PARAM,
"Session string third word '%s' is not a valid noun", noun2);
332 log_dev(
"Valid session string: %s", str);
Hashtable entry for cached word lookups.
int safe_snprintf(char *buffer, size_t buffer_size, const char *format,...)
Safe formatted string printing to buffer.