From f20d1e390f25dd9f566c69968286b2ff4406ca87 Mon Sep 17 00:00:00 2001 From: Matt Ranostay Date: Thu, 9 May 2019 19:06:11 -0700 Subject: 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 --- binding/CMakeLists.txt | 4 +- binding/bluetooth-map-api.c | 7 +- binding/bluetooth-map-bmessage.c | 194 +++++++++++++++++++++++++++++++++++++++ binding/bluetooth-map-common.h | 4 + 4 files changed, 204 insertions(+), 5 deletions(-) create mode 100644 binding/bluetooth-map-bmessage.c (limited to 'binding') diff --git a/binding/CMakeLists.txt b/binding/CMakeLists.txt index 6c137e9..97a28df 100644 --- a/binding/CMakeLists.txt +++ b/binding/CMakeLists.txt @@ -21,7 +21,9 @@ PROJECT_TARGET_ADD(bluetooth-map-binding) # Define project Targets - add_library(bluetooth-map-binding MODULE bluetooth-map-api.c bluetooth-map-bluez.c) + add_library(bluetooth-map-binding MODULE bluetooth-map-api.c + bluetooth-map-bluez.c + bluetooth-map-bmessage.c) # Binder exposes a unique public entry point SET_TARGET_PROPERTIES(${TARGET_NAME} PROPERTIES diff --git a/binding/bluetooth-map-api.c b/binding/bluetooth-map-api.c index c897838..df90ae5 100644 --- a/binding/bluetooth-map-api.c +++ b/binding/bluetooth-map-api.c @@ -402,10 +402,9 @@ static void map_notification_event(struct map_state *ns, gchar *filename) if (!g_file_get_contents(filename, &buf, NULL, NULL)) return; - jresp = json_object_new_object(); - json_object_object_add(jresp, "bmessage", json_object_new_string(buf)); - - afb_event_push(ns->notification_event, jresp); + jresp = bmessage_parse(buf); + if (jresp) + afb_event_push(ns->notification_event, jresp); g_free(buf); } 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 + * + * 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 +#include +#include +#include +#include +#include + +#include +#include +#include + +#define AFB_BINDING_VERSION 3 +#include + +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; +} diff --git a/binding/bluetooth-map-common.h b/binding/bluetooth-map-common.h index 12fe35a..ba68781 100644 --- a/binding/bluetooth-map-common.h +++ b/binding/bluetooth-map-common.h @@ -171,4 +171,8 @@ json_object *get_named_property(const struct property_info *pi, gboolean is_json_name, const char *name, json_object *jprop); +/* functions defined in bluetooth-map-bmessage.c */ + +json_object *bmessage_parse(const gchar *bmessage); + #endif /* BLUETOOTH_MAP_COMMON_H */ -- cgit 1.2.3-korg