summaryrefslogtreecommitdiffstats
path: root/src/cdma-smsutil.c
diff options
context:
space:
mode:
authorLei Yu <lei.2.yu@nokia.com>2011-01-18 14:54:19 -0800
committerDenis Kenzior <denkenz@gmail.com>2011-05-24 11:55:18 -0500
commitd7ab83a50f57b4f491337b38b54c4faa7a6407fe (patch)
tree25dd9994c0ebbac82013d9a3f1706305811173df /src/cdma-smsutil.c
parentb2299175cf0afcda7312d6ad28f0fff368e251e2 (diff)
downloadofono-d7ab83a50f57b4f491337b38b54c4faa7a6407fe.tar.bz2
cdma-sms: Add CDMA SMS Support
Diffstat (limited to 'src/cdma-smsutil.c')
-rw-r--r--src/cdma-smsutil.c735
1 files changed, 735 insertions, 0 deletions
diff --git a/src/cdma-smsutil.c b/src/cdma-smsutil.c
new file mode 100644
index 00000000..283e1ed2
--- /dev/null
+++ b/src/cdma-smsutil.c
@@ -0,0 +1,735 @@
+/*
+ *
+ * oFono - Open Source Telephony
+ *
+ * Copyright (C) 2010-2011 Nokia Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <string.h>
+#include <dirent.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <glib.h>
+
+#include "cdma-smsutil.h"
+
+#define uninitialized_var(x) x = x
+
+enum cdma_sms_rec_flag {
+ CDMA_SMS_REC_FLAG_MANDATORY = 1,
+};
+
+typedef gboolean (*rec_handler)(const guint8 *, guint8, void *);
+
+struct simple_iter {
+ guint8 max;
+ const guint8 *pdu;
+ guint8 pos;
+ guint8 id;
+ guint8 len;
+ const guint8 *data;
+};
+
+static void simple_iter_init(struct simple_iter *iter,
+ const guint8 *pdu, guint8 len)
+{
+ iter->pdu = pdu;
+ iter->max = len;
+ iter->pos = 0;
+ iter->id = 0;
+ iter->len = 0;
+ iter->data = NULL;
+}
+
+static gboolean simple_iter_next(struct simple_iter *iter)
+{
+ const guint8 *pdu = iter->pdu + iter->pos;
+ const guint8 *end = iter->pdu + iter->max;
+ guint8 id;
+ guint8 len;
+
+ if (pdu == end)
+ return FALSE;
+
+ id = *pdu;
+ pdu++;
+
+ if (pdu == end)
+ return FALSE;
+
+ len = *pdu++;
+
+ if (pdu + len > end)
+ return FALSE;
+
+ iter->id = id;
+ iter->len = len;
+ iter->data = pdu;
+
+ iter->pos = pdu + len - iter->pdu;
+
+ return TRUE;
+}
+
+static guint8 simple_iter_get_id(struct simple_iter *iter)
+{
+ return iter->id;
+}
+
+static guint8 simple_iter_get_length(struct simple_iter *iter)
+{
+ return iter->len;
+}
+
+static const guint8 *simple_iter_get_data(struct simple_iter *iter)
+{
+ return iter->data;
+}
+
+static inline void set_bitmap(guint32 *bitmap, guint8 pos)
+{
+ *bitmap = *bitmap | (1 << pos);
+}
+
+/* Unpacks the byte stream. The field has to be <= 8 bits. */
+static guint8 bit_field_unpack(const guint8 *buf, guint16 offset, guint8 nbit)
+{
+ guint8 bit_pos;
+ guint8 val = 0;
+ const guint8 *pdu;
+
+ pdu = buf + (offset >> 3);
+ bit_pos = 8 - (offset & 0x7);
+
+ /* Field to be extracted is within current byte */
+ if (nbit <= bit_pos)
+ return (*pdu >> (bit_pos - nbit)) & ((1 << nbit) - 1);
+
+ /* Field to be extracted crossing two bytes */
+ val = *pdu & ((1 << bit_pos) - 1);
+ nbit -= bit_pos;
+ pdu++;
+
+ return (val << nbit) | (*pdu >> (8 - nbit));
+}
+
+/* Convert CDMA DTMF digits into a string */
+static gboolean dtmf_to_ascii(char *buf, const guint8 *addr,
+ guint8 num_fields)
+{
+ /*
+ * Mapping from binary DTMF code to the digit it represents.
+ * As defined in Table 2.7.1.3.2.4-4 of 3GPP2 C.S0005-E v2.0.
+ * Note, 0 is NOT a valid value and not mapped to
+ * any valid DTMF digit.
+ */
+ static const char dtmf_digits[13] = {0, '1', '2', '3', '4', '5', '6',
+ '7', '8', '9', '0', '*', '#'};
+ guint8 index;
+ guint8 value;
+
+ for (index = 0; index < num_fields; index++) {
+ if (addr[index] == 0 || addr[index] > 12)
+ return FALSE; /* Invalid digit in address field */
+
+ value = addr[index];
+ buf[index] = dtmf_digits[value];
+ }
+
+ buf[index] = 0; /* Make it NULL terminated string */
+
+ return TRUE;
+}
+
+const char *cdma_sms_address_to_string(const struct cdma_sms_address *addr)
+{
+ static char buf[CDMA_SMS_MAX_ADDR_FIELDS + 1];
+
+ /* TODO: Only support CDMA_SMS_DIGIT_MODE_4BIT_DTMF currently */
+ switch (addr->digit_mode) {
+ case CDMA_SMS_DIGIT_MODE_4BIT_DTMF:
+ if (dtmf_to_ascii(buf, addr->address,
+ addr->num_fields) == TRUE)
+ return buf;
+ else
+ return NULL;
+ case CDMA_SMS_DIGIT_MODE_8BIT_ASCII:
+ return NULL;
+ }
+
+ return NULL;
+}
+
+/* Decode Teleservice ID */
+static gboolean cdma_sms_decode_teleservice(const guint8 *buf, guint8 len,
+ void *data)
+{
+ enum cdma_sms_teleservice_id *id = data;
+
+ *id = bit_field_unpack(buf, 0, 8) << 8 |
+ bit_field_unpack(buf, 8, 8);
+
+ switch (*id) {
+ case CDMA_SMS_TELESERVICE_ID_CMT91:
+ case CDMA_SMS_TELESERVICE_ID_WPT:
+ case CDMA_SMS_TELESERVICE_ID_WMT:
+ case CDMA_SMS_TELESERVICE_ID_VMN:
+ case CDMA_SMS_TELESERVICE_ID_WAP:
+ case CDMA_SMS_TELESERVICE_ID_WEMT:
+ case CDMA_SMS_TELESERVICE_ID_SCPT:
+ case CDMA_SMS_TELESERVICE_ID_CATPT:
+ return TRUE;
+ }
+
+ return FALSE; /* Invalid teleservice type */
+}
+
+/* Decode Address parameter record */
+static gboolean cdma_sms_decode_addr(const guint8 *buf, guint8 len,
+ void *data)
+{
+ struct cdma_sms_address *addr = data;
+ guint16 bit_offset = 0;
+ guint8 chari_len;
+ guint16 total_num_bits = len * 8;
+ guint8 index;
+
+ addr->digit_mode = bit_field_unpack(buf, bit_offset, 1);
+ bit_offset += 1;
+
+ addr->number_mode = bit_field_unpack(buf, bit_offset, 1);
+ bit_offset += 1;
+
+ if (addr->digit_mode == CDMA_SMS_DIGIT_MODE_8BIT_ASCII) {
+ if (addr->number_mode == CDMA_SMS_NUM_MODE_DIGIT)
+ addr->digi_num_type =
+ bit_field_unpack(buf, bit_offset, 3);
+ else
+ addr->data_nw_num_type =
+ bit_field_unpack(buf, bit_offset, 3);
+
+ bit_offset += 3;
+
+ if (addr->number_mode == CDMA_SMS_NUM_MODE_DIGIT) {
+ if (bit_offset + 4 > total_num_bits)
+ return FALSE;
+
+ addr->number_plan =
+ bit_field_unpack(buf, bit_offset, 4);
+ bit_offset += 4;
+ }
+ }
+
+ if (bit_offset + 8 > total_num_bits)
+ return FALSE;
+
+ addr->num_fields = bit_field_unpack(buf, bit_offset, 8);
+ bit_offset += 8;
+
+ if (addr->digit_mode == CDMA_SMS_DIGIT_MODE_4BIT_DTMF)
+ chari_len = 4;
+ else
+ chari_len = 8;
+
+ if ((bit_offset + chari_len * addr->num_fields) > total_num_bits)
+ return FALSE;
+
+ for (index = 0; index < addr->num_fields; index++) {
+ addr->address[index] = bit_field_unpack(buf,
+ bit_offset,
+ chari_len);
+ bit_offset += chari_len;
+ }
+
+ return TRUE;
+}
+
+static char *decode_text_7bit_ascii(const struct cdma_sms_ud *ud)
+{
+ char *buf;
+
+ buf = g_new(char, ud->num_fields + 1);
+ if (buf == NULL)
+ return NULL;
+
+ memcpy(buf, ud->chari, ud->num_fields);
+ buf[ud->num_fields] = 0; /* Make it NULL terminated string */
+
+ return buf;
+}
+
+char *cdma_sms_decode_text(const struct cdma_sms_ud *ud)
+{
+ switch (ud->msg_encoding) {
+ case CDMA_SMS_MSG_ENCODING_OCTET:
+ case CDMA_SMS_MSG_ENCODING_EXTENDED_PROTOCOL_MSG:
+ return NULL; /* TODO */
+ case CDMA_SMS_MSG_ENCODING_7BIT_ASCII:
+ return decode_text_7bit_ascii(ud);
+ case CDMA_SMS_MSG_ENCODING_IA5:
+ case CDMA_SMS_MSG_ENCODING_UNICODE:
+ case CDMA_SMS_MSG_ENCODING_SHIFT_JIS:
+ case CDMA_SMS_MSG_ENCODING_KOREAN:
+ case CDMA_SMS_MSG_ENCODING_LATIN_HEBREW:
+ case CDMA_SMS_MSG_ENCODING_LATIN:
+ case CDMA_SMS_MSG_ENCODING_GSM_7BIT:
+ case CDMA_SMS_MSG_ENCODING_GSM_DATA_CODING:
+ return NULL; /* TODO */
+ }
+
+ return NULL;
+}
+
+/* Decode User Data */
+static gboolean cdma_sms_decode_ud(const guint8 *buf, guint8 len, void *data)
+{
+ guint16 bit_offset = 0;
+ guint8 chari_len = 0;
+ guint16 total_num_bits = len * 8;
+ guint8 index;
+ enum cdma_sms_msg_encoding msg_encoding;
+ struct cdma_sms_ud *ud = data;
+
+ if (total_num_bits < 13)
+ return FALSE;
+
+ msg_encoding = bit_field_unpack(buf, bit_offset, 5);
+ ud->msg_encoding = msg_encoding;
+ bit_offset += 5;
+
+ if (msg_encoding == CDMA_SMS_MSG_ENCODING_EXTENDED_PROTOCOL_MSG ||
+ msg_encoding == CDMA_SMS_MSG_ENCODING_GSM_DATA_CODING) {
+ /*
+ * Skip message type field for now.
+ * TODO: Add support for message type field.
+ */
+ bit_offset += 8;
+ }
+
+ if (bit_offset + 8 > total_num_bits)
+ return FALSE;
+
+ ud->num_fields = bit_field_unpack(buf, bit_offset, 8);
+ bit_offset += 8;
+
+ switch (msg_encoding) {
+ case CDMA_SMS_MSG_ENCODING_OCTET:
+ chari_len = 8;
+ break;
+ case CDMA_SMS_MSG_ENCODING_EXTENDED_PROTOCOL_MSG:
+ return FALSE; /* TODO */
+ case CDMA_SMS_MSG_ENCODING_7BIT_ASCII:
+ case CDMA_SMS_MSG_ENCODING_IA5:
+ chari_len = 7;
+ break;
+ case CDMA_SMS_MSG_ENCODING_UNICODE:
+ case CDMA_SMS_MSG_ENCODING_SHIFT_JIS:
+ case CDMA_SMS_MSG_ENCODING_KOREAN:
+ return FALSE; /* TODO */
+ case CDMA_SMS_MSG_ENCODING_LATIN_HEBREW:
+ case CDMA_SMS_MSG_ENCODING_LATIN:
+ chari_len = 8;
+ break;
+ case CDMA_SMS_MSG_ENCODING_GSM_7BIT:
+ chari_len = 7;
+ break;
+ case CDMA_SMS_MSG_ENCODING_GSM_DATA_CODING:
+ return FALSE; /* TODO */
+ }
+
+ /* TODO: Add support for all other encoding types */
+ if (chari_len == 0)
+ return FALSE;
+
+ if (bit_offset + chari_len * ud->num_fields > total_num_bits)
+ return FALSE;
+
+ for (index = 0; index < ud->num_fields; index++) {
+ ud->chari[index] = bit_field_unpack(buf,
+ bit_offset,
+ chari_len);
+ bit_offset += chari_len;
+ }
+
+ return TRUE;
+}
+
+/* Decode Message Identifier */
+static gboolean cdma_sms_decode_message_id(const guint8 *buf, guint8 len,
+ void *data)
+{
+ struct cdma_sms_identifier *id = data;
+
+ if (len != 3)
+ return FALSE;
+
+ id->msg_type = bit_field_unpack(buf, 0, 4);
+
+ if (id->msg_type <= 0 ||
+ id->msg_type > CDMA_SMS_MSG_TYPE_SUBMIT_REPORT)
+ return FALSE; /* Invalid message type */
+
+ id->msg_id = (bit_field_unpack(buf, 4, 8) << 8) |
+ bit_field_unpack(buf, 12, 8);
+
+ id->header_ind = bit_field_unpack(buf, 20, 1);
+
+ return TRUE;
+}
+
+static gboolean find_and_decode(struct simple_iter *iter, guint8 rec_id,
+ rec_handler handler, void *data)
+{
+ guint8 id;
+ guint8 len;
+ const guint8 *buf;
+
+ while (simple_iter_next(iter) == TRUE) {
+ id = simple_iter_get_id(iter);
+ if (id != rec_id)
+ continue;
+
+ len = simple_iter_get_length(iter);
+ buf = simple_iter_get_data(iter);
+
+ return handler(buf, len, data);
+ }
+
+ return FALSE;
+}
+
+static rec_handler subparam_handler_for_id(enum cdma_sms_subparam_id id)
+{
+ switch (id) {
+ case CDMA_SMS_SUBPARAM_ID_MESSAGE_ID:
+ return cdma_sms_decode_message_id;
+ case CDMA_SMS_SUBPARAM_ID_USER_DATA:
+ return cdma_sms_decode_ud;
+ case CDMA_SMS_SUBPARAM_ID_USER_RESPONSE_CODE:
+ case CDMA_SMS_SUBPARAM_ID_MC_TIME_STAMP:
+ case CDMA_SMS_SUBPARAM_ID_VALIDITY_PERIOD_ABSOLUTE:
+ case CDMA_SMS_SUBPARAM_ID_VALIDITY_PERIOD_RELATIVE:
+ case CDMA_SMS_SUBPARAM_ID_DEFERRED_DELIVERY_TIME_ABSOLUTE:
+ case CDMA_SMS_SUBPARAM_ID_DEFERRED_DELIVERY_TIME_RELATIVE:
+ case CDMA_SMS_SUBPARAM_ID_PRIORITY_INDICATOR:
+ case CDMA_SMS_SUBPARAM_ID_PRIVACY_INDICATOR:
+ case CDMA_SMS_SUBPARAM_ID_REPLY_OPTION:
+ case CDMA_SMS_SUBPARAM_ID_NUMBER_OF_MESSAGES:
+ case CDMA_SMS_SUBPARAM_ID_ALERT_ON_MESSAGE_DELIVERY:
+ case CDMA_SMS_SUBPARAM_ID_LANGUAGE_INDICATOR:
+ case CDMA_SMS_SUBPARAM_ID_CALL_BACK_NUMBER:
+ case CDMA_SMS_SUBPARAM_ID_MESSAGE_DISPLAY_MODE:
+ case CDMA_SMS_SUBPARAM_ID_MULTIPLE_ENCODING_USER_DATA:
+ case CDMA_SMS_SUBPARAM_ID_MESSAGE_DEPOSIT_INDEX:
+ case CDMA_SMS_SUBPARAM_ID_SERVICE_CATEGORY_PROGRAM_DATA:
+ case CDMA_SMS_SUBPARAM_ID_SERVICE_CATEGORY_PROGRAM_RESULT:
+ case CDMA_SMS_SUBPARAM_ID_MESSAGE_STATUS:
+ case CDMA_SMS_SUBPARAM_ID_TP_FAILURE_CAUSE:
+ case CDMA_SMS_SUBPARAM_ID_ENHANCED_VMN:
+ case CDMA_SMS_SUBPARAM_ID_ENHANCED_VMN_ACK:
+ return NULL; /* TODO */
+ }
+
+ return NULL;
+}
+
+struct subparam_handler_entry {
+ enum cdma_sms_subparam_id id;
+ int flags;
+ gboolean found;
+ void *data;
+};
+
+static gboolean decode_subparams(struct simple_iter *iter, guint32 *bitmap,
+ void *data, ...)
+{
+ GSList *entries = NULL;
+ GSList *l;
+ va_list args;
+ gboolean decode_result = TRUE;
+
+ va_start(args, data);
+
+ while (data != NULL) {
+ struct subparam_handler_entry *entry;
+
+ entry = g_new0(struct subparam_handler_entry, 1);
+
+ entry->data = data;
+ entry->id = va_arg(args, enum cdma_sms_subparam_id);
+ entry->flags = va_arg(args, int);
+
+ data = va_arg(args, void *);
+ entries = g_slist_prepend(entries, entry);
+ }
+
+ va_end(args);
+
+ entries = g_slist_reverse(entries);
+
+ l = entries;
+ while (simple_iter_next(iter) == TRUE) {
+ rec_handler handler;
+ struct subparam_handler_entry *entry;
+ guint8 subparam_len;
+ const guint8 *subparam_buf;
+ GSList *l2;
+
+ for (l2 = l; l2; l2 = l2->next) {
+ entry = l2->data;
+
+ if (simple_iter_get_id(iter) == entry->id)
+ break;
+ }
+
+ /* Ignore unexpected subparameter record */
+ if (l2 == NULL)
+ continue;
+
+ entry->found = TRUE;
+
+ subparam_len = simple_iter_get_length(iter);
+ subparam_buf = simple_iter_get_data(iter);
+
+ handler = subparam_handler_for_id(entry->id);
+
+ decode_result = handler(subparam_buf,
+ subparam_len,
+ entry->data);
+ if (decode_result == FALSE)
+ break; /* Stop if decoding failed */
+
+ set_bitmap(bitmap, entry->id);
+ }
+
+ for (; l; l = l->next) {
+ struct subparam_handler_entry *entry = l->data;
+
+ if ((entry->flags & CDMA_SMS_REC_FLAG_MANDATORY) &&
+ (entry->found == FALSE)) {
+ decode_result = FALSE;
+ break;
+ }
+ }
+
+ g_slist_foreach(entries, (GFunc) g_free, NULL);
+ g_slist_free(entries);
+
+ return decode_result;
+}
+
+/* Decode WMT */
+static gboolean cdma_sms_decode_wmt(struct simple_iter *iter,
+ struct cdma_sms_bearer_data *bd)
+{
+ switch (bd->id.msg_type) {
+ case CDMA_SMS_MSG_TYPE_RESERVED:
+ return FALSE; /* Invalid */
+ case CDMA_SMS_MSG_TYPE_DELIVER:
+ /*
+ * WMT DELIVER, table 4.3.4-1 of C.S0015-B v2.0
+ * TODO: Not all optional subparameters supported.
+ */
+ return decode_subparams(iter,
+ &bd->subparam_bitmap,
+ &bd->wmt_deliver.ud,
+ CDMA_SMS_SUBPARAM_ID_USER_DATA,
+ 0,
+ NULL);
+ break;
+ case CDMA_SMS_MSG_TYPE_SUBMIT:
+ case CDMA_SMS_MSG_TYPE_CANCEL:
+ return FALSE; /* Invalid for MT WMT */
+ case CDMA_SMS_MSG_TYPE_DELIVER_ACK:
+ case CDMA_SMS_MSG_TYPE_USER_ACK:
+ case CDMA_SMS_MSG_TYPE_READ_ACK:
+ return FALSE; /* TODO: Not supported yet */
+ case CDMA_SMS_MSG_TYPE_DELIVER_REPORT:
+ case CDMA_SMS_MSG_TYPE_SUBMIT_REPORT:
+ return FALSE; /* Invalid for MT WMT */
+ }
+
+ return FALSE;
+}
+
+static gboolean p2p_decode_bearer_data(const guint8 *buf, guint8 len,
+ enum cdma_sms_teleservice_id tele_id,
+ struct cdma_sms_bearer_data *bd)
+{
+ struct simple_iter iter;
+
+ simple_iter_init(&iter, buf, len);
+
+ /* Message Identifier is mandatory, * Section 4 of C.S0015-B v2.0 */
+ if (find_and_decode(&iter,
+ CDMA_SMS_SUBPARAM_ID_MESSAGE_ID,
+ cdma_sms_decode_message_id,
+ &bd->id) != TRUE)
+ return FALSE;
+
+ set_bitmap(&bd->subparam_bitmap, CDMA_SMS_SUBPARAM_ID_MESSAGE_ID);
+
+ simple_iter_init(&iter, buf, len);
+
+ switch (tele_id) {
+ case CDMA_SMS_TELESERVICE_ID_CMT91:
+ case CDMA_SMS_TELESERVICE_ID_WPT:
+ return FALSE; /* TODO */
+ case CDMA_SMS_TELESERVICE_ID_WMT:
+ return cdma_sms_decode_wmt(&iter, bd);
+ case CDMA_SMS_TELESERVICE_ID_VMN:
+ case CDMA_SMS_TELESERVICE_ID_WAP:
+ case CDMA_SMS_TELESERVICE_ID_WEMT:
+ case CDMA_SMS_TELESERVICE_ID_SCPT:
+ case CDMA_SMS_TELESERVICE_ID_CATPT:
+ return FALSE; /* TODO */
+ }
+
+ return FALSE;
+}
+
+/* Decode Bearer Data */
+static gboolean cdma_sms_decode_bearer_data(const guint8 *buf, guint8 len,
+ void *data)
+{
+ struct cdma_sms *msg = data;
+
+ switch (msg->type) {
+ case CDMA_SMS_TP_MSG_TYPE_P2P:
+ return p2p_decode_bearer_data(buf, len,
+ msg->p2p_msg.teleservice_id,
+ &msg->p2p_msg.bd);
+ case CDMA_SMS_TP_MSG_TYPE_BCAST:
+ return FALSE; /* TODO */
+ case CDMA_SMS_TP_MSG_TYPE_ACK:
+ return FALSE; /* Invalid */
+ }
+
+ return FALSE;
+}
+
+static rec_handler param_handler_for_id(enum cdma_sms_param_id id,
+ struct cdma_sms *incoming,
+ void **data)
+{
+ if (incoming->type != CDMA_SMS_TP_MSG_TYPE_P2P)
+ return NULL; /* TODO: Other types not supported yet */
+
+ switch (id) {
+ case CDMA_SMS_PARAM_ID_TELESERVICE_IDENTIFIER:
+ *data = &incoming->p2p_msg.teleservice_id;
+ return cdma_sms_decode_teleservice;
+ case CDMA_SMS_PARAM_ID_SERVICE_CATEGORY:
+ return NULL; /* TODO */
+ case CDMA_SMS_PARAM_ID_ORIGINATING_ADDRESS:
+ *data = &incoming->p2p_msg.oaddr;
+ return cdma_sms_decode_addr;
+ case CDMA_SMS_PARAM_ID_ORIGINATING_SUBADDRESS:
+ case CDMA_SMS_PARAM_ID_DESTINATION_ADDRESS:
+ case CDMA_SMS_PARAM_ID_DESTINATION_SUBADDRESS:
+ case CDMA_SMS_PARAM_ID_BEARER_REPLY_OPTION:
+ case CDMA_SMS_PARAM_ID_CAUSE_CODE:
+ return NULL; /* TODO */
+ case CDMA_SMS_PARAM_ID_BEARER_DATA:
+ *data = incoming;
+ return cdma_sms_decode_bearer_data;
+ }
+
+ return NULL;
+}
+
+static gboolean cdma_sms_p2p_decode(const guint8 *pdu, guint8 len,
+ struct cdma_sms *incoming)
+{
+ struct simple_iter iter;
+
+ simple_iter_init(&iter, pdu, len);
+
+ /*
+ * Teleservice Identifier is mandatory,
+ * Table 3.4.2.1-1 of C.S0015-B v2.0
+ */
+ if (find_and_decode(&iter,
+ CDMA_SMS_PARAM_ID_TELESERVICE_IDENTIFIER,
+ cdma_sms_decode_teleservice,
+ &incoming->p2p_msg.teleservice_id) != TRUE)
+ return FALSE;
+
+ set_bitmap(&incoming->p2p_msg.param_bitmap,
+ CDMA_SMS_PARAM_ID_TELESERVICE_IDENTIFIER);
+
+ simple_iter_init(&iter, pdu, len);
+
+ while (simple_iter_next(&iter) == TRUE) {
+ rec_handler handler;
+ enum cdma_sms_param_id rec_id;
+ guint8 rec_len;
+ const guint8 *rec_buf;
+ void *dataobj;
+
+ rec_id = simple_iter_get_id(&iter);
+ if (rec_id == CDMA_SMS_PARAM_ID_TELESERVICE_IDENTIFIER)
+ continue;
+
+ rec_len = simple_iter_get_length(&iter);
+ rec_buf = simple_iter_get_data(&iter);
+
+ handler = param_handler_for_id(rec_id, incoming, &dataobj);
+ if (handler != NULL) {
+ if (handler(rec_buf, rec_len, dataobj) == FALSE)
+ return FALSE;
+
+ set_bitmap(&incoming->p2p_msg.param_bitmap, rec_id);
+ }
+ }
+
+ /*
+ * Originating Address is mandatory field,
+ * Table 3.4.2.1-1 of C.S0015-B v2.0
+ */
+ if ((incoming->p2p_msg.param_bitmap &
+ (1 << CDMA_SMS_PARAM_ID_ORIGINATING_ADDRESS)) == 0)
+ return FALSE;
+
+ return TRUE;
+}
+
+gboolean cdma_sms_decode(const guint8 *pdu, guint8 len,
+ struct cdma_sms *incoming)
+{
+ incoming->type = bit_field_unpack(pdu, 0, 8);
+ pdu += 1;
+ len -= 1;
+
+ switch (incoming->type) {
+ case CDMA_SMS_TP_MSG_TYPE_P2P:
+ return cdma_sms_p2p_decode(pdu, len, incoming);
+ case CDMA_SMS_TP_MSG_TYPE_BCAST:
+ case CDMA_SMS_TP_MSG_TYPE_ACK:
+ /* TODO: Not supported yet */
+ return FALSE;
+ }
+
+ return FALSE;
+}