aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatt Ranostay <matt.ranostay@konsulko.com>2019-05-09 19:06:11 -0700
committerMatt Ranostay <matt.ranostay@konsulko.com>2019-05-12 19:21:39 -0700
commitf20d1e390f25dd9f566c69968286b2ff4406ca87 (patch)
tree172fabe412cea2f7b7edbd531b5183ef238ab8f5
parent063ca7cb02117d8788af450e3d382705440fe8f0 (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>
-rw-r--r--README.md33
-rw-r--r--binding/CMakeLists.txt4
-rw-r--r--binding/bluetooth-map-api.c7
-rw-r--r--binding/bluetooth-map-bmessage.c194
-rw-r--r--binding/bluetooth-map-common.h4
5 files changed, 212 insertions, 30 deletions
diff --git a/README.md b/README.md
index f84bccc..66d92be 100644
--- a/README.md
+++ b/README.md
@@ -59,30 +59,13 @@ Send a message (if supported) via MAP profile:
<pre>
{
- "bmessage":
- "BEGIN:BMSG\r\n
- VERSION:1.0\r\n
- STATUS:UNREAD\r\n
- TYPE:SMS_GSM\r\n
- FOLDER:telecom/msg/inbox\r\n
- NOTIFICATION:1\r\n
- BEGIN:VCARD\r\n
- VERSION:2.1\r\n
- FN;CHARSET=UTF-8:Satoshi Nakamoto\r\n
- N;CHARSET=UTF-8:Satoshi\r\n
- TEL:+13605551212\r\n
- END:VCARD\r\n
- BEGIN:BENV\r\n
- BEGIN:BBODY\r\n
- CHARSET:UTF-8\r\n
- LANGUAGE:UNKNOWN\r\n
- LENGTH:46\r\n
- BEGIN:MSG\r\n
- Meet at Victor 23 at 6p?\r\n
- END:MSG\r\n
- END:BBODY\r\n
- END:BENV\r\n
- END:BMSG\r\n
- "
+ "status": "UNREAD",
+ "type": "SMS_GSM",
+ "folder": "telecom/msg/inbox",
+ "recipient": {
+ "fn": "Satoshi Nakamoto",
+ "n": "Satoshi"
+ },
+ "message": "Meet at Victor 23 at 6p?"
}
</pre>
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 <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;
+}
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 */