summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--gatchat/gatserver.c266
1 files changed, 165 insertions, 101 deletions
diff --git a/gatchat/gatserver.c b/gatchat/gatserver.c
index dd481944..a1f23983 100644
--- a/gatchat/gatserver.c
+++ b/gatchat/gatserver.c
@@ -32,6 +32,22 @@
#include "ringbuffer.h"
#include "gatserver.h"
+enum ParserState {
+ PARSER_STATE_IDLE,
+ PARSER_STATE_A,
+ PARSER_STATE_SLASH,
+ PARSER_STATE_COMMAND,
+ PARSER_STATE_GARBAGE,
+};
+
+enum ParserResult {
+ PARSER_RESULT_COMMAND,
+ PARSER_RESULT_EMPTY_COMMAND,
+ PARSER_RESULT_REPEAT_LAST,
+ PARSER_RESULT_GARBAGE,
+ PARSER_RESULT_UNSURE,
+};
+
/* V.250 Table 1/V.250 Result codes */
static const char *server_result_to_string(GAtServerResult result)
{
@@ -82,10 +98,9 @@ struct _GAtServer {
gpointer debug_data; /* Data to pass to debug func */
struct ring_buffer *buf; /* Current read buffer */
guint max_read_attempts; /* Max reads per select */
+ enum ParserState parser_state;
};
-static int at_server_parse(GAtServer *server, char *buf);
-
static void g_at_server_send_result(GAtServer *server, GAtServerResult result)
{
struct v250_settings v250 = server->v250;
@@ -113,111 +128,155 @@ static void g_at_server_send_result(GAtServer *server, GAtServerResult result)
&wbuf);
}
-static gsize skip_space(const char *buf, gsize pos)
-{
- gsize i = pos;
- char c = buf[i];
-
- while (c == ' ')
- c = buf[++i];
-
- return i;
-}
-
static inline gboolean is_at_command_prefix(const char c)
{
- if (c == '&')
+ switch (c) {
+ case '+':
+ case '*':
+ case '!':
+ case '%':
+ return TRUE;
+ default:
return FALSE;
-
- return g_ascii_ispunct(c);
+ }
}
-static int parse_at_command(GAtServer *server, char *buf)
+static void parse_at_command(GAtServer *server, char *buf)
{
- int res = G_AT_SERVER_RESULT_ERROR;
-
- return res;
+ g_at_server_send_result(server, G_AT_SERVER_RESULT_ERROR);
}
-static int parse_v250_settings(GAtServer *server, char *buf)
+static void parse_v250_settings(GAtServer *server, char *buf)
{
- int res = G_AT_SERVER_RESULT_ERROR;
-
- return res;
+ g_at_server_send_result(server, G_AT_SERVER_RESULT_ERROR);
}
-static int at_server_parse(GAtServer *server, char *buf)
+static void server_parse_line(GAtServer *server, char *line)
{
- int res = G_AT_SERVER_RESULT_ERROR;
gsize i = 0;
char c;
- /* skip space after "AT" or previous command */
- i = skip_space(buf, i);
+ if (line == NULL) {
+ g_at_server_send_result(server, G_AT_SERVER_RESULT_ERROR);
+ goto done;
+ }
+
+ if (line[0] == '\0') {
+ g_at_server_send_result(server, G_AT_SERVER_RESULT_OK);
+ goto done;
+ }
- c = buf[i];
+ c = line[i];
/* skip semicolon */
if (c == ';')
- c = buf[++i];
+ c = line[++i];
if (is_at_command_prefix(c) || c == 'A' || c == 'D' || c == 'H')
- res = parse_at_command(server, buf + i);
+ parse_at_command(server, line + i);
else if (g_ascii_isalpha(c) || c == '&')
- res = parse_v250_settings(server, buf + i);
- else if (c == '\0')
- res = G_AT_SERVER_RESULT_OK;
+ parse_v250_settings(server, line + i);
+ else
+ g_at_server_send_result(server, G_AT_SERVER_RESULT_ERROR);
- return res;
+done:
+ g_free(line);
}
-static void parse_buffer(GAtServer *server, char *buf)
+static enum ParserResult server_feed(GAtServer *server,
+ const char *bytes, gsize *len)
{
- int res = G_AT_SERVER_RESULT_ERROR;
gsize i = 0;
+ enum ParserResult res = PARSER_RESULT_UNSURE;
+ char s3 = server->v250.s3;
+
+ while (i < *len) {
+ char byte = bytes[i];
+
+ switch (server->parser_state) {
+ case PARSER_STATE_IDLE:
+ if (byte == s3) {
+ i += 1;
+ res = PARSER_RESULT_EMPTY_COMMAND;
+ goto out;
+ } else if (byte == '\n') {
+ i += 1;
+ res = PARSER_RESULT_GARBAGE;
+ goto out;
+ } else if (byte == 'A' || byte == 'a')
+ server->parser_state = PARSER_STATE_A;
+ else if (byte != ' ' && byte != '\t')
+ server->parser_state = PARSER_STATE_GARBAGE;
+ break;
- if (!buf)
- return;
+ case PARSER_STATE_A:
+ if (byte == s3) {
+ server->parser_state = PARSER_STATE_IDLE;
+ i += 1;
+ res = PARSER_RESULT_GARBAGE;
+ goto out;
+ } else if (byte == '/')
+ server->parser_state = PARSER_STATE_SLASH;
+ else if (byte == 'T' || byte == 't')
+ server->parser_state = PARSER_STATE_COMMAND;
+ else
+ server->parser_state = PARSER_STATE_GARBAGE;
- g_at_util_debug_chat(TRUE, (char *) buf, strlen(buf),
- server->debugf, server->debug_data);
+ break;
- /* skip header space */
- buf += skip_space(buf, i);
+ case PARSER_STATE_SLASH:
+ if (byte == s3) {
+ server->parser_state = PARSER_STATE_IDLE;
+ i+= 1;
+ res = PARSER_RESULT_REPEAT_LAST;
+ goto out;
+ } else if (byte != ' ' && byte != '\t')
+ server->parser_state = PARSER_STATE_GARBAGE;
- /* Make sure the command line prefix is "AT" or "at" */
- if (g_str_has_prefix(buf, "AT") ||
- g_str_has_prefix(buf, "at"))
- res = at_server_parse(server, (char *) buf + 2);
+ break;
- g_at_server_send_result(server, res);
+ case PARSER_STATE_COMMAND:
+ if (byte == s3) {
+ server->parser_state = PARSER_STATE_IDLE;
+ i += 1;
+ res = PARSER_RESULT_COMMAND;
+ goto out;
+ }
+ break;
- /* We're overflowing the buffer, shutdown the socket */
- if (server->buf && ring_buffer_avail(server->buf) == 0)
- g_at_server_shutdown(server);
+ case PARSER_STATE_GARBAGE:
+ if (byte == s3) {
+ server->parser_state = PARSER_STATE_IDLE;
+
+ i += 1;
+ res = PARSER_RESULT_GARBAGE;
+ goto out;
+ }
+ break;
+
+ default:
+ break;
+ };
- if (buf)
- g_free(buf);
+ i += 1;
+ }
+
+out:
+ *len = i;
+ return res;
}
-static char *extract_line(GAtServer *p, unsigned int *unread)
+static char *extract_line(GAtServer *p)
{
unsigned int wrap = ring_buffer_len_no_wrap(p->buf);
unsigned int pos = 0;
unsigned char *buf = ring_buffer_read_ptr(p->buf, pos);
- char s3 = p->v250.s3;
- char s4 = p->v250.s4;
- char *line;
-
int strip_front = 0;
- int strip_tail = 0;
int line_length = 0;
+ char *line;
while (pos < p->read_so_far) {
- if (*buf == s3 || *buf == s4)
- if (!line_length)
+ if (line_length == 0 && (*buf == ' ' || *buf == '\t'))
strip_front += 1;
- else
- break;
else
line_length += 1;
@@ -228,10 +287,8 @@ static char *extract_line(GAtServer *p, unsigned int *unread)
buf = ring_buffer_read_ptr(p->buf, pos);
}
- if (!line_length) {
- ring_buffer_drain(p->buf, strip_front);
- return NULL;
- }
+ /* We will strip AT and \r */
+ line_length -= 3;
line = g_try_new(char, line_length + 1);
@@ -240,28 +297,14 @@ static char *extract_line(GAtServer *p, unsigned int *unread)
return NULL;
}
- ring_buffer_drain(p->buf, strip_front);
+ /* Strip leading whitespace + AT */
+ ring_buffer_drain(p->buf, strip_front + 2);
ring_buffer_read(p->buf, line, line_length);
+ /* Strip \r */
+ ring_buffer_drain(p->buf, 1);
line[line_length] = '\0';
- while (pos < p->read_so_far) {
- if (*buf == s3 || *buf == s4)
- strip_tail += 1;
- else
- break;
-
- buf += 1;
- pos += 1;
-
- if (pos == wrap)
- buf = ring_buffer_read_ptr(p->buf, pos);
- }
-
- ring_buffer_drain(p->buf, strip_tail);
-
- *unread = p->read_so_far - strip_front - line_length - strip_tail;
-
return line;
}
@@ -270,35 +313,56 @@ static void new_bytes(GAtServer *p)
unsigned int len = ring_buffer_len(p->buf);
unsigned int wrap = ring_buffer_len_no_wrap(p->buf);
unsigned char *buf = ring_buffer_read_ptr(p->buf, p->read_so_far);
- char s3 = p->v250.s3;
+ enum ParserState result;
- while (p->read_so_far < len) {
+ while (p->server_io && (p->read_so_far < len)) {
gsize rbytes = MIN(len - p->read_so_far, wrap - p->read_so_far);
- unsigned char *s3_pos = memchr(buf, s3, rbytes);
- char *line = NULL;
- unsigned int unread = 0;
+ result = server_feed(p, (char *)buf, &rbytes);
+ buf += rbytes;
p->read_so_far += rbytes;
- if (s3_pos)
- line = extract_line(p, &unread);
-
- buf += rbytes - unread;
- p->read_so_far -= unread;
-
if (p->read_so_far == wrap) {
buf = ring_buffer_read_ptr(p->buf, p->read_so_far);
wrap = len;
}
- if (s3_pos) {
- parse_buffer(p, line);
+ if (result == PARSER_RESULT_UNSURE)
+ continue;
+
+ switch (result) {
+ case PARSER_RESULT_EMPTY_COMMAND:
+ /*
+ * According to section 5.2.4 and 5.6 of V250,
+ * Empty commands must be OK by the DCE
+ */
+ g_at_server_send_result(p, G_AT_SERVER_RESULT_OK);
+ ring_buffer_drain(p->buf, p->read_so_far);
+ break;
+
+ case PARSER_RESULT_COMMAND:
+ server_parse_line(p, extract_line(p));
+ break;
+
+ case PARSER_RESULT_REPEAT_LAST:
+ /* TODO */
+ g_at_server_send_result(p, G_AT_SERVER_RESULT_OK);
+ ring_buffer_drain(p->buf, p->read_so_far);
+ break;
- len -= p->read_so_far;
- wrap -= p->read_so_far;
- p->read_so_far = 0;
+ default:
+ ring_buffer_drain(p->buf, p->read_so_far);
+ break;
}
+
+ len -= p->read_so_far;
+ wrap -= p->read_so_far;
+ p->read_so_far = 0;
}
+
+ /* We're overflowing the buffer, shutdown the socket */
+ if (p->buf && ring_buffer_avail(p->buf) == 0)
+ g_source_remove(p->server_watch);
}
static gboolean received_data(GIOChannel *channel, GIOCondition cond,