diff options
author | Přemysl Janouch <p.janouch@gmail.com> | 2015-04-26 21:46:59 +0200 |
---|---|---|
committer | Přemysl Janouch <p.janouch@gmail.com> | 2015-04-26 22:30:44 +0200 |
commit | 9e548889c72d4279bcf1aa803aff14216b5fd91f (patch) | |
tree | ffcf508b550b0ec9c76988c482d351f43fa0f7fa /degesch.c | |
parent | e7341909792c26af7d7f1b0666e6973626cbf835 (diff) | |
download | xK-9e548889c72d4279bcf1aa803aff14216b5fd91f.tar.gz xK-9e548889c72d4279bcf1aa803aff14216b5fd91f.tar.xz xK-9e548889c72d4279bcf1aa803aff14216b5fd91f.zip |
degesch: add a CTCP parser
Diffstat (limited to 'degesch.c')
-rw-r--r-- | degesch.c | 154 |
1 files changed, 154 insertions, 0 deletions
@@ -2239,6 +2239,160 @@ init_readline (void) return 0; } +// --- CTCP decoding ----------------------------------------------------------- + +#define CTCP_M_QUOTE '\020' +#define CTCP_X_DELIM '\001' +#define CTCP_X_QUOTE '\\' + +struct ctcp_chunk +{ + LIST_HEADER (struct ctcp_chunk) + + bool is_extended; ///< Is this a tagged extended message? + struct str tag; ///< The tag, if any + struct str text; ///< Message contents +}; + +static struct ctcp_chunk * +ctcp_chunk_new (void) +{ + struct ctcp_chunk *self = xcalloc (1, sizeof *self); + str_init (&self->tag); + str_init (&self->text); + return self; +} + +static void +ctcp_chunk_destroy (struct ctcp_chunk *self) +{ + str_free (&self->tag); + str_free (&self->text); + free (self); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +static void +ctcp_low_level_decode (const char *message, struct str *output) +{ + bool escape = false; + for (const char *p = message; *p; p++) + { + if (escape) + { + switch (*p) + { + case '0': str_append_c (output, '\0'); break; + case 'r': str_append_c (output, '\r'); break; + case 'n': str_append_c (output, '\n'); break; + default: str_append_c (output, *p); + } + escape = false; + } + else if (*p == CTCP_M_QUOTE) + escape = true; + else + str_append_c (output, *p); + } +} + +static void +ctcp_intra_decode (const char *chunk, size_t len, struct str *output) +{ + bool escape = false; + for (size_t i = 0; i < len; i++) + { + char c = chunk[i]; + if (escape) + { + if (c == 'a') + str_append_c (output, CTCP_X_DELIM); + else + str_append_c (output, c); + escape = false; + } + else if (c == CTCP_X_QUOTE) + escape = true; + else + str_append_c (output, c); + } +} + +static void +ctcp_parse_tagged (const char *chunk, size_t len, struct ctcp_chunk *output) +{ + // We may search for the space before doing the higher level decoding, + // as it doesn't concern space characters at all + size_t tag_end = len; + for (size_t i = 0; i < len; i++) + if (chunk[i] == ' ') + { + tag_end = i; + break; + } + + output->is_extended = true; + ctcp_intra_decode (chunk, tag_end, &output->tag); + if (tag_end++ != len) + ctcp_intra_decode (chunk + tag_end, len - tag_end, &output->text); +} + +static struct ctcp_chunk * +ctcp_parse (const char *message) +{ + struct str m; + str_init (&m); + ctcp_low_level_decode (message, &m); + + struct ctcp_chunk *result = NULL, *result_tail = NULL; + + size_t start = 0; + bool in_ctcp = false; + for (size_t i = 0; i < m.len; i++) + { + char c = m.str[i]; + if (c != CTCP_X_DELIM) + continue; + + // Remember the current state + size_t my_start = start; + bool my_is_ctcp = in_ctcp; + + start = i + 1; + in_ctcp = !in_ctcp; + + // Skip empty chunks + if (my_start == i) + continue; + + struct ctcp_chunk *chunk = ctcp_chunk_new (); + if (my_is_ctcp) + ctcp_parse_tagged (m.str + my_start, i - my_start, chunk); + else + ctcp_intra_decode (m.str + my_start, i - my_start, &chunk->text); + LIST_APPEND_WITH_TAIL (result, result_tail, chunk); + } + + // Finish the last text part. We ignore unended tagged chunks. + if (!in_ctcp && start != m.len) + { + struct ctcp_chunk *chunk = ctcp_chunk_new (); + ctcp_intra_decode (m.str + start, m.len - start, &chunk->text); + LIST_APPEND_WITH_TAIL (result, result_tail, chunk); + } + + str_free (&m); + return result; +} + +static void +ctcp_destroy (struct ctcp_chunk *list) +{ + LIST_FOR_EACH (struct ctcp_chunk, iter, list) + ctcp_chunk_destroy (iter); +} + // --- Input handling ---------------------------------------------------------- // TODO: we will need a proper mode parser; to be shared with kike |