diff options
author | Matt Ranostay <matt.ranostay@konsulko.com> | 2019-05-09 19:06:11 -0700 |
---|---|---|
committer | Matt Ranostay <matt.ranostay@konsulko.com> | 2019-05-12 19:21:39 -0700 |
commit | f20d1e390f25dd9f566c69968286b2ff4406ca87 (patch) | |
tree | 172fabe412cea2f7b7edbd531b5183ef238ab8f5 /binding/bluetooth-map-bmessage.c | |
parent | 063ca7cb02117d8788af450e3d382705440fe8f0 (diff) |
binding: bluetooth-map: add parser for bMessage output
Parser to output more user-readable JSON of incoming messages
Bug-AGL: SPEC-2351 SPEC-2392
Change-Id: Ib758f8901ff01579899112c75119596c4a0726be
Signed-off-by: Matt Ranostay <matt.ranostay@konsulko.com>
Diffstat (limited to 'binding/bluetooth-map-bmessage.c')
-rw-r--r-- | binding/bluetooth-map-bmessage.c | 194 |
1 files changed, 194 insertions, 0 deletions
diff --git a/binding/bluetooth-map-bmessage.c b/binding/bluetooth-map-bmessage.c new file mode 100644 index 0000000..146c7db --- /dev/null +++ b/binding/bluetooth-map-bmessage.c @@ -0,0 +1,194 @@ +/* + * Copyright (C) 2019 Konsulko Group + * Author: Matt Ranostay <matt.ranostay@konsulko.com> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define _GNU_SOURCE +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/time.h> +#include <time.h> + +#include <glib.h> +#include <gio/gio.h> +#include <json-c/json.h> + +#define AFB_BINDING_VERSION 3 +#include <afb/afb-binding.h> + +static void populate_json_entry(gchar *msg, json_object *jresp) +{ + gchar **tmp = g_strsplit(msg, ":", 2); + gchar *key = g_ascii_strdown(tmp[0], -1), *ptr; + + // TODO: process CHARSET in fields + ptr = strstr(key, ";"); + if (ptr) + *ptr = '\0'; + + if (strlen(tmp[1])) + json_object_object_add(jresp, key, json_object_new_string(tmp[1])); + + g_free(key); + g_strfreev(tmp); +} + +static gboolean __expect(gchar ***msg, gchar *str, json_object *jresp, gboolean autoincrement) +{ + gboolean ret = FALSE; + gchar *tmp; + + if (!**msg) + return FALSE; + + tmp = **msg; + + if (jresp) { + ret = g_str_has_prefix(tmp, str); + populate_json_entry(tmp, jresp); + } else if (g_str_has_suffix(str, ":")) { + ret = g_str_has_prefix(tmp, str); + } else { + if (!g_strcmp0(tmp, str)) + ret = TRUE; + } + + if (autoincrement) + (*msg)++; + + return ret; +} + +#define expect(msg, str, jresp) { if (!__expect(msg, str, jresp, TRUE)) { return FALSE; }; } + +static gboolean parse_recipient_vcard(gchar ***msg, json_object *jresp) +{ + json_object *jobj; + + expect(msg, "VERSION:", NULL); + + jobj = json_object_new_object(); + json_object_object_add(jresp, "recipient", jobj); + + expect(msg, "FN", jobj); + expect(msg, "N", jobj); + expect(msg, "TEL:", jobj); + + return TRUE; +} + +static gboolean __bmessage_parse(gchar **msg, json_object *jresp) +{ + gboolean benv = FALSE, bmsg = FALSE, vcard = FALSE; + GString *message = g_string_new(NULL); + + // BMSG: Begin of valid message + expect(&msg, "BEGIN:BMSG", NULL); + + // BMSG: Both Android and iOS + expect(&msg, "VERSION:", NULL); + expect(&msg, "STATUS:", jresp); + expect(&msg, "TYPE:", jresp); + expect(&msg, "FOLDER:", jresp); + + // BMSG: iOS puts some non-standard stuff here + + while (*msg) { + if (!vcard && __expect(&msg, "BEGIN:VCARD", NULL, TRUE)) { + vcard = parse_recipient_vcard(&msg, jresp); + continue; + } + + // VCARD: any BENV is invalid if comes before the recipient data + if (!vcard) + continue; + + if (!benv && __expect(&msg, "BEGIN:BENV", NULL, TRUE)) { + benv = TRUE; + continue; + } + + // BENV: can't be in the message body yet + if (!benv) + continue; + + if (!bmsg && __expect(&msg, "BEGIN:MSG", NULL, TRUE)) { + bmsg = TRUE; + continue; + } + + // MSG: not in message yet + if (!bmsg) + continue; + + // MSG: Keep looking for END:MSG + if (!__expect(&msg, "END:MSG", NULL, FALSE)) { + g_string_append(message, *msg); + g_string_append(message, "\n"); + } + + // MSG: Keep looking for END:MSG (increment this time) + if (__expect(&msg, "END:MSG", NULL, TRUE)) + break; + } + + // BMSG: End of valid message + expect(&msg, "END:BBODY", NULL); + expect(&msg, "END:BENV", NULL); + expect(&msg, "END:BMSG", NULL); + + json_object_object_add(jresp, "message", json_object_new_string(message->str)); + g_string_free(message, TRUE); + + // If not BMSG then isn't a valid message + return bmsg; +} + +// NOTE: replaces \r with \0 to make it easier to parse +static void sanitize_msg(gchar **msg) +{ + while (*msg) { + gchar *tmp = *msg++; + if (g_str_has_suffix(tmp, "\r")) + *strstr(tmp, "\r") = '\0'; + } +} + +json_object *bmessage_parse(const gchar *bmessage) +{ + gchar **msg; + json_object *jresp = json_object_new_object(); + gboolean ret; + + if (!bmessage || !strlen(bmessage)) + return NULL; + + AFB_INFO("Parsing incoming bMessage of length %lu", strlen(bmessage)); + + msg = g_strsplit(bmessage, "\n", -1); + sanitize_msg(msg); + + ret = __bmessage_parse(msg, jresp); + if (!ret) { + json_object_put(jresp); + jresp = NULL; + } + + g_strfreev(msg); + + return jresp; +} |