degesch: add a CTCP parser
This commit is contained in:
parent
e734190979
commit
9e548889c7
154
degesch.c
154
degesch.c
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue