blob: 4f31af4549466dcca233b9f9d261308604043310 [file] [log] [blame]
/*
*
* oFono - Open Source Telephony
*
* Copyright (C) 2008-2011 Intel 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
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#include <glib.h>
#include <ell/ell.h>
#include <ofono/types.h>
#include "smsutil.h"
#include "stkutil.h"
#include "simutil.h"
#include "util.h"
enum stk_data_object_flag {
DATAOBJ_FLAG_MANDATORY = 1,
DATAOBJ_FLAG_MINIMUM = 2,
DATAOBJ_FLAG_CR = 4,
DATAOBJ_FLAG_LIST = 8,
};
struct stk_file_iter {
const uint8_t *start;
unsigned int pos;
unsigned int max;
uint8_t len;
const uint8_t *file;
};
struct stk_tlv_builder {
struct comprehension_tlv_builder ctlv;
uint8_t *value;
unsigned int len;
unsigned int max_len;
};
typedef bool (*dataobj_handler)(struct comprehension_tlv_iter *, void *);
typedef bool (*dataobj_writer)(struct stk_tlv_builder *, const void *, bool);
/*
* Defined in TS 102.223 Section 8.13
* The type of gsm sms can be SMS-COMMAND AND SMS-SUBMIT. According to 23.040,
* the maximum length is 164 bytes. But for SMS-SUBMIT, sms may be packed by
* ME. Thus the maximum length of messsage could be 160 bytes, instead of 140
* bytes. So the total maximum length could be 184 bytes. Refer TS 31.111,
* section 6.4.10 for details.
*/
struct gsm_sms_tpdu {
unsigned int len;
uint8_t tpdu[184];
};
#define CHECK_TEXT_AND_ICON(text, icon_id) \
if (status != STK_PARSE_RESULT_OK) \
return status; \
\
if ((text == NULL || text[0] == '\0') && icon_id != 0) \
status = STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD; \
static char *decode_text(uint8_t dcs, int len, const unsigned char *data)
{
char *utf8;
enum sms_charset charset;
if (sms_dcs_decode(dcs, NULL, &charset, NULL, NULL) == FALSE)
return NULL;
switch (charset) {
case SMS_CHARSET_7BIT:
{
long written;
unsigned long max_to_unpack = len * 8 / 7;
uint8_t *unpacked = unpack_7bit(data, len, 0, false,
max_to_unpack,
&written, 0);
if (unpacked == NULL)
return NULL;
utf8 = convert_gsm_to_utf8(unpacked, written,
NULL, NULL, 0);
l_free(unpacked);
break;
}
case SMS_CHARSET_8BIT:
utf8 = convert_gsm_to_utf8(data, len, NULL, NULL, 0);
break;
case SMS_CHARSET_UCS2:
utf8 = l_utf8_from_ucs2be(data, len);
break;
default:
utf8 = NULL;
}
return utf8;
}
/* For data object only to indicate its existence */
static bool parse_dataobj_common_bool(struct comprehension_tlv_iter *iter,
bool *out)
{
if (comprehension_tlv_iter_get_length(iter) != 0)
return false;
*out = true;
return true;
}
/* For data object that only has one byte */
static bool parse_dataobj_common_byte(struct comprehension_tlv_iter *iter,
uint8_t *out)
{
const uint8_t *data;
if (comprehension_tlv_iter_get_length(iter) != 1)
return false;
data = comprehension_tlv_iter_get_data(iter);
*out = data[0];
return true;
}
/* For data object that only has text terminated by '\0' */
static bool parse_dataobj_common_text(struct comprehension_tlv_iter *iter,
char **text)
{
const uint8_t *data;
unsigned int len = comprehension_tlv_iter_get_length(iter);
if (len < 1)
return false;
data = comprehension_tlv_iter_get_data(iter);
*text = l_malloc(len + 1);
memcpy(*text, data, len);
(*text)[len] = '\0';
return true;
}
/* For data object that only has a byte array with undetermined length */
static bool parse_dataobj_common_byte_array(struct comprehension_tlv_iter *iter,
struct stk_common_byte_array *array)
{
const uint8_t *data;
unsigned int len = comprehension_tlv_iter_get_length(iter);
if (len < 1)
return false;
data = comprehension_tlv_iter_get_data(iter);
array->len = len;
array->array = l_malloc(len);
memcpy(array->array, data, len);
return true;
}
static void stk_file_iter_init(struct stk_file_iter *iter,
const uint8_t *start, unsigned int len)
{
iter->start = start;
iter->max = len;
iter->pos = 0;
}
static bool stk_file_iter_next(struct stk_file_iter *iter)
{
unsigned int pos = iter->pos;
const unsigned int max = iter->max;
const uint8_t *start = iter->start;
unsigned int i;
uint8_t last_type;
if (pos + 2 >= max)
return false;
/* SIM EFs always start with ROOT MF, 0x3f */
if (start[iter->pos] != 0x3f)
return false;
last_type = 0x3f;
for (i = pos + 2; i < max; i += 2) {
/*
* Check the validity of file type.
* According to TS 11.11, each file id contains of two bytes,
* in which the first byte is the type of file. For GSM is:
* 0x3f: master file
* 0x7f: 1st level dedicated file
* 0x5f: 2nd level dedicated file
* 0x2f: elementary file under the master file
* 0x6f: elementary file under 1st level dedicated file
* 0x4f: elementary file under 2nd level dedicated file
*/
switch (start[i]) {
case 0x2f:
if (last_type != 0x3f)
return false;
break;
case 0x6f:
if (last_type != 0x7f)
return false;
break;
case 0x4f:
if (last_type != 0x5f)
return false;
break;
case 0x7f:
if (last_type != 0x3f)
return false;
break;
case 0x5f:
if (last_type != 0x7f)
return false;
break;
default:
return false;
}
if ((start[i] == 0x2f) || (start[i] == 0x6f) ||
(start[i] == 0x4f)) {
if (i + 1 >= max)
return false;
iter->file = start + pos;
iter->len = i - pos + 2;
iter->pos = i + 2;
return true;
}
last_type = start[i];
}
return false;
}
/* Defined in TS 102.223 Section 8.1 */
static bool parse_dataobj_address(struct comprehension_tlv_iter *iter,
void *user)
{
struct stk_address *addr = user;
const uint8_t *data;
unsigned int len;
char *number;
len = comprehension_tlv_iter_get_length(iter);
if (len < 2)
return false;
data = comprehension_tlv_iter_get_data(iter);
number = l_malloc(len * 2 - 1);
addr->ton_npi = data[0];
addr->number = number;
sim_extract_bcd_number(data + 1, len - 1, addr->number);
return true;
}
/* Defined in TS 102.223 Section 8.2 */
static bool parse_dataobj_alpha_id(struct comprehension_tlv_iter *iter,
void *user)
{
char **alpha_id = user;
const uint8_t *data;
unsigned int len;
char *utf8;
len = comprehension_tlv_iter_get_length(iter);
if (len == 0) {
*alpha_id = NULL;
return true;
}
data = comprehension_tlv_iter_get_data(iter);
utf8 = sim_string_to_utf8(data, len);
if (utf8 == NULL)
return false;
*alpha_id = utf8;
return true;
}
/* Defined in TS 102.223 Section 8.3 */
static bool parse_dataobj_subaddress(struct comprehension_tlv_iter *iter,
void *user)
{
struct stk_subaddress *subaddr = user;
const uint8_t *data;
unsigned int len;
len = comprehension_tlv_iter_get_length(iter);
if (len < 1)
return false;
if (len > sizeof(subaddr->subaddr))
return false;
data = comprehension_tlv_iter_get_data(iter);
subaddr->len = len;
memcpy(subaddr->subaddr, data, len);
subaddr->has_subaddr = true;
return true;
}
/* Defined in TS 102.223 Section 8.4 */
static bool parse_dataobj_ccp(struct comprehension_tlv_iter *iter, void *user)
{
struct stk_ccp *ccp = user;
const uint8_t *data;
unsigned int len;
len = comprehension_tlv_iter_get_length(iter);
if (len < 1)
return false;
if (len > sizeof(ccp->ccp))
return false;
data = comprehension_tlv_iter_get_data(iter);
ccp->len = len;
memcpy(ccp->ccp, data, len);
return true;
}
/* Defined in TS 31.111 Section 8.5 */
static bool parse_dataobj_cbs_page(struct comprehension_tlv_iter *iter,
void *user)
{
struct stk_cbs_page *cp = user;
const uint8_t *data;
unsigned int len;
len = comprehension_tlv_iter_get_length(iter);
if (len < 1)
return false;
if (len > sizeof(cp->page))
return false;
data = comprehension_tlv_iter_get_data(iter);
cp->len = len;
memcpy(cp->page, data, len);
return true;
}
/* Described in TS 102.223 Section 8.8 */
static bool parse_dataobj_duration(struct comprehension_tlv_iter *iter,
void *user)
{
struct stk_duration *duration = user;
const uint8_t *data;
if (comprehension_tlv_iter_get_length(iter) != 2)
return false;
data = comprehension_tlv_iter_get_data(iter);
if (data[0] > 0x02)
return false;
if (data[1] == 0)
return false;
duration->unit = data[0];
duration->interval = data[1];
return true;
}
/* Defined in TS 102.223 Section 8.9 */
static bool parse_dataobj_item(struct comprehension_tlv_iter *iter, void *user)
{
struct stk_item *item = user;
const uint8_t *data;
unsigned int len;
char *utf8;
len = comprehension_tlv_iter_get_length(iter);
if (len == 0)
return true;
if (len == 1)
return false;
data = comprehension_tlv_iter_get_data(iter);
/* The identifier is between 0x01 and 0xFF */
if (data[0] == 0)
return false;
utf8 = sim_string_to_utf8(data + 1, len - 1);
if (utf8 == NULL)
return false;
item->id = data[0];
item->text = utf8;
return true;
}
/* Defined in TS 102.223 Section 8.10 */
static bool parse_dataobj_item_id(struct comprehension_tlv_iter *iter,
void *user)
{
uint8_t *id = user;
const uint8_t *data;
if (comprehension_tlv_iter_get_length(iter) != 1)
return false;
data = comprehension_tlv_iter_get_data(iter);
*id = data[0];
return true;
}
/* Defined in TS 102.223 Section 8.11 */
static bool parse_dataobj_response_len(struct comprehension_tlv_iter *iter,
void *user)
{
struct stk_response_length *response_len = user;
const uint8_t *data;
if (comprehension_tlv_iter_get_length(iter) != 2)
return false;
data = comprehension_tlv_iter_get_data(iter);
response_len->min = data[0];
response_len->max = data[1];
return true;
}
/* Defined in TS 102.223 Section 8.12 */
static bool parse_dataobj_result(struct comprehension_tlv_iter *iter,
void *user)
{
struct stk_result *result = user;
const uint8_t *data;
unsigned int len;
uint8_t *additional;
len = comprehension_tlv_iter_get_length(iter);
if (len < 1)
return false;
data = comprehension_tlv_iter_get_data(iter);
if ((len < 2) && ((data[0] == 0x20) || (data[0] == 0x21) ||
(data[0] == 0x26) || (data[0] == 0x38) ||
(data[0] == 0x39) || (data[0] == 0x3a) ||
(data[0] == 0x3c) || (data[0] == 0x3d)))
return false;
additional = l_malloc(len - 1);
result->type = data[0];
result->additional_len = len - 1;
result->additional = additional;
memcpy(result->additional, data + 1, len - 1);
return true;
}
/* Defined in TS 102.223 Section 8.13 */
static bool parse_dataobj_gsm_sms_tpdu(struct comprehension_tlv_iter *iter,
void *user)
{
struct gsm_sms_tpdu *tpdu = user;
const uint8_t *data;
unsigned int len;
len = comprehension_tlv_iter_get_length(iter);
if (len < 1 || len > sizeof(tpdu->tpdu))
return false;
data = comprehension_tlv_iter_get_data(iter);
tpdu->len = len;
memcpy(tpdu->tpdu, data, len);
return true;
}
/* Defined in TS 102.223 Section 8.14 */
static bool parse_dataobj_ss(struct comprehension_tlv_iter *iter, void *user)
{
struct stk_ss *ss = user;
const uint8_t *data;
unsigned int len;
char *s;
len = comprehension_tlv_iter_get_length(iter);
if (len < 2)
return false;
data = comprehension_tlv_iter_get_data(iter);
s = l_malloc(len * 2 - 1);
ss->ton_npi = data[0];
ss->ss = s;
sim_extract_bcd_number(data + 1, len - 1, ss->ss);
return true;
}
/* Defined in TS 102.223 Section 8.15 */
static bool parse_dataobj_text(struct comprehension_tlv_iter *iter, void *user)
{
char **text = user;
unsigned int len = comprehension_tlv_iter_get_length(iter);
const uint8_t *data;
char *utf8;
if (len <= 1) {
*text = l_new(char, 1);
return true;
}
data = comprehension_tlv_iter_get_data(iter);
utf8 = decode_text(data[0], len - 1, data + 1);
if (utf8 == NULL)
return false;
*text = utf8;
return true;
}
/* Defined in TS 102.223 Section 8.16 */
static bool parse_dataobj_tone(struct comprehension_tlv_iter *iter, void *user)
{
uint8_t *byte = user;
return parse_dataobj_common_byte(iter, byte);
}
/* Defined in TS 102.223 Section 8.17 */
static bool parse_dataobj_ussd(struct comprehension_tlv_iter *iter, void *user)
{
struct stk_ussd_string *us = user;
unsigned int len = comprehension_tlv_iter_get_length(iter);
const uint8_t *data = comprehension_tlv_iter_get_data(iter);
if (len <= 1 || len > 161)
return false;
us->dcs = data[0];
us->len = len - 1;
memcpy(us->string, data + 1, us->len);
return true;
}
/* Defined in TS 102.223 Section 8.18 */
static bool parse_dataobj_file_list(struct comprehension_tlv_iter *iter,
void *user)
{
struct l_queue **out = user;
struct l_queue *fl;
const uint8_t *data;
unsigned int len;
struct stk_file *sf;
struct stk_file_iter sf_iter;
len = comprehension_tlv_iter_get_length(iter);
if (len < 5)
return false;
data = comprehension_tlv_iter_get_data(iter);
stk_file_iter_init(&sf_iter, data + 1, len - 1);
fl = l_queue_new();
while (stk_file_iter_next(&sf_iter)) {
sf = l_new(struct stk_file, 1);
sf->len = sf_iter.len;
memcpy(sf->file, sf_iter.file, sf_iter.len);
l_queue_push_tail(fl, sf);
}
if (sf_iter.pos != sf_iter.max)
goto error;
*out = fl;
return true;
error:
l_queue_destroy(fl, l_free);
return false;
}
/* Defined in TS 102.223 Section 8.19 */
static bool parse_dataobj_location_info(struct comprehension_tlv_iter *iter,
void *user)
{
struct stk_location_info *li = user;
const uint8_t *data;
unsigned int len;
len = comprehension_tlv_iter_get_length(iter);
if ((len != 5) && (len != 7) && (len != 9))
return false;
data = comprehension_tlv_iter_get_data(iter);
sim_parse_mcc_mnc(data, li->mcc, li->mnc);
li->lac_tac = (data[3] << 8) + data[4];
if (len >= 7) {
li->has_ci = true;
li->ci = (data[5] << 8) + data[6];
}
if (len == 9) {
li->has_ext_ci = true;
li->ext_ci = (data[7] << 8) + data[8];
}
return true;
}
/*
* Defined in TS 102.223 Section 8.20.
*
* According to 3GPP TS 24.008, Section 10.5.1.4, IMEI is composed of
* 15 digits and totally 8 bytes are used to represent it.
*
* Bits 1-3 of first byte represent the type of identity, and they
* are 0 1 0 separately for IMEI. Bit 4 of first byte is the odd/even
* indication, and it's 1 to indicate IMEI has odd number of digits (15).
* The rest bytes are coded using BCD coding.
*
* For example, if the IMEI is "123456789012345", then it's coded as
* "1A 32 54 76 98 10 32 54".
*/
static bool parse_dataobj_imei(struct comprehension_tlv_iter *iter,
void *user)
{
char *imei = user;
const uint8_t *data;
unsigned int len;
static const char digit_lut[] = "0123456789*#abc\0";
len = comprehension_tlv_iter_get_length(iter);
if (len != 8)
return false;
data = comprehension_tlv_iter_get_data(iter);
if ((data[0] & 0x0f) != 0x0a)
return false;
/* Assume imei is at least 16 bytes long (15 for imei + null) */
imei[0] = digit_lut[(data[0] & 0xf0) >> 4];
extract_bcd_number(data + 1, 7, imei + 1);
return true;
}
/* Defined in TS 102.223 Section 8.21 */
static bool parse_dataobj_help_request(struct comprehension_tlv_iter *iter,
void *user)
{
bool *ret = user;
return parse_dataobj_common_bool(iter, ret);
}
/* Defined in TS 102.223 Section 8.22 */
static bool parse_dataobj_network_measurement_results(
struct comprehension_tlv_iter *iter, void *user)
{
uint8_t *nmr = user;
const uint8_t *data;
unsigned int len;
len = comprehension_tlv_iter_get_length(iter);
if (len != 0x10)
return false;
data = comprehension_tlv_iter_get_data(iter);
/* Assume network measurement result is 16 bytes long */
memcpy(nmr, data, len);
return true;
}
/* Defined in TS 102.223 Section 8.23 */
static bool parse_dataobj_default_text(struct comprehension_tlv_iter *iter,
void *user)
{
char **text = user;
unsigned int len = comprehension_tlv_iter_get_length(iter);
const uint8_t *data = comprehension_tlv_iter_get_data(iter);
char *utf8;
/* DCS followed by some text, cannot be 1 */
if (len <= 1)
return false;
utf8 = decode_text(data[0], len - 1, data + 1);
if (utf8 == NULL)
return false;
*text = utf8;
return true;
}
/* Defined in TS 102.223 Section 8.24 */
static bool parse_dataobj_items_next_action_indicator(
struct comprehension_tlv_iter *iter, void *user)
{
struct stk_items_next_action_indicator *inai = user;
const uint8_t *data;
unsigned int len = comprehension_tlv_iter_get_length(iter);
if ((len < 1) || (len > sizeof(inai->list)))
return false;
data = comprehension_tlv_iter_get_data(iter);
inai->len = len;
memcpy(inai->list, data, len);
return true;
}
/* Defined in TS 102.223 Section 8.25 */
static bool parse_dataobj_event_list(struct comprehension_tlv_iter *iter,
void *user)
{
struct stk_event_list *el = user;
const uint8_t *data;
unsigned int len = comprehension_tlv_iter_get_length(iter);
if (len == 0)
return true;
if (len > sizeof(el->list))
return false;
data = comprehension_tlv_iter_get_data(iter);
el->len = len;
memcpy(el->list, data, len);
return true;
}
/* Defined in TS 102.223 Section 8.26 */
static bool parse_dataobj_cause(struct comprehension_tlv_iter *iter,
void *user)
{
struct stk_cause *cause = user;
const uint8_t *data;
unsigned int len = comprehension_tlv_iter_get_length(iter);
if ((len == 1) || (len > sizeof(cause->cause)))
return false;
cause->has_cause = true;
if (len == 0)
return true;
data = comprehension_tlv_iter_get_data(iter);
cause->len = len;
memcpy(cause->cause, data, len);
return true;
}
/* Defined in TS 102.223 Section 8.27 */
static bool parse_dataobj_location_status(struct comprehension_tlv_iter *iter,
void *user)
{
uint8_t *byte = user;
return parse_dataobj_common_byte(iter, byte);
}
/* Defined in TS 102.223 Section 8.28 */
static bool parse_dataobj_transaction_id(struct comprehension_tlv_iter *iter,
void *user)
{
struct stk_transaction_id *ti = user;
const uint8_t *data;
unsigned int len = comprehension_tlv_iter_get_length(iter);
if ((len < 1) || (len > sizeof(ti->list)))
return false;
data = comprehension_tlv_iter_get_data(iter);
ti->len = len;
memcpy(ti->list, data, len);
return true;
}
/* Defined in TS 31.111 Section 8.29 */
static bool parse_dataobj_bcch_channel_list(struct comprehension_tlv_iter *iter,
void *user)
{
struct stk_bcch_channel_list *bcl = user;
const uint8_t *data;
unsigned int len = comprehension_tlv_iter_get_length(iter);
unsigned int i;
if (len < 1)
return false;
data = comprehension_tlv_iter_get_data(iter);
bcl->num = len * 8 / 10;
for (i = 0; i < bcl->num; i++) {
unsigned int index = i * 10 / 8;
unsigned int occupied = i * 10 % 8;
bcl->channels[i] = (data[index] << (2 + occupied)) +
(data[index + 1] >> (6 - occupied));
}
bcl->has_list = true;
return true;
}
/* Defined in TS 102.223 Section 8.30 */
static bool parse_dataobj_call_control_requested_action(
struct comprehension_tlv_iter *iter, void *user)
{
struct stk_common_byte_array *array = user;
return parse_dataobj_common_byte_array(iter, array);
}
/* Defined in TS 102.223 Section 8.31 */
static bool parse_dataobj_icon_id(struct comprehension_tlv_iter *iter,
void *user)
{
struct stk_icon_id *id = user;
const uint8_t *data;
if (comprehension_tlv_iter_get_length(iter) != 2)
return false;
data = comprehension_tlv_iter_get_data(iter);
id->qualifier = data[0];
id->id = data[1];
return true;
}
/* Defined in TS 102.223 Section 8.32 */
static bool parse_dataobj_item_icon_id_list(struct comprehension_tlv_iter *iter,
void *user)
{
struct stk_item_icon_id_list *iiil = user;
const uint8_t *data;
unsigned int len = comprehension_tlv_iter_get_length(iter);
if ((len < 2) || (len > 127))
return false;
data = comprehension_tlv_iter_get_data(iter);
iiil->qualifier = data[0];
iiil->len = len - 1;
memcpy(iiil->list, data + 1, iiil->len);
return true;
}
/* Defined in TS 102.223 Section 8.33 */
static bool parse_dataobj_card_reader_status(
struct comprehension_tlv_iter *iter, void *user)
{
uint8_t *byte = user;
return parse_dataobj_common_byte(iter, byte);
}
/* Defined in TS 102.223 Section 8.34 */
static bool parse_dataobj_card_atr(struct comprehension_tlv_iter *iter,
void *user)
{
struct stk_card_atr *ca = user;
const uint8_t *data;
unsigned int len = comprehension_tlv_iter_get_length(iter);
if ((len < 1) || (len > sizeof(ca->atr)))
return false;
data = comprehension_tlv_iter_get_data(iter);
ca->len = len;
memcpy(ca->atr, data, len);
return true;
}
/* Defined in TS 102.223 Section 8.35 */
static bool parse_dataobj_c_apdu(struct comprehension_tlv_iter *iter,
void *user)
{
struct stk_c_apdu *ca = user;
const uint8_t *data;
unsigned int len = comprehension_tlv_iter_get_length(iter);
unsigned int pos;
if ((len < 4) || (len > 241))
return false;
data = comprehension_tlv_iter_get_data(iter);
ca->cla = data[0];
ca->ins = data[1];
ca->p1 = data[2];
ca->p2 = data[3];
pos = 4;
/*
* lc is 0 has the same meaning as lc is absent. But le is 0 means
* the maximum number of bytes expected in the response data field
* is 256. So we need to rely on has_le to know if it presents.
*/
if (len > 5) {
ca->lc = data[4];
if (ca->lc > sizeof(ca->data))
return false;
pos += ca->lc + 1;
if (len - pos > 1)
return false;
memcpy(ca->data, data+5, ca->lc);
}
if (len - pos > 0) {
ca->le = data[len - 1];
ca->has_le = true;
}
return true;
}
/* Defined in TS 102.223 Section 8.36 */
static bool parse_dataobj_r_apdu(struct comprehension_tlv_iter *iter,
void *user)
{
struct stk_r_apdu *ra = user;
const uint8_t *data;
unsigned int len = comprehension_tlv_iter_get_length(iter);
if ((len < 2) || (len > 239))
return false;
data = comprehension_tlv_iter_get_data(iter);
ra->sw1 = data[len-2];
ra->sw2 = data[len-1];
if (len > 2) {
ra->len = len - 2;
memcpy(ra->data, data, ra->len);
} else
ra->len = 0;
return true;
}
/* Defined in TS 102.223 Section 8.37 */
static bool parse_dataobj_timer_id(struct comprehension_tlv_iter *iter,
void *user)
{
uint8_t *byte = user;
return parse_dataobj_common_byte(iter, byte);
}
/* Defined in TS 102.223 Section 8.38 */
static bool parse_dataobj_timer_value(struct comprehension_tlv_iter *iter,
void *user)
{
struct stk_timer_value *tv = user;
const uint8_t *data;
if (comprehension_tlv_iter_get_length(iter) != 3)
return false;
data = comprehension_tlv_iter_get_data(iter);
tv->hour = sms_decode_semi_octet(data[0]);
tv->minute = sms_decode_semi_octet(data[1]);
tv->second = sms_decode_semi_octet(data[2]);
tv->has_value = true;
return true;
}
/* Defined in TS 102.223 Section 8.39 */
static bool parse_dataobj_datetime_timezone(
struct comprehension_tlv_iter *iter, void *user)
{
struct sms_scts *scts = user;
const uint8_t *data;
int offset = 0;
if (comprehension_tlv_iter_get_length(iter) != 7)
return false;
data = comprehension_tlv_iter_get_data(iter);
sms_decode_scts(data, 7, &offset, scts);
return true;
}
/* Defined in TS 102.223 Section 8.40 */
static bool parse_dataobj_at_command(struct comprehension_tlv_iter *iter,
void *user)
{
char **command = user;
return parse_dataobj_common_text(iter, command);
}
/* Defined in TS 102.223 Section 8.41 */
static bool parse_dataobj_at_response(struct comprehension_tlv_iter *iter,
void *user)
{
char **response = user;
return parse_dataobj_common_text(iter, response);
}
/* Defined in TS 102.223 Section 8.42 */
static bool parse_dataobj_bc_repeat_indicator(
struct comprehension_tlv_iter *iter, void *user)
{
struct stk_bc_repeat *bc_repeat = user;
if (!parse_dataobj_common_byte(iter, &bc_repeat->value))
return false;
bc_repeat->has_bc_repeat = true;
return true;
}
/* Defined in 102.223 Section 8.43 */
static bool parse_dataobj_imm_resp(struct comprehension_tlv_iter *iter,
void *user)
{
bool *ret = user;
return parse_dataobj_common_bool(iter, ret);
}
/* Defined in 102.223 Section 8.44 */
static bool parse_dataobj_dtmf_string(struct comprehension_tlv_iter *iter,
void *user)
{
char **dtmf = user;
const uint8_t *data;
unsigned int len = comprehension_tlv_iter_get_length(iter);
if (len < 1)
return false;
data = comprehension_tlv_iter_get_data(iter);
*dtmf = l_malloc(len * 2 + 1);
sim_extract_bcd_number(data, len, *dtmf);
return true;
}
/* Defined in 102.223 Section 8.45 */
static bool parse_dataobj_language(struct comprehension_tlv_iter *iter,
void *user)
{
char *lang = user;
const uint8_t *data;
unsigned int len = comprehension_tlv_iter_get_length(iter);
if (len != 2)
return false;
data = comprehension_tlv_iter_get_data(iter);
/*
* This is a 2 character pair as defined in ISO 639, coded using
* GSM default 7 bit alphabet with bit 8 set to 0. Since the english
* letters have the same mapping in GSM as ASCII, no conversion
* is required here
*/
memcpy(lang, data, len);
lang[len] = '\0';
return true;
}
/* Defined in 31.111 Section 8.46 */
static bool parse_dataobj_timing_advance(struct comprehension_tlv_iter *iter,
void *user)
{
struct stk_timing_advance *ta = user;
const uint8_t *data;
unsigned int len = comprehension_tlv_iter_get_length(iter);
if (len != 2)
return false;
data = comprehension_tlv_iter_get_data(iter);
ta->has_value = true;
ta->status = data[0];
ta->advance = data[1];
return true;
}
/* Defined in 102.223 Section 8.47 */
static bool parse_dataobj_browser_id(struct comprehension_tlv_iter *iter,
void *user)
{
uint8_t *byte = user;
if (!parse_dataobj_common_byte(iter, byte) || *byte > 4)
return false;
return true;
}
/* Defined in TS 102.223 Section 8.48 */
static bool parse_dataobj_url(struct comprehension_tlv_iter *iter, void *user)
{
char **url = user;
unsigned int len = comprehension_tlv_iter_get_length(iter);
if (len == 0) {
*url = NULL;
return true;
}
return parse_dataobj_common_text(iter, url);
}
/* Defined in TS 102.223 Section 8.49 */
static bool parse_dataobj_bearer(struct comprehension_tlv_iter *iter,
void *user)
{
struct stk_common_byte_array *array = user;
return parse_dataobj_common_byte_array(iter, array);
}
/* Defined in TS 102.223 Section 8.50 */
static bool parse_dataobj_provisioning_file_reference(
struct comprehension_tlv_iter *iter, void *user)
{
struct stk_file *f = user;
const uint8_t *data;
struct stk_file_iter sf_iter;
unsigned int len = comprehension_tlv_iter_get_length(iter);
if ((len < 1) || (len > 8))
return false;
data = comprehension_tlv_iter_get_data(iter);
stk_file_iter_init(&sf_iter, data, len);
stk_file_iter_next(&sf_iter);
if (sf_iter.pos != sf_iter.max)
return false;
f->len = len;
memcpy(f->file, data, len);
return true;
}
/* Defined in 102.223 Section 8.51 */
static bool parse_dataobj_browser_termination_cause(
struct comprehension_tlv_iter *iter, void *user)
{
uint8_t *byte = user;
return parse_dataobj_common_byte(iter, byte);
}
/* Defined in TS 102.223 Section 8.52 */
static bool parse_dataobj_bearer_description(
struct comprehension_tlv_iter *iter, void *user)
{
struct stk_bearer_description *bd = user;
const uint8_t *data;
unsigned int len = comprehension_tlv_iter_get_length(iter);
if (len < 1)
return false;
data = comprehension_tlv_iter_get_data(iter);
bd->type = data[0];
/* Parse only the packet data service bearer parameters */
if (bd->type != STK_BEARER_TYPE_GPRS_UTRAN)
return false;
if (len < 7)
return false;
bd->gprs.precedence = data[1];
bd->gprs.delay = data[2];
bd->gprs.reliability = data[3];
bd->gprs.peak = data[4];
bd->gprs.mean = data[5];
bd->gprs.pdp_type = data[6];
return true;
}
/* Defined in TS 102.223 Section 8.53 */
static bool parse_dataobj_channel_data(struct comprehension_tlv_iter *iter,
void *user)
{
struct stk_common_byte_array *array = user;
return parse_dataobj_common_byte_array(iter, array);
}
/* Defined in TS 102.223 Section 8.54 */
static bool parse_dataobj_channel_data_length(
struct comprehension_tlv_iter *iter, void *user)
{
uint8_t *byte = user;
return parse_dataobj_common_byte(iter, byte);
}
/* Defined in TS 102.223 Section 8.55 */
static bool parse_dataobj_buffer_size(struct comprehension_tlv_iter *iter,
void *user)
{
uint16_t *size = user;
const uint8_t *data;
if (comprehension_tlv_iter_get_length(iter) != 2)
return false;
data = comprehension_tlv_iter_get_data(iter);
*size = (data[0] << 8) + data[1];
return true;
}
/* Defined in TS 102.223 Section 8.56 */
static bool parse_dataobj_channel_status(
struct comprehension_tlv_iter *iter, void *user)
{
uint8_t *status = user;
const uint8_t *data;
if (comprehension_tlv_iter_get_length(iter) != 2)
return false;
data = comprehension_tlv_iter_get_data(iter);
/* Assume channel status is 2 bytes long */
memcpy(status, data, 2);
return true;
}
/* Defined in TS 102.223 Section 8.57 */
static bool parse_dataobj_card_reader_id(struct comprehension_tlv_iter *iter,
void *user)
{
struct stk_card_reader_id *cr_id = user;
const uint8_t *data;
unsigned int len = comprehension_tlv_iter_get_length(iter);
if (len < 1)
return false;
data = comprehension_tlv_iter_get_data(iter);
cr_id->len = len;
memcpy(cr_id->id, data, len);
return true;
}
/* Defined in TS 102.223 Section 8.58 */
static bool parse_dataobj_other_address(struct comprehension_tlv_iter *iter,
void *user)
{
struct stk_other_address *oa = user;
const uint8_t *data;
uint8_t len = comprehension_tlv_iter_get_length(iter);
if (len == 0) {
oa->type = STK_ADDRESS_AUTO;
return true;
}
if ((len != 5) && (len != 17))
return false;
data = comprehension_tlv_iter_get_data(iter);
if (data[0] != STK_ADDRESS_IPV4 && data[0] != STK_ADDRESS_IPV6)
return false;
oa->type = data[0];
if (oa->type == STK_ADDRESS_IPV4)
memcpy(&oa->addr.ipv4, data + 1, 4);
else
memcpy(&oa->addr.ipv6, data + 1, 16);
return true;
}
/* Defined in TS 102.223 Section 8.59 */
static bool parse_dataobj_uicc_te_interface(struct comprehension_tlv_iter *iter,
void *user)
{
struct stk_uicc_te_interface *uti = user;
const uint8_t *data;
uint8_t len = comprehension_tlv_iter_get_length(iter);
if (len != 3)
return false;
data = comprehension_tlv_iter_get_data(iter);
uti->protocol = data[0];
uti->port = (data[1] << 8) + data[2];
return true;
}
/* Defined in TS 102.223 Section 8.60 */
static bool parse_dataobj_aid(struct comprehension_tlv_iter *iter, void *user)
{
struct stk_aid *aid = user;
const uint8_t *data;
uint8_t len = comprehension_tlv_iter_get_length(iter);
if ((len > 16) || (len < 12))
return false;
data = comprehension_tlv_iter_get_data(iter);
aid->len = len;
memcpy(aid->aid, data, len);
return true;
}
/*
* Defined in TS 102.223 Section 8.61. According to it, the technology field
* can have at most 127 bytes. However, all the defined values are only 1 byte,
* so we just use 1 byte to represent it.
*/
static bool parse_dataobj_access_technology(
struct comprehension_tlv_iter *iter, void *user)
{
uint8_t *byte = user;
return parse_dataobj_common_byte(iter, byte);
}
/* Defined in TS 102.223 Section 8.62 */
static bool parse_dataobj_display_parameters(
struct comprehension_tlv_iter *iter, void *user)
{
struct stk_display_parameters *dp = user;
const uint8_t *data;
if (comprehension_tlv_iter_get_length(iter) != 3)
return false;
data = comprehension_tlv_iter_get_data(iter);
dp->height = data[0];
dp->width = data[1];
dp->effects = data[2];
return true;
}
/* Defined in TS 102.223 Section 8.63 */
static bool parse_dataobj_service_record(struct comprehension_tlv_iter *iter,
void *user)
{
struct stk_service_record *sr = user;
const uint8_t *data;
unsigned int len;
len = comprehension_tlv_iter_get_length(iter);
if (len < 3)
return false;
data = comprehension_tlv_iter_get_data(iter);
sr->tech_id = data[0];
sr->serv_id = data[1];
sr->len = len - 2;
sr->serv_rec = l_malloc(sr->len);
memcpy(sr->serv_rec, data + 2, sr->len);
return true;
}
/* Defined in TS 102.223 Section 8.64 */
static bool parse_dataobj_device_filter(struct comprehension_tlv_iter *iter,
void *user)
{
struct stk_device_filter *df = user;
const uint8_t *data;
unsigned int len = comprehension_tlv_iter_get_length(iter);
if (len < 2)
return false;
data = comprehension_tlv_iter_get_data(iter);
/* According to TS 102.223, everything except BT & IRDA is RFU */
if (data[0] != STK_TECHNOLOGY_BLUETOOTH &&
data[0] != STK_TECHNOLOGY_IRDA)
return false;
df->tech_id = data[0];
df->len = len - 1;
df->dev_filter = l_malloc(df->len);
memcpy(df->dev_filter, data + 1, df->len);
return true;
}
/* Defined in TS 102.223 Section 8.65 */
static bool parse_dataobj_service_search(
struct comprehension_tlv_iter *iter, void *user)
{
struct stk_service_search *ss = user;
const uint8_t *data;
unsigned int len = comprehension_tlv_iter_get_length(iter);
if (len < 2)
return false;
data = comprehension_tlv_iter_get_data(iter);
/* According to TS 102.223, everything except BT & IRDA is RFU */
if (data[0] != STK_TECHNOLOGY_BLUETOOTH &&
data[0] != STK_TECHNOLOGY_IRDA)
return false;
ss->tech_id = data[0];
ss->len = len - 1;
ss->ser_search = l_malloc(ss->len);
memcpy(ss->ser_search, data + 1, ss->len);
return true;
}
/* Defined in TS 102.223 Section 8.66 */
static bool parse_dataobj_attribute_info(struct comprehension_tlv_iter *iter,
void *user)
{
struct stk_attribute_info *ai = user;
const uint8_t *data;
unsigned int len = comprehension_tlv_iter_get_length(iter);
if (len < 2)
return false;
data = comprehension_tlv_iter_get_data(iter);
/* According to TS 102.223, everything except BT & IRDA is RFU */
if (data[0] != STK_TECHNOLOGY_BLUETOOTH &&
data[0] != STK_TECHNOLOGY_IRDA)
return false;
ai->tech_id = data[0];
ai->len = len - 1;
ai->attr_info = l_malloc(ai->len);
memcpy(ai->attr_info, data + 1, ai->len);
return true;
}
/* Defined in TS 102.223 Section 8.67 */
static bool parse_dataobj_service_availability(
struct comprehension_tlv_iter *iter, void *user)
{
struct stk_common_byte_array *array = user;
return parse_dataobj_common_byte_array(iter, array);
}
/* Defined in TS 102.223 Section 8.68 */
static bool parse_dataobj_remote_entity_address(
struct comprehension_tlv_iter *iter, void *user)
{
struct stk_remote_entity_address *rea = user;
const uint8_t *data;
unsigned int len = comprehension_tlv_iter_get_length(iter);
data = comprehension_tlv_iter_get_data(iter);
switch (data[0]) {
case 0x00:
if (len != 7)
return false;
break;
case 0x01:
if (len != 5)
return false;
break;
default:
return false;
}
rea->has_address = true;
rea->coding_type = data[0];
memcpy(&rea->addr, data + 1, len - 1);
return true;
}
/* Defined in TS 102.223 Section 8.69 */
static bool parse_dataobj_esn(struct comprehension_tlv_iter *iter, void *user)
{
uint8_t *esn = user;
const uint8_t *data;
unsigned int len = comprehension_tlv_iter_get_length(iter);
if (len != 4)
return false;
data = comprehension_tlv_iter_get_data(iter);
/* Assume esn is 4 bytes long */
memcpy(esn, data, len);
return true;
}
/* Defined in TS 102.223 Section 8.70 */
static bool parse_dataobj_network_access_name(
struct comprehension_tlv_iter *iter,
void *user)
{
char **apn = user;
const uint8_t *data;
unsigned int len = comprehension_tlv_iter_get_length(iter);
uint8_t label_size;
uint8_t offset = 0;
char decoded_apn[100];
if (len == 0 || len > 100)
return false;
data = comprehension_tlv_iter_get_data(iter);
/*
* As specified in TS 23 003 Section 9
* The APN consists of one or more labels. Each label is coded as
* a one octet length field followed by that number of octets coded
* as 8 bit ASCII characters
*/
while (len) {
label_size = *data;
if (label_size == 0 || label_size > (len - 1))
return false;
memcpy(decoded_apn + offset, data + 1, label_size);
data += label_size + 1;
offset += label_size;
len -= label_size + 1;
if (len)
decoded_apn[offset++] = '.';
}
decoded_apn[offset] = '\0';
*apn = l_strdup(decoded_apn);
return true;
}
/* Defined in TS 102.223 Section 8.71 */
static bool parse_dataobj_cdma_sms_tpdu(struct comprehension_tlv_iter *iter,
void *user)
{
struct stk_common_byte_array *array = user;
return parse_dataobj_common_byte_array(iter, array);
}
/* Defined in TS 102.223 Section 8.72 */
static bool parse_dataobj_text_attr(struct comprehension_tlv_iter *iter,
void *user)
{
struct stk_text_attribute *attr = user;
const uint8_t *data;
unsigned int len;
len = comprehension_tlv_iter_get_length(iter);
if (len > sizeof(attr->attributes))
return false;
data = comprehension_tlv_iter_get_data(iter);
memcpy(attr->attributes, data, len);
attr->len = len;
return true;
}
/* Defined in TS 31.111 Section 8.72 */
static bool parse_dataobj_pdp_act_par(
struct comprehension_tlv_iter *iter, void *user)
{
struct stk_pdp_act_par *pcap = user;
const uint8_t *data;
unsigned int len;
len = comprehension_tlv_iter_get_length(iter);
if (len > sizeof(pcap->par))
return false;
data = comprehension_tlv_iter_get_data(iter);
memcpy(pcap->par, data, len);
pcap->len = len;
return true;
}
/* Defined in TS 102.223 Section 8.73 */
static bool parse_dataobj_item_text_attribute_list(
struct comprehension_tlv_iter *iter, void *user)
{
struct stk_item_text_attribute_list *ital = user;
const uint8_t *data;
unsigned int len = comprehension_tlv_iter_get_length(iter);
if ((len > sizeof(ital->list)) || (len % 4 != 0))
return false;
data = comprehension_tlv_iter_get_data(iter);
memcpy(ital->list, data, len);
ital->len = len;
return true;
}
/* Defined in TS 31.111 Section 8.73 */
static bool parse_dataobj_utran_meas_qualifier(
struct comprehension_tlv_iter *iter, void *user)
{
uint8_t *byte = user;
return parse_dataobj_common_byte(iter, byte);
}
/*
* Defined in TS 102.223 Section 8.74.
*
* According to 3GPP TS 24.008, Section 10.5.1.4, IMEISV is composed of
* 16 digits and totally 9 bytes are used to represent it.
*
* Bits 1-3 of first byte represent the type of identity, and they
* are 0 1 1 separately for IMEISV. Bit 4 of first byte is the odd/even
* indication, and it's 0 to indicate IMEISV has odd number of digits (16).
* The rest bytes are coded using BCD coding.
*
* For example, if the IMEISV is "1234567890123456", then it's coded as
* "13 32 54 76 98 10 32 54 F6".
*/
static bool parse_dataobj_imeisv(struct comprehension_tlv_iter *iter,
void *user)
{
char *imeisv = user;
const uint8_t *data;
unsigned int len;
static const char digit_lut[] = "0123456789*#abc\0";
len = comprehension_tlv_iter_get_length(iter);
if (len != 9)
return false;
data = comprehension_tlv_iter_get_data(iter);
if ((data[0] & 0x0f) != 0x03)
return false;
if (data[8] >> 4 != 0x0f)
return false;
/* Assume imeisv is at least 17 bytes long (16 for imeisv + null) */
imeisv[0] = digit_lut[data[0] >> 4];
extract_bcd_number(data + 1, 7, imeisv + 1);
imeisv[15] = digit_lut[data[8] & 0x0f];
imeisv[16] = '\0';
return true;
}
/* Defined in TS 102.223 Section 8.75 */
static bool parse_dataobj_network_search_mode(
struct comprehension_tlv_iter *iter, void *user)
{
uint8_t *byte = user;
return parse_dataobj_common_byte(iter, byte);
}
/* Defined in TS 102.223 Section 8.76 */
static bool parse_dataobj_battery_state(struct comprehension_tlv_iter *iter,
void *user)
{
uint8_t *byte = user;
return parse_dataobj_common_byte(iter, byte);
}
/* Defined in TS 102.223 Section 8.77 */
static bool parse_dataobj_browsing_status(
struct comprehension_tlv_iter *iter, void *user)
{
struct stk_common_byte_array *array = user;
return parse_dataobj_common_byte_array(iter, array);
}
/* Defined in TS 102.223 Section 8.78 */
static bool parse_dataobj_frame_layout(struct comprehension_tlv_iter *iter,
void *user)
{
struct stk_frame_layout *fl = user;
const uint8_t *data;
uint8_t len = comprehension_tlv_iter_get_length(iter);
if (len < 2)
return false;
data = comprehension_tlv_iter_get_data(iter);
if (data[0] != STK_LAYOUT_HORIZONTAL &&
data[0] != STK_LAYOUT_VERTICAL)
return false;
fl->layout = data[0];
fl->len = len - 1;
memcpy(fl->size, data + 1, fl->len);
return true;
}
/* Defined in TS 102.223 Section 8.79 */
static bool parse_dataobj_frames_info(struct comprehension_tlv_iter *iter,
void *user)
{
struct stk_frames_info *fi = user;
const uint8_t *data;
uint8_t len = comprehension_tlv_iter_get_length(iter);
unsigned int i;
if (len < 1)
return false;
data = comprehension_tlv_iter_get_data(iter);
if (data[0] > 0x0f)
return false;
if ((len == 1 && data[0] != 0) || (len > 1 && data[0] == 0))
return false;
if (len % 2 == 0)
return false;
if (len == 1)
return true;
fi->id = data[0];
fi->len = (len - 1) / 2;
for (i = 0; i < len; i++) {
fi->list[i].height = data[i * 2 + 1] & 0x1f;
fi->list[i].width = data[i * 2 + 2] & 0x7f;
}
return true;
}
/* Defined in TS 102.223 Section 8.80 */
static bool parse_dataobj_frame_id(struct comprehension_tlv_iter *iter,
void *user)
{
struct stk_frame_id *fi = user;
const uint8_t *data;
if (comprehension_tlv_iter_get_length(iter) != 1)
return false;
data = comprehension_tlv_iter_get_data(iter);
if (data[0] >= 0x10)
return false;
fi->has_id = true;
fi->id = data[0];
return true;
}
/* Defined in TS 102.223 Section 8.81 */
static bool parse_dataobj_meid(struct comprehension_tlv_iter *iter,
void *user)
{
uint8_t *meid = user;
const uint8_t *data;
if (comprehension_tlv_iter_get_length(iter) != 8)
return false;
data = comprehension_tlv_iter_get_data(iter);
/* Assume meid is 8 bytes long */
memcpy(meid, data, 8);
return true;
}
/* Defined in TS 102.223 Section 8.82 */
static bool parse_dataobj_mms_reference(struct comprehension_tlv_iter *iter,
void *user)
{
struct stk_mms_reference *mr = user;
const uint8_t *data;
unsigned int len = comprehension_tlv_iter_get_length(iter);
if (len < 1)
return false;
data = comprehension_tlv_iter_get_data(iter);
mr->len = len;
memcpy(mr->ref, data, len);
return true;
}
/* Defined in TS 102.223 Section 8.83 */
static bool parse_dataobj_mms_id(struct comprehension_tlv_iter *iter,
void *user)
{
struct stk_mms_id *mi = user;
const uint8_t *data;
unsigned int len = comprehension_tlv_iter_get_length(iter);
if (len < 1)
return false;
data = comprehension_tlv_iter_get_data(iter);
mi->len = len;
memcpy(mi->id, data, len);
return true;
}
/* Defined in TS 102.223 Section 8.84 */
static bool parse_dataobj_mms_transfer_status(
struct comprehension_tlv_iter *iter, void *user)
{
struct stk_mms_transfer_status *mts = user;
const uint8_t *data;
unsigned int len = comprehension_tlv_iter_get_length(iter);
if (len < 1)
return false;
data = comprehension_tlv_iter_get_data(iter);
mts->len = len;
memcpy(mts->status, data, len);
return true;
}
/* Defined in TS 102.223 Section 8.85 */
static bool parse_dataobj_mms_content_id(
struct comprehension_tlv_iter *iter, void *user)
{
struct stk_mms_content_id *mci = user;
const uint8_t *data;
unsigned int len = comprehension_tlv_iter_get_length(iter);
if (len < 1)
return false;
data = comprehension_tlv_iter_get_data(iter);
mci->len = len;
memcpy(mci->id, data, len);
return true;
}
/* Defined in TS 102.223 Section 8.86 */
static bool parse_dataobj_mms_notification(
struct comprehension_tlv_iter *iter, void *user)
{
struct stk_common_byte_array *array = user;
return parse_dataobj_common_byte_array(iter, array);
}
/* Defined in TS 102.223 Section 8.87 */
static bool parse_dataobj_last_envelope(struct comprehension_tlv_iter *iter,
void *user)
{
bool *ret = user;
return parse_dataobj_common_bool(iter, ret);
}
/* Defined in TS 102.223 Section 8.88 */
static bool parse_dataobj_registry_application_data(
struct comprehension_tlv_iter *iter, void *user)
{
struct stk_registry_application_data *rad = user;
const uint8_t *data;
char *utf8;
unsigned int len = comprehension_tlv_iter_get_length(iter);
if (len < 5)
return false;
data = comprehension_tlv_iter_get_data(iter);
utf8 = decode_text(data[2], len - 4, data + 4);
if (utf8 == NULL)
return false;
rad->name = utf8;
rad->port = (data[0] << 8) + data[1];
rad->type = data[3];
return true;
}
/* Defined in TS 102.223 Section 8.89 */
static bool parse_dataobj_activate_descriptor(
struct comprehension_tlv_iter *iter, void *user)
{
uint8_t *byte = user;
const uint8_t *data;
if (comprehension_tlv_iter_get_length(iter) != 1)
return false;
data = comprehension_tlv_iter_get_data(iter);
if (data[0] != 0x01)
return false;
*byte = data[0];
return true;
}
/* Defined in TS 102.223 Section 8.90 */
static bool parse_dataobj_broadcast_network_info(
struct comprehension_tlv_iter *iter, void *user)
{
struct stk_broadcast_network_information *bni = user;
const uint8_t *data;
unsigned int len = comprehension_tlv_iter_get_length(iter);
if (len < 2)
return false;
data = comprehension_tlv_iter_get_data(iter);
if (data[0] > 0x03)
return false;
bni->tech = data[0];
bni->len = len - 1;
memcpy(bni->loc_info, data + 1, bni->len);
return true;
}
static dataobj_handler handler_for_type(enum stk_data_object_type type)
{
switch (type) {
case STK_DATA_OBJECT_TYPE_ADDRESS:
return parse_dataobj_address;
case STK_DATA_OBJECT_TYPE_ALPHA_ID:
return parse_dataobj_alpha_id;
case STK_DATA_OBJECT_TYPE_SUBADDRESS:
return parse_dataobj_subaddress;
case STK_DATA_OBJECT_TYPE_CCP:
return parse_dataobj_ccp;
case STK_DATA_OBJECT_TYPE_CBS_PAGE:
return parse_dataobj_cbs_page;
case STK_DATA_OBJECT_TYPE_DURATION:
return parse_dataobj_duration;
case STK_DATA_OBJECT_TYPE_ITEM:
return parse_dataobj_item;
case STK_DATA_OBJECT_TYPE_ITEM_ID:
return parse_dataobj_item_id;
case STK_DATA_OBJECT_TYPE_RESPONSE_LENGTH:
return parse_dataobj_response_len;
case STK_DATA_OBJECT_TYPE_RESULT:
return parse_dataobj_result;
case STK_DATA_OBJECT_TYPE_GSM_SMS_TPDU:
return parse_dataobj_gsm_sms_tpdu;
case STK_DATA_OBJECT_TYPE_SS_STRING:
return parse_dataobj_ss;
case STK_DATA_OBJECT_TYPE_TEXT:
return parse_dataobj_text;
case STK_DATA_OBJECT_TYPE_TONE:
return parse_dataobj_tone;
case STK_DATA_OBJECT_TYPE_USSD_STRING:
return parse_dataobj_ussd;
case STK_DATA_OBJECT_TYPE_FILE_LIST:
return parse_dataobj_file_list;
case STK_DATA_OBJECT_TYPE_LOCATION_INFO:
return parse_dataobj_location_info;
case STK_DATA_OBJECT_TYPE_IMEI:
return parse_dataobj_imei;
case STK_DATA_OBJECT_TYPE_HELP_REQUEST:
return parse_dataobj_help_request;
case STK_DATA_OBJECT_TYPE_NETWORK_MEASUREMENT_RESULTS:
return parse_dataobj_network_measurement_results;
case STK_DATA_OBJECT_TYPE_DEFAULT_TEXT:
return parse_dataobj_default_text;
case STK_DATA_OBJECT_TYPE_ITEMS_NEXT_ACTION_INDICATOR:
return parse_dataobj_items_next_action_indicator;
case STK_DATA_OBJECT_TYPE_EVENT_LIST:
return parse_dataobj_event_list;
case STK_DATA_OBJECT_TYPE_CAUSE:
return parse_dataobj_cause;
case STK_DATA_OBJECT_TYPE_LOCATION_STATUS:
return parse_dataobj_location_status;
case STK_DATA_OBJECT_TYPE_TRANSACTION_ID:
return parse_dataobj_transaction_id;
case STK_DATA_OBJECT_TYPE_BCCH_CHANNEL_LIST:
return parse_dataobj_bcch_channel_list;
case STK_DATA_OBJECT_TYPE_CALL_CONTROL_REQUESTED_ACTION:
return parse_dataobj_call_control_requested_action;
case STK_DATA_OBJECT_TYPE_ICON_ID:
return parse_dataobj_icon_id;
case STK_DATA_OBJECT_TYPE_ITEM_ICON_ID_LIST:
return parse_dataobj_item_icon_id_list;
case STK_DATA_OBJECT_TYPE_CARD_READER_STATUS:
return parse_dataobj_card_reader_status;
case STK_DATA_OBJECT_TYPE_CARD_ATR:
return parse_dataobj_card_atr;
case STK_DATA_OBJECT_TYPE_C_APDU:
return parse_dataobj_c_apdu;
case STK_DATA_OBJECT_TYPE_R_APDU:
return parse_dataobj_r_apdu;
case STK_DATA_OBJECT_TYPE_TIMER_ID:
return parse_dataobj_timer_id;
case STK_DATA_OBJECT_TYPE_TIMER_VALUE:
return parse_dataobj_timer_value;
case STK_DATA_OBJECT_TYPE_DATETIME_TIMEZONE:
return parse_dataobj_datetime_timezone;
case STK_DATA_OBJECT_TYPE_AT_COMMAND:
return parse_dataobj_at_command;
case STK_DATA_OBJECT_TYPE_AT_RESPONSE:
return parse_dataobj_at_response;
case STK_DATA_OBJECT_TYPE_BC_REPEAT_INDICATOR:
return parse_dataobj_bc_repeat_indicator;
case STK_DATA_OBJECT_TYPE_IMMEDIATE_RESPONSE:
return parse_dataobj_imm_resp;
case STK_DATA_OBJECT_TYPE_DTMF_STRING:
return parse_dataobj_dtmf_string;
case STK_DATA_OBJECT_TYPE_LANGUAGE:
return parse_dataobj_language;
case STK_DATA_OBJECT_TYPE_BROWSER_ID:
return parse_dataobj_browser_id;
case STK_DATA_OBJECT_TYPE_TIMING_ADVANCE:
return parse_dataobj_timing_advance;
case STK_DATA_OBJECT_TYPE_URL:
return parse_dataobj_url;
case STK_DATA_OBJECT_TYPE_BEARER:
return parse_dataobj_bearer;
case STK_DATA_OBJECT_TYPE_PROVISIONING_FILE_REF:
return parse_dataobj_provisioning_file_reference;
case STK_DATA_OBJECT_TYPE_BROWSER_TERMINATION_CAUSE:
return parse_dataobj_browser_termination_cause;
case STK_DATA_OBJECT_TYPE_BEARER_DESCRIPTION:
return parse_dataobj_bearer_description;
case STK_DATA_OBJECT_TYPE_CHANNEL_DATA:
return parse_dataobj_channel_data;
case STK_DATA_OBJECT_TYPE_CHANNEL_DATA_LENGTH:
return parse_dataobj_channel_data_length;
case STK_DATA_OBJECT_TYPE_BUFFER_SIZE:
return parse_dataobj_buffer_size;
case STK_DATA_OBJECT_TYPE_CHANNEL_STATUS:
return parse_dataobj_channel_status;
case STK_DATA_OBJECT_TYPE_CARD_READER_ID:
return parse_dataobj_card_reader_id;
case STK_DATA_OBJECT_TYPE_OTHER_ADDRESS:
return parse_dataobj_other_address;
case STK_DATA_OBJECT_TYPE_UICC_TE_INTERFACE:
return parse_dataobj_uicc_te_interface;
case STK_DATA_OBJECT_TYPE_AID:
return parse_dataobj_aid;
case STK_DATA_OBJECT_TYPE_ACCESS_TECHNOLOGY:
return parse_dataobj_access_technology;
case STK_DATA_OBJECT_TYPE_DISPLAY_PARAMETERS:
return parse_dataobj_display_parameters;
case STK_DATA_OBJECT_TYPE_SERVICE_RECORD:
return parse_dataobj_service_record;
case STK_DATA_OBJECT_TYPE_DEVICE_FILTER:
return parse_dataobj_device_filter;
case STK_DATA_OBJECT_TYPE_SERVICE_SEARCH:
return parse_dataobj_service_search;
case STK_DATA_OBJECT_TYPE_ATTRIBUTE_INFO:
return parse_dataobj_attribute_info;
case STK_DATA_OBJECT_TYPE_SERVICE_AVAILABILITY:
return parse_dataobj_service_availability;
case STK_DATA_OBJECT_TYPE_REMOTE_ENTITY_ADDRESS:
return parse_dataobj_remote_entity_address;
case STK_DATA_OBJECT_TYPE_ESN:
return parse_dataobj_esn;
case STK_DATA_OBJECT_TYPE_NETWORK_ACCESS_NAME:
return parse_dataobj_network_access_name;
case STK_DATA_OBJECT_TYPE_CDMA_SMS_TPDU:
return parse_dataobj_cdma_sms_tpdu;
case STK_DATA_OBJECT_TYPE_TEXT_ATTRIBUTE:
return parse_dataobj_text_attr;
case STK_DATA_OBJECT_TYPE_PDP_ACTIVATION_PARAMETER:
return parse_dataobj_pdp_act_par;
case STK_DATA_OBJECT_TYPE_ITEM_TEXT_ATTRIBUTE_LIST:
return parse_dataobj_item_text_attribute_list;
case STK_DATA_OBJECT_TYPE_UTRAN_MEASUREMENT_QUALIFIER:
return parse_dataobj_utran_meas_qualifier;
case STK_DATA_OBJECT_TYPE_IMEISV:
return parse_dataobj_imeisv;
case STK_DATA_OBJECT_TYPE_NETWORK_SEARCH_MODE:
return parse_dataobj_network_search_mode;
case STK_DATA_OBJECT_TYPE_BATTERY_STATE:
return parse_dataobj_battery_state;
case STK_DATA_OBJECT_TYPE_BROWSING_STATUS:
return parse_dataobj_browsing_status;
case STK_DATA_OBJECT_TYPE_FRAME_LAYOUT:
return parse_dataobj_frame_layout;
case STK_DATA_OBJECT_TYPE_FRAMES_INFO:
return parse_dataobj_frames_info;
case STK_DATA_OBJECT_TYPE_FRAME_ID:
return parse_dataobj_frame_id;
case STK_DATA_OBJECT_TYPE_MEID:
return parse_dataobj_meid;
case STK_DATA_OBJECT_TYPE_MMS_REFERENCE:
return parse_dataobj_mms_reference;
case STK_DATA_OBJECT_TYPE_MMS_ID:
return parse_dataobj_mms_id;
case STK_DATA_OBJECT_TYPE_MMS_TRANSFER_STATUS:
return parse_dataobj_mms_transfer_status;
case STK_DATA_OBJECT_TYPE_MMS_CONTENT_ID:
return parse_dataobj_mms_content_id;
case STK_DATA_OBJECT_TYPE_MMS_NOTIFICATION:
return parse_dataobj_mms_notification;
case STK_DATA_OBJECT_TYPE_LAST_ENVELOPE:
return parse_dataobj_last_envelope;
case STK_DATA_OBJECT_TYPE_REGISTRY_APPLICATION_DATA:
return parse_dataobj_registry_application_data;
case STK_DATA_OBJECT_TYPE_ACTIVATE_DESCRIPTOR:
return parse_dataobj_activate_descriptor;
case STK_DATA_OBJECT_TYPE_BROADCAST_NETWORK_INFO:
return parse_dataobj_broadcast_network_info;
default:
return NULL;
}
}
static void destroy_stk_item(gpointer pointer)
{
struct stk_item *item = pointer;
l_free(item->text);
l_free(item);
}
static bool parse_item_list(struct comprehension_tlv_iter *iter, void *data)
{
struct l_queue **out = data;
uint16_t tag = STK_DATA_OBJECT_TYPE_ITEM;
struct comprehension_tlv_iter iter_old;
struct stk_item item;
struct l_queue *list = l_queue_new();
unsigned int count = 0;
bool has_empty = false;
do {
comprehension_tlv_iter_copy(iter, &iter_old);
memset(&item, 0, sizeof(item));
count++;
if (!parse_dataobj_item(iter, &item))
continue;
if (item.id == 0) {
has_empty = true;
continue;
}
l_queue_push_tail(list, l_memdup(&item, sizeof(item)));
} while (comprehension_tlv_iter_next(iter) == TRUE &&
comprehension_tlv_iter_get_tag(iter) == tag);
comprehension_tlv_iter_copy(&iter_old, iter);
if (!has_empty || count == 1) {
*out = list;
return true;
}
l_queue_destroy(list, destroy_stk_item);
return false;
}
static bool parse_provisioning_list(struct comprehension_tlv_iter *iter,
void *data)
{
struct l_queue **out = data;
uint16_t tag = STK_DATA_OBJECT_TYPE_PROVISIONING_FILE_REF;
struct comprehension_tlv_iter iter_old;
struct stk_file file;
struct l_queue *list = l_queue_new();
do {
comprehension_tlv_iter_copy(iter, &iter_old);
memset(&file, 0, sizeof(file));
if (!parse_dataobj_provisioning_file_reference(iter, &file))
continue;
l_queue_push_tail(list, l_memdup(&file, sizeof(file)));
} while (comprehension_tlv_iter_next(iter) == TRUE &&
comprehension_tlv_iter_get_tag(iter) == tag);
comprehension_tlv_iter_copy(&iter_old, iter);
*out = list;
return true;
}
static dataobj_handler list_handler_for_type(enum stk_data_object_type type)
{
switch (type) {
case STK_DATA_OBJECT_TYPE_ITEM:
return parse_item_list;
case STK_DATA_OBJECT_TYPE_PROVISIONING_FILE_REF:
return parse_provisioning_list;
default:
return NULL;
}
}
struct dataobj_handler_entry {
enum stk_data_object_type type;
int flags;
void *data;
};
static enum stk_command_parse_result parse_dataobj(
struct comprehension_tlv_iter *iter,
enum stk_data_object_type type, ...)
{
GSList *entries = NULL;
GSList *l;
va_list args;
bool minimum_set = true;
bool parse_error = false;
va_start(args, type);
while (type != STK_DATA_OBJECT_TYPE_INVALID) {
struct dataobj_handler_entry *entry;
entry = g_new0(struct dataobj_handler_entry, 1);
entry->type = type;
entry->flags = va_arg(args, int);
entry->data = va_arg(args, void *);
type = va_arg(args, enum stk_data_object_type);
entries = g_slist_prepend(entries, entry);
}
va_end(args);
entries = g_slist_reverse(entries);
l = entries;
while (comprehension_tlv_iter_next(iter) == TRUE) {
dataobj_handler handler;
struct dataobj_handler_entry *entry;
GSList *l2;
for (l2 = l; l2; l2 = l2->next) {
entry = l2->data;
if (comprehension_tlv_iter_get_tag(iter) == entry->type)
break;
/* Can't skip over mandatory objects */
if (entry->flags & DATAOBJ_FLAG_MANDATORY) {
l2 = NULL;
break;
}
}
if (l2 == NULL) {
if (comprehension_tlv_get_cr(iter) == TRUE)
parse_error = true;
continue;
}
if (entry->flags & DATAOBJ_FLAG_LIST)
handler = list_handler_for_type(entry->type);
else
handler = handler_for_type(entry->type);
if (!handler(iter, entry->data))
parse_error = true;
l = l2->next;
}
for (; l; l = l->next) {
struct dataobj_handler_entry *entry = l->data;
if (entry->flags & DATAOBJ_FLAG_MANDATORY)
minimum_set = false;
}
g_slist_free_full(entries, g_free);
if (!minimum_set)
return STK_PARSE_RESULT_MISSING_VALUE;
if (parse_error)
return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
return STK_PARSE_RESULT_OK;
}
static void destroy_display_text(struct stk_command *command)
{
l_free(command->display_text.text);
}
static enum stk_command_parse_result parse_display_text(
struct stk_command *command,
struct comprehension_tlv_iter *iter)
{
struct stk_command_display_text *obj = &command->display_text;
enum stk_command_parse_result status;
if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC)
return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
if (command->dst != STK_DEVICE_IDENTITY_TYPE_DISPLAY)
return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
command->destructor = destroy_display_text;
status = parse_dataobj(iter, STK_DATA_OBJECT_TYPE_TEXT,
DATAOBJ_FLAG_MANDATORY | DATAOBJ_FLAG_MINIMUM,
&obj->text,
STK_DATA_OBJECT_TYPE_ICON_ID, 0,
&obj->icon_id,
STK_DATA_OBJECT_TYPE_IMMEDIATE_RESPONSE, 0,
&obj->immediate_response,
STK_DATA_OBJECT_TYPE_DURATION, 0,
&obj->duration,
STK_DATA_OBJECT_TYPE_TEXT_ATTRIBUTE, 0,
&obj->text_attr,
STK_DATA_OBJECT_TYPE_FRAME_ID, 0,
&obj->frame_id,
STK_DATA_OBJECT_TYPE_INVALID);
CHECK_TEXT_AND_ICON(obj->text, obj->icon_id.id);
return status;
}
static void destroy_get_inkey(struct stk_command *command)
{
l_free(command->get_inkey.text);
}
static enum stk_command_parse_result parse_get_inkey(
struct stk_command *command,
struct comprehension_tlv_iter *iter)
{
struct stk_command_get_inkey *obj = &command->get_inkey;
enum stk_command_parse_result status;
if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC)
return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
if (command->dst != STK_DEVICE_IDENTITY_TYPE_TERMINAL)
return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
command->destructor = destroy_get_inkey;
status = parse_dataobj(iter, STK_DATA_OBJECT_TYPE_TEXT,
DATAOBJ_FLAG_MANDATORY | DATAOBJ_FLAG_MINIMUM,
&obj->text,
STK_DATA_OBJECT_TYPE_ICON_ID, 0,
&obj->icon_id,
STK_DATA_OBJECT_TYPE_DURATION, 0,
&obj->duration,
STK_DATA_OBJECT_TYPE_TEXT_ATTRIBUTE, 0,
&obj->text_attr,
STK_DATA_OBJECT_TYPE_FRAME_ID, 0,
&obj->frame_id,
STK_DATA_OBJECT_TYPE_INVALID);
CHECK_TEXT_AND_ICON(obj->text, obj->icon_id.id);
return status;
}
static void destroy_get_input(struct stk_command *command)
{
l_free(command->get_input.text);
l_free(command->get_input.default_text);
}
static enum stk_command_parse_result parse_get_input(
struct stk_command *command,
struct comprehension_tlv_iter *iter)
{
struct stk_command_get_input *obj = &command->get_input;
enum stk_command_parse_result status;
if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC)
return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
if (command->dst != STK_DEVICE_IDENTITY_TYPE_TERMINAL)
return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
command->destructor = destroy_get_input;
status = parse_dataobj(iter, STK_DATA_OBJECT_TYPE_TEXT,
DATAOBJ_FLAG_MANDATORY | DATAOBJ_FLAG_MINIMUM,
&obj->text,
STK_DATA_OBJECT_TYPE_RESPONSE_LENGTH,
DATAOBJ_FLAG_MANDATORY | DATAOBJ_FLAG_MINIMUM,
&obj->resp_len,
STK_DATA_OBJECT_TYPE_DEFAULT_TEXT, 0,
&obj->default_text,
STK_DATA_OBJECT_TYPE_ICON_ID, 0,
&obj->icon_id,
STK_DATA_OBJECT_TYPE_TEXT_ATTRIBUTE, 0,
&obj->text_attr,
STK_DATA_OBJECT_TYPE_FRAME_ID, 0,
&obj->frame_id,
STK_DATA_OBJECT_TYPE_INVALID);
CHECK_TEXT_AND_ICON(obj->text, obj->icon_id.id);
return status;
}
static enum stk_command_parse_result parse_more_time(
struct stk_command *command,
struct comprehension_tlv_iter *iter)
{
if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC)
return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
if (command->dst != STK_DEVICE_IDENTITY_TYPE_TERMINAL)
return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
return STK_PARSE_RESULT_OK;
}
static void destroy_play_tone(struct stk_command *command)
{
l_free(command->play_tone.alpha_id);
}
static enum stk_command_parse_result parse_play_tone(
struct stk_command *command,
struct comprehension_tlv_iter *iter)
{
struct stk_command_play_tone *obj = &command->play_tone;
enum stk_command_parse_result status;
if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC)
return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
if (command->dst != STK_DEVICE_IDENTITY_TYPE_EARPIECE)
return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
command->destructor = destroy_play_tone;
status = parse_dataobj(iter, STK_DATA_OBJECT_TYPE_ALPHA_ID, 0,
&obj->alpha_id,
STK_DATA_OBJECT_TYPE_TONE, 0,
&obj->tone,
STK_DATA_OBJECT_TYPE_DURATION, 0,
&obj->duration,
STK_DATA_OBJECT_TYPE_ICON_ID, 0,
&obj->icon_id,
STK_DATA_OBJECT_TYPE_TEXT_ATTRIBUTE, 0,
&obj->text_attr,
STK_DATA_OBJECT_TYPE_FRAME_ID, 0,
&obj->frame_id,
STK_DATA_OBJECT_TYPE_INVALID);
CHECK_TEXT_AND_ICON(obj->alpha_id, obj->icon_id.id);
return status;
}
static enum stk_command_parse_result parse_poll_interval(
struct stk_command *command,
struct comprehension_tlv_iter *iter)
{
struct stk_command_poll_interval *obj = &command->poll_interval;
if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC)
return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
if (command->dst != STK_DEVICE_IDENTITY_TYPE_TERMINAL)
return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
return parse_dataobj(iter, STK_DATA_OBJECT_TYPE_DURATION,
DATAOBJ_FLAG_MANDATORY | DATAOBJ_FLAG_MINIMUM,
&obj->duration,
STK_DATA_OBJECT_TYPE_INVALID);
}
static void destroy_setup_menu(struct stk_command *command)
{
l_free(command->setup_menu.alpha_id);
l_queue_destroy(command->setup_menu.items, destroy_stk_item);
}
static enum stk_command_parse_result parse_setup_menu(
struct stk_command *command,
struct comprehension_tlv_iter *iter)
{
struct stk_command_setup_menu *obj = &command->setup_menu;
enum stk_command_parse_result status;
if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC)
return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
if (command->dst != STK_DEVICE_IDENTITY_TYPE_TERMINAL)
return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
command->destructor = destroy_setup_menu;
status = parse_dataobj(iter,
STK_DATA_OBJECT_TYPE_ALPHA_ID,
DATAOBJ_FLAG_MANDATORY | DATAOBJ_FLAG_MINIMUM,
&obj->alpha_id,
STK_DATA_OBJECT_TYPE_ITEM,
DATAOBJ_FLAG_MANDATORY | DATAOBJ_FLAG_MINIMUM |
DATAOBJ_FLAG_LIST, &obj->items,
STK_DATA_OBJECT_TYPE_ITEMS_NEXT_ACTION_INDICATOR, 0,
&obj->next_act,
STK_DATA_OBJECT_TYPE_ICON_ID, 0,
&obj->icon_id,
STK_DATA_OBJECT_TYPE_ITEM_ICON_ID_LIST, 0,
&obj->item_icon_id_list,
STK_DATA_OBJECT_TYPE_TEXT_ATTRIBUTE, 0,
&obj->text_attr,
STK_DATA_OBJECT_TYPE_ITEM_TEXT_ATTRIBUTE_LIST, 0,
&obj->item_text_attr_list,
STK_DATA_OBJECT_TYPE_INVALID);
CHECK_TEXT_AND_ICON(obj->alpha_id, obj->icon_id.id);
return status;
}
static void destroy_select_item(struct stk_command *command)
{
l_free(command->select_item.alpha_id);
l_queue_destroy(command->select_item.items, destroy_stk_item);
}
static enum stk_command_parse_result parse_select_item(
struct stk_command *command,
struct comprehension_tlv_iter *iter)
{
struct stk_command_select_item *obj = &command->select_item;
enum stk_command_parse_result status;
if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC)
return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
if (command->dst != STK_DEVICE_IDENTITY_TYPE_TERMINAL)
return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
status = parse_dataobj(iter,
STK_DATA_OBJECT_TYPE_ALPHA_ID, 0,
&obj->alpha_id,
STK_DATA_OBJECT_TYPE_ITEM,
DATAOBJ_FLAG_MANDATORY | DATAOBJ_FLAG_MINIMUM |
DATAOBJ_FLAG_LIST, &obj->items,
STK_DATA_OBJECT_TYPE_ITEMS_NEXT_ACTION_INDICATOR, 0,
&obj->next_act,
STK_DATA_OBJECT_TYPE_ITEM_ID, 0,
&obj->item_id,
STK_DATA_OBJECT_TYPE_ICON_ID, 0,
&obj->icon_id,
STK_DATA_OBJECT_TYPE_ITEM_ICON_ID_LIST, 0,
&obj->item_icon_id_list,
STK_DATA_OBJECT_TYPE_TEXT_ATTRIBUTE, 0,
&obj->text_attr,
STK_DATA_OBJECT_TYPE_ITEM_TEXT_ATTRIBUTE_LIST, 0,
&obj->item_text_attr_list,
STK_DATA_OBJECT_TYPE_FRAME_ID, 0,
&obj->frame_id,
STK_DATA_OBJECT_TYPE_INVALID);
command->destructor = destroy_select_item;
if (status == STK_PARSE_RESULT_OK && l_queue_isempty(obj->items))
status = STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
CHECK_TEXT_AND_ICON(obj->alpha_id, obj->icon_id.id);
return status;
}
static void destroy_send_sms(struct stk_command *command)
{
l_free(command->send_sms.alpha_id);
l_free(command->send_sms.cdma_sms.array);
}
static enum stk_command_parse_result parse_send_sms(
struct stk_command *command,
struct comprehension_tlv_iter *iter)
{
struct stk_command_send_sms *obj = &command->send_sms;
enum stk_command_parse_result status;
struct gsm_sms_tpdu gsm_tpdu;
struct stk_address sc_address = { 0, NULL };
if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC)
return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
if (command->dst != STK_DEVICE_IDENTITY_TYPE_NETWORK)
return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
memset(&gsm_tpdu, 0, sizeof(gsm_tpdu));
status = parse_dataobj(iter, STK_DATA_OBJECT_TYPE_ALPHA_ID, 0,
&obj->alpha_id,
STK_DATA_OBJECT_TYPE_ADDRESS, 0,
&sc_address,
STK_DATA_OBJECT_TYPE_GSM_SMS_TPDU, 0,
&gsm_tpdu,
STK_DATA_OBJECT_TYPE_CDMA_SMS_TPDU, 0,
&obj->cdma_sms,
STK_DATA_OBJECT_TYPE_ICON_ID, 0,
&obj->icon_id,
STK_DATA_OBJECT_TYPE_TEXT_ATTRIBUTE, 0,
&obj->text_attr,
STK_DATA_OBJECT_TYPE_FRAME_ID, 0,
&obj->frame_id,
STK_DATA_OBJECT_TYPE_INVALID);
command->destructor = destroy_send_sms;
if (status != STK_PARSE_RESULT_OK)
goto out;
CHECK_TEXT_AND_ICON(obj->alpha_id, obj->icon_id.id);
if (status != STK_PARSE_RESULT_OK)
goto out;
if (gsm_tpdu.len == 0 && obj->cdma_sms.len == 0) {
status = STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
goto out;
}
if (gsm_tpdu.len > 0 && obj->cdma_sms.len > 0) {
status = STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
goto out;
}
/* We don't process CDMA pdus for now */
if (obj->cdma_sms.len > 0)
goto out;
/* packing is needed */
if (command->qualifier & 0x01) {
if (!sms_decode_unpacked_stk_pdu(gsm_tpdu.tpdu, gsm_tpdu.len,
&obj->gsm_sms)) {
status = STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
goto out;
}
goto set_addr;
}
if (sms_decode(gsm_tpdu.tpdu, gsm_tpdu.len, TRUE,
gsm_tpdu.len, &obj->gsm_sms) == FALSE) {
status = STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
goto out;
}
if (obj->gsm_sms.type != SMS_TYPE_SUBMIT &&
obj->gsm_sms.type != SMS_TYPE_COMMAND) {
status = STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
goto out;
}
set_addr:
if (sc_address.number == NULL)
goto out;
if (strlen(sc_address.number) > 20) {
status = STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
goto out;
}
strcpy(obj->gsm_sms.sc_addr.address, sc_address.number);
obj->gsm_sms.sc_addr.numbering_plan = sc_address.ton_npi & 15;
obj->gsm_sms.sc_addr.number_type = (sc_address.ton_npi >> 4) & 7;
out:
l_free(sc_address.number);
return status;
}
static void destroy_send_ss(struct stk_command *command)
{
l_free(command->send_ss.alpha_id);
l_free(command->send_ss.ss.ss);
}
static enum stk_command_parse_result parse_send_ss(struct stk_command *command,
struct comprehension_tlv_iter *iter)
{
struct stk_command_send_ss *obj = &command->send_ss;
if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC)
return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
if (command->dst != STK_DEVICE_IDENTITY_TYPE_NETWORK)
return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
command->destructor = destroy_send_ss;
return parse_dataobj(iter, STK_DATA_OBJECT_TYPE_ALPHA_ID, 0,
&obj->alpha_id,
STK_DATA_OBJECT_TYPE_SS_STRING,
DATAOBJ_FLAG_MANDATORY | DATAOBJ_FLAG_MINIMUM,
&obj->ss,
STK_DATA_OBJECT_TYPE_ICON_ID, 0,
&obj->icon_id,
STK_DATA_OBJECT_TYPE_TEXT_ATTRIBUTE, 0,
&obj->text_attr,
STK_DATA_OBJECT_TYPE_FRAME_ID, 0,
&obj->frame_id,
STK_DATA_OBJECT_TYPE_INVALID);
}
static void destroy_send_ussd(struct stk_command *command)
{
l_free(command->send_ussd.alpha_id);
}
static enum stk_command_parse_result parse_send_ussd(
struct stk_command *command,
struct comprehension_tlv_iter *iter)
{
struct stk_command_send_ussd *obj = &command->send_ussd;
if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC)
return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
if (command->dst != STK_DEVICE_IDENTITY_TYPE_NETWORK)
return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
command->destructor = destroy_send_ussd;
return parse_dataobj(iter, STK_DATA_OBJECT_TYPE_ALPHA_ID, 0,
&obj->alpha_id,
STK_DATA_OBJECT_TYPE_USSD_STRING,
DATAOBJ_FLAG_MANDATORY | DATAOBJ_FLAG_MINIMUM,
&obj->ussd_string,
STK_DATA_OBJECT_TYPE_ICON_ID, 0,
&obj->icon_id,
STK_DATA_OBJECT_TYPE_TEXT_ATTRIBUTE, 0,
&obj->text_attr,
STK_DATA_OBJECT_TYPE_FRAME_ID, 0,
&obj->frame_id,
STK_DATA_OBJECT_TYPE_INVALID);
}
static void destroy_setup_call(struct stk_command *command)
{
l_free(command->setup_call.alpha_id_usr_cfm);
l_free(command->setup_call.addr.number);
l_free(command->setup_call.alpha_id_call_setup);
}
static enum stk_command_parse_result parse_setup_call(
struct stk_command *command,
struct comprehension_tlv_iter *iter)
{
struct stk_command_setup_call *obj = &command->setup_call;
enum stk_command_parse_result status;
if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC)
return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
if (command->dst != STK_DEVICE_IDENTITY_TYPE_NETWORK)
return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
command->destructor = destroy_setup_call;
status = parse_dataobj(iter, STK_DATA_OBJECT_TYPE_ALPHA_ID, 0,
&obj->alpha_id_usr_cfm,
STK_DATA_OBJECT_TYPE_ADDRESS,
DATAOBJ_FLAG_MANDATORY | DATAOBJ_FLAG_MINIMUM,
&obj->addr,
STK_DATA_OBJECT_TYPE_CCP, 0,
&obj->ccp,
STK_DATA_OBJECT_TYPE_SUBADDRESS, 0,
&obj->subaddr,
STK_DATA_OBJECT_TYPE_DURATION, 0,
&obj->duration,
STK_DATA_OBJECT_TYPE_ICON_ID, 0,
&obj->icon_id_usr_cfm,
STK_DATA_OBJECT_TYPE_ALPHA_ID, 0,
&obj->alpha_id_call_setup,
STK_DATA_OBJECT_TYPE_ICON_ID, 0,
&obj->icon_id_call_setup,
STK_DATA_OBJECT_TYPE_TEXT_ATTRIBUTE, 0,
&obj->text_attr_usr_cfm,
STK_DATA_OBJECT_TYPE_TEXT_ATTRIBUTE, 0,
&obj->text_attr_call_setup,
STK_DATA_OBJECT_TYPE_FRAME_ID, 0,
&obj->frame_id,
STK_DATA_OBJECT_TYPE_INVALID);
CHECK_TEXT_AND_ICON(obj->alpha_id_usr_cfm, obj->icon_id_usr_cfm.id);
CHECK_TEXT_AND_ICON(obj->alpha_id_call_setup,
obj->icon_id_call_setup.id);
return status;
}
static void destroy_refresh(struct stk_command *command)
{
l_queue_destroy(command->refresh.file_list, l_free);
l_free(command->refresh.alpha_id);
}
static enum stk_command_parse_result parse_refresh(
struct stk_command *command,
struct comprehension_tlv_iter *iter)
{
struct stk_command_refresh *obj = &command->refresh;
enum stk_command_parse_result status;
if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC)
return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
if (command->dst != STK_DEVICE_IDENTITY_TYPE_TERMINAL)
return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
command->destructor = destroy_refresh;
status = parse_dataobj(iter, STK_DATA_OBJECT_TYPE_FILE_LIST, 0,
&obj->file_list,
STK_DATA_OBJECT_TYPE_AID, 0,
&obj->aid,
STK_DATA_OBJECT_TYPE_ALPHA_ID, 0,
&obj->alpha_id,
STK_DATA_OBJECT_TYPE_ICON_ID, 0,
&obj->icon_id,
STK_DATA_OBJECT_TYPE_TEXT_ATTRIBUTE, 0,
&obj->text_attr,
STK_DATA_OBJECT_TYPE_FRAME_ID, 0,
&obj->frame_id,
STK_DATA_OBJECT_TYPE_INVALID);
CHECK_TEXT_AND_ICON(obj->alpha_id, obj->icon_id.id);
return status;
}
static enum stk_command_parse_result parse_polling_off(
struct stk_command *command,
struct comprehension_tlv_iter *iter)
{
if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC)
return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
if (command->dst != STK_DEVICE_IDENTITY_TYPE_TERMINAL)
return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
return STK_PARSE_RESULT_OK;
}
static enum stk_command_parse_result parse_provide_local_info(
struct stk_command *command,
struct comprehension_tlv_iter *iter)
{
if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC)
return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
if (command->dst != STK_DEVICE_IDENTITY_TYPE_TERMINAL)
return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
return STK_PARSE_RESULT_OK;
}
static enum stk_command_parse_result parse_setup_event_list(
struct stk_command *command,
struct comprehension_tlv_iter *iter)
{
struct stk_command_setup_event_list *obj = &command->setup_event_list;
if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC)
return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
if (command->dst != STK_DEVICE_IDENTITY_TYPE_TERMINAL)
return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
return parse_dataobj(iter, STK_DATA_OBJECT_TYPE_EVENT_LIST,
DATAOBJ_FLAG_MANDATORY | DATAOBJ_FLAG_MINIMUM,
&obj->event_list,
STK_DATA_OBJECT_TYPE_INVALID);
}
static enum stk_command_parse_result parse_perform_card_apdu(
struct stk_command *command,
struct comprehension_tlv_iter *iter)
{
struct stk_command_perform_card_apdu *obj = &command->perform_card_apdu;
if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC)
return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
if ((command->dst < STK_DEVICE_IDENTITY_TYPE_CARD_READER_0) ||
(command->dst > STK_DEVICE_IDENTITY_TYPE_CARD_READER_7))
return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
return parse_dataobj(iter, STK_DATA_OBJECT_TYPE_C_APDU,
DATAOBJ_FLAG_MANDATORY | DATAOBJ_FLAG_MINIMUM,
&obj->c_apdu,
STK_DATA_OBJECT_TYPE_INVALID);
}
static enum stk_command_parse_result parse_power_off_card(
struct stk_command *command,
struct comprehension_tlv_iter *iter)
{
if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC)
return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
if ((command->dst < STK_DEVICE_IDENTITY_TYPE_CARD_READER_0) ||
(command->dst > STK_DEVICE_IDENTITY_TYPE_CARD_READER_7))
return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
return STK_PARSE_RESULT_OK;
}
static enum stk_command_parse_result parse_power_on_card(
struct stk_command *command,
struct comprehension_tlv_iter *iter)
{
if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC)
return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
if ((command->dst < STK_DEVICE_IDENTITY_TYPE_CARD_READER_0) ||
(command->dst > STK_DEVICE_IDENTITY_TYPE_CARD_READER_7))
return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
return STK_PARSE_RESULT_OK;
}
static enum stk_command_parse_result parse_get_reader_status(
struct stk_command *command,
struct comprehension_tlv_iter *iter)
{
if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC)
return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
switch (command->qualifier) {
case STK_QUALIFIER_TYPE_CARD_READER_STATUS:
if (command->dst != STK_DEVICE_IDENTITY_TYPE_TERMINAL)
return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
break;
case STK_QUALIFIER_TYPE_CARD_READER_ID:
if ((command->dst < STK_DEVICE_IDENTITY_TYPE_CARD_READER_0) ||
(command->dst >
STK_DEVICE_IDENTITY_TYPE_CARD_READER_7))
return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
break;
default:
return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
}
return STK_PARSE_RESULT_OK;
}
static enum stk_command_parse_result parse_timer_mgmt(
struct stk_command *command,
struct comprehension_tlv_iter *iter)
{
struct stk_command_timer_mgmt *obj = &command->timer_mgmt;
enum stk_data_object_flag value_flags = 0;
if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC)
return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
if (command->dst != STK_DEVICE_IDENTITY_TYPE_TERMINAL)
return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
if ((command->qualifier & 3) == 0) /* Start a timer */
value_flags = DATAOBJ_FLAG_MANDATORY;
return parse_dataobj(iter, STK_DATA_OBJECT_TYPE_TIMER_ID,
DATAOBJ_FLAG_MANDATORY | DATAOBJ_FLAG_MINIMUM,
&obj->timer_id,
STK_DATA_OBJECT_TYPE_TIMER_VALUE, value_flags,
&obj->timer_value,
STK_DATA_OBJECT_TYPE_INVALID);
}
static void destroy_setup_idle_mode_text(struct stk_command *command)
{
l_free(command->setup_idle_mode_text.text);
}
static enum stk_command_parse_result parse_setup_idle_mode_text(
struct stk_command *command,
struct comprehension_tlv_iter *iter)
{
struct stk_command_setup_idle_mode_text *obj =
&command->setup_idle_mode_text;
enum stk_command_parse_result status;
if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC)
return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
if (command->dst != STK_DEVICE_IDENTITY_TYPE_TERMINAL)
return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
command->destructor = destroy_setup_idle_mode_text;
status = parse_dataobj(iter, STK_DATA_OBJECT_TYPE_TEXT,
DATAOBJ_FLAG_MANDATORY | DATAOBJ_FLAG_MINIMUM,
&obj->text,
STK_DATA_OBJECT_TYPE_ICON_ID, 0,
&obj->icon_id,
STK_DATA_OBJECT_TYPE_TEXT_ATTRIBUTE, 0,
&obj->text_attr,
STK_DATA_OBJECT_TYPE_FRAME_ID, 0,
&obj->frame_id,
STK_DATA_OBJECT_TYPE_INVALID);
CHECK_TEXT_AND_ICON(obj->text, obj->icon_id.id);
return status;
}
static void destroy_run_at_command(struct stk_command *command)
{
l_free(command->run_at_command.alpha_id);
l_free(command->run_at_command.at_command);
}
static enum stk_command_parse_result parse_run_at_command(
struct stk_command *command,
struct comprehension_tlv_iter *iter)
{
struct stk_command_run_at_command *obj = &command->run_at_command;
enum stk_command_parse_result status;
if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC)
return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
if (command->dst != STK_DEVICE_IDENTITY_TYPE_TERMINAL)
return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
command->destructor = destroy_run_at_command;
status = parse_dataobj(iter, STK_DATA_OBJECT_TYPE_ALPHA_ID, 0,
&obj->alpha_id,
STK_DATA_OBJECT_TYPE_AT_COMMAND,
DATAOBJ_FLAG_MANDATORY | DATAOBJ_FLAG_MINIMUM,
&obj->at_command,
STK_DATA_OBJECT_TYPE_ICON_ID, 0,
&obj->icon_id,
STK_DATA_OBJECT_TYPE_TEXT_ATTRIBUTE, 0,
&obj->text_attr,
STK_DATA_OBJECT_TYPE_FRAME_ID, 0,
&obj->frame_id,
STK_DATA_OBJECT_TYPE_INVALID);
CHECK_TEXT_AND_ICON(obj->alpha_id, obj->icon_id.id);
return status;
}
static void destroy_send_dtmf(struct stk_command *command)
{
l_free(command->send_dtmf.alpha_id);
l_free(command->send_dtmf.dtmf);
}
static enum stk_command_parse_result parse_send_dtmf(
struct stk_command *command,
struct comprehension_tlv_iter *iter)
{
struct stk_command_send_dtmf *obj = &command->send_dtmf;
enum stk_command_parse_result status;
if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC)
return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
if (command->dst != STK_DEVICE_IDENTITY_TYPE_NETWORK)
return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
command->destructor = destroy_send_dtmf;
status = parse_dataobj(iter, STK_DATA_OBJECT_TYPE_ALPHA_ID, 0,
&obj->alpha_id,
STK_DATA_OBJECT_TYPE_DTMF_STRING,
DATAOBJ_FLAG_MANDATORY | DATAOBJ_FLAG_MINIMUM,
&obj->dtmf,
STK_DATA_OBJECT_TYPE_ICON_ID, 0,
&obj->icon_id,
STK_DATA_OBJECT_TYPE_TEXT_ATTRIBUTE, 0,
&obj->text_attr,
STK_DATA_OBJECT_TYPE_FRAME_ID, 0,
&obj->frame_id,
STK_DATA_OBJECT_TYPE_INVALID);
CHECK_TEXT_AND_ICON(obj->alpha_id, obj->icon_id.id);
return status;
}
static enum stk_command_parse_result parse_language_notification(
struct stk_command *command,
struct comprehension_tlv_iter *iter)
{
struct stk_command_language_notification *obj =
&command->language_notification;
if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC)
return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
if (command->dst != STK_DEVICE_IDENTITY_TYPE_TERMINAL)
return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
return parse_dataobj(iter, STK_DATA_OBJECT_TYPE_LANGUAGE, 0,
&obj->language,
STK_DATA_OBJECT_TYPE_INVALID);
}
static void destroy_launch_browser(struct stk_command *command)
{
l_free(command->launch_browser.url);
l_free(command->launch_browser.bearer.array);
l_queue_destroy(command->launch_browser.prov_file_refs, l_free);
l_free(command->launch_browser.text_gateway_proxy_id);
l_free(command->launch_browser.alpha_id);
l_free(command->launch_browser.network_name.array);
l_free(command->launch_browser.text_usr);
l_free(command->launch_browser.text_passwd);
}
static enum stk_command_parse_result parse_launch_browser(
struct stk_command *command,
struct comprehension_tlv_iter *iter)
{
struct stk_command_launch_browser *obj = &command->launch_browser;
if (command->qualifier > 3 || command->qualifier == 1)
return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC)
return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
if (command->dst != STK_DEVICE_IDENTITY_TYPE_TERMINAL)
return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
command->destructor = destroy_launch_browser;
return parse_dataobj(iter,
STK_DATA_OBJECT_TYPE_BROWSER_ID, 0,
&obj->browser_id,
STK_DATA_OBJECT_TYPE_URL,
DATAOBJ_FLAG_MANDATORY | DATAOBJ_FLAG_MINIMUM,
&obj->url,
STK_DATA_OBJECT_TYPE_BEARER, 0,
&obj->bearer,
STK_DATA_OBJECT_TYPE_PROVISIONING_FILE_REF,
DATAOBJ_FLAG_LIST,
&obj->prov_file_refs,
STK_DATA_OBJECT_TYPE_TEXT, 0,
&obj->text_gateway_proxy_id,
STK_DATA_OBJECT_TYPE_ALPHA_ID, 0,
&obj->alpha_id,
STK_DATA_OBJECT_TYPE_ICON_ID, 0,
&obj->icon_id,
STK_DATA_OBJECT_TYPE_TEXT_ATTRIBUTE, 0,
&obj->text_attr,
STK_DATA_OBJECT_TYPE_FRAME_ID, 0,
&obj->frame_id,
STK_DATA_OBJECT_TYPE_NETWORK_ACCESS_NAME, 0,
&obj->network_name,
STK_DATA_OBJECT_TYPE_TEXT, 0,
&obj->text_usr,
STK_DATA_OBJECT_TYPE_TEXT, 0,
&obj->text_passwd,
STK_DATA_OBJECT_TYPE_INVALID);
}
static void destroy_open_channel(struct stk_command *command)
{
l_free(command->open_channel.alpha_id);
l_free(command->open_channel.apn);
l_free(command->open_channel.text_usr);
l_free(command->open_channel.text_passwd);
}
static enum stk_command_parse_result parse_open_channel(
struct stk_command *command,
struct comprehension_tlv_iter *iter)
{
struct stk_command_open_channel *obj = &command->open_channel;
enum stk_command_parse_result status;
if (command->qualifier >= 0x08)
return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC)
return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
if (command->dst != STK_DEVICE_IDENTITY_TYPE_TERMINAL)
return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
command->destructor = destroy_open_channel;
/*
* parse the Open Channel data objects related to packet data service
* bearer
*/
status = parse_dataobj(iter,
STK_DATA_OBJECT_TYPE_ALPHA_ID, 0,
&obj->alpha_id,
STK_DATA_OBJECT_TYPE_ICON_ID, 0,
&obj->icon_id,
STK_DATA_OBJECT_TYPE_BEARER_DESCRIPTION,
DATAOBJ_FLAG_MANDATORY | DATAOBJ_FLAG_MINIMUM,
&obj->bearer_desc,
STK_DATA_OBJECT_TYPE_BUFFER_SIZE,
DATAOBJ_FLAG_MANDATORY | DATAOBJ_FLAG_MINIMUM,
&obj->buf_size,
STK_DATA_OBJECT_TYPE_NETWORK_ACCESS_NAME, 0,
&obj->apn,
STK_DATA_OBJECT_TYPE_OTHER_ADDRESS, 0,
&obj->local_addr,
STK_DATA_OBJECT_TYPE_TEXT, 0,
&obj->text_usr,
STK_DATA_OBJECT_TYPE_TEXT, 0,
&obj->text_passwd,
STK_DATA_OBJECT_TYPE_UICC_TE_INTERFACE, 0,
&obj->uti,
STK_DATA_OBJECT_TYPE_OTHER_ADDRESS, 0,
&obj->data_dest_addr,
STK_DATA_OBJECT_TYPE_TEXT_ATTRIBUTE, 0,
&obj->text_attr,
STK_DATA_OBJECT_TYPE_FRAME_ID, 0,
&obj->frame_id,
STK_DATA_OBJECT_TYPE_INVALID);
CHECK_TEXT_AND_ICON(obj->alpha_id, obj->icon_id.id);
return status;
}
static void destroy_close_channel(struct stk_command *command)
{
l_free(command->close_channel.alpha_id);
}
static enum stk_command_parse_result parse_close_channel(
struct stk_command *command,
struct comprehension_tlv_iter *iter)
{
struct stk_command_close_channel *obj = &command->close_channel;
enum stk_command_parse_result status;
if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC)
return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
if ((command->dst < STK_DEVICE_IDENTITY_TYPE_CHANNEL_1) ||
(command->dst > STK_DEVICE_IDENTITY_TYPE_CHANNEL_7))
return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
command->destructor = destroy_close_channel;
status = parse_dataobj(iter, STK_DATA_OBJECT_TYPE_ALPHA_ID, 0,
&obj->alpha_id,
STK_DATA_OBJECT_TYPE_ICON_ID, 0,
&obj->icon_id,
STK_DATA_OBJECT_TYPE_TEXT_ATTRIBUTE, 0,
&obj->text_attr,
STK_DATA_OBJECT_TYPE_FRAME_ID, 0,
&obj->frame_id,
STK_DATA_OBJECT_TYPE_INVALID);
CHECK_TEXT_AND_ICON(obj->alpha_id, obj->icon_id.id);
return status;
}
static void destroy_receive_data(struct stk_command *command)
{
l_free(command->receive_data.alpha_id);
}
static enum stk_command_parse_result parse_receive_data(
struct stk_command *command,
struct comprehension_tlv_iter *iter)
{
struct stk_command_receive_data *obj = &command->receive_data;
enum stk_command_parse_result status;
if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC)
return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
if ((command->dst < STK_DEVICE_IDENTITY_TYPE_CHANNEL_1) ||
(command->dst > STK_DEVICE_IDENTITY_TYPE_CHANNEL_7))
return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
command->destructor = destroy_receive_data;
status = parse_dataobj(iter, STK_DATA_OBJECT_TYPE_ALPHA_ID, 0,
&obj->alpha_id,
STK_DATA_OBJECT_TYPE_ICON_ID, 0,
&obj->icon_id,
STK_DATA_OBJECT_TYPE_CHANNEL_DATA_LENGTH,
DATAOBJ_FLAG_MANDATORY | DATAOBJ_FLAG_MINIMUM,
&obj->data_len,
STK_DATA_OBJECT_TYPE_TEXT_ATTRIBUTE, 0,
&obj->text_attr,
STK_DATA_OBJECT_TYPE_FRAME_ID, 0,
&obj->frame_id,
STK_DATA_OBJECT_TYPE_INVALID);
CHECK_TEXT_AND_ICON(obj->alpha_id, obj->icon_id.id);
return status;
}
static void destroy_send_data(struct stk_command *command)
{
l_free(command->send_data.alpha_id);
l_free(command->send_data.data.array);
}
static enum stk_command_parse_result parse_send_data(
struct stk_command *command,
struct comprehension_tlv_iter *iter)
{
struct stk_command_send_data *obj = &command->send_data;
enum stk_command_parse_result status;
if (command->qualifier > STK_SEND_DATA_IMMEDIATELY)
return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC)
return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
if ((command->dst < STK_DEVICE_IDENTITY_TYPE_CHANNEL_1) ||
(command->dst > STK_DEVICE_IDENTITY_TYPE_CHANNEL_7))
return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
command->destructor = destroy_send_data;
status = parse_dataobj(iter, STK_DATA_OBJECT_TYPE_ALPHA_ID, 0,
&obj->alpha_id,
STK_DATA_OBJECT_TYPE_ICON_ID, 0,
&obj->icon_id,
STK_DATA_OBJECT_TYPE_CHANNEL_DATA,
DATAOBJ_FLAG_MANDATORY | DATAOBJ_FLAG_MINIMUM,
&obj->data,
STK_DATA_OBJECT_TYPE_TEXT_ATTRIBUTE, 0,
&obj->text_attr,
STK_DATA_OBJECT_TYPE_FRAME_ID, 0,
&obj->frame_id,
STK_DATA_OBJECT_TYPE_INVALID);
CHECK_TEXT_AND_ICON(obj->alpha_id, obj->icon_id.id);
return status;
}
static enum stk_command_parse_result parse_get_channel_status(
struct stk_command *command,
struct comprehension_tlv_iter *iter)
{
if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC)
return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
if (command->dst != STK_DEVICE_IDENTITY_TYPE_TERMINAL)
return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
return STK_PARSE_RESULT_OK;
}
static void destroy_service_search(struct stk_command *command)
{
l_free(command->service_search.alpha_id);
l_free(command->service_search.serv_search.ser_search);
l_free(command->service_search.dev_filter.dev_filter);
}
static enum stk_command_parse_result parse_service_search(
struct stk_command *command,
struct comprehension_tlv_iter *iter)
{
struct stk_command_service_search *obj = &command->service_search;
if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC)
return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
if (command->dst != STK_DEVICE_IDENTITY_TYPE_TERMINAL)
return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
command->destructor = destroy_service_search;
return parse_dataobj(iter, STK_DATA_OBJECT_TYPE_ALPHA_ID, 0,
&obj->alpha_id,
STK_DATA_OBJECT_TYPE_ICON_ID, 0,
&obj->icon_id,
STK_DATA_OBJECT_TYPE_SERVICE_SEARCH,
DATAOBJ_FLAG_MANDATORY | DATAOBJ_FLAG_MINIMUM,
&obj->serv_search,
STK_DATA_OBJECT_TYPE_DEVICE_FILTER, 0,
&obj->dev_filter,
STK_DATA_OBJECT_TYPE_TEXT_ATTRIBUTE, 0,
&obj->text_attr,
STK_DATA_OBJECT_TYPE_FRAME_ID, 0,
&obj->frame_id,
STK_DATA_OBJECT_TYPE_INVALID);
}
static void destroy_get_service_info(struct stk_command *command)
{
l_free(command->get_service_info.alpha_id);
l_free(command->get_service_info.attr_info.attr_info);
}
static enum stk_command_parse_result parse_get_service_info(
struct stk_command *command,
struct comprehension_tlv_iter *iter)
{
struct stk_command_get_service_info *obj = &command->get_service_info;
if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC)
return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
if (command->dst != STK_DEVICE_IDENTITY_TYPE_TERMINAL)
return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
command->destructor = destroy_get_service_info;
return parse_dataobj(iter, STK_DATA_OBJECT_TYPE_ALPHA_ID, 0,
&obj->alpha_id,
STK_DATA_OBJECT_TYPE_ICON_ID, 0,
&obj->icon_id,
STK_DATA_OBJECT_TYPE_ATTRIBUTE_INFO,
DATAOBJ_FLAG_MANDATORY | DATAOBJ_FLAG_MINIMUM,
&obj->attr_info,
STK_DATA_OBJECT_TYPE_TEXT_ATTRIBUTE, 0,
&obj->text_attr,
STK_DATA_OBJECT_TYPE_FRAME_ID, 0,
&obj->frame_id,
STK_DATA_OBJECT_TYPE_INVALID);
}
static void destroy_declare_service(struct stk_command *command)
{
l_free(command->declare_service.serv_rec.serv_rec);
}
static enum stk_command_parse_result parse_declare_service(
struct stk_command *command,
struct comprehension_tlv_iter *iter)
{
struct stk_command_declare_service *obj = &command->declare_service;
if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC)
return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
if (command->dst != STK_DEVICE_IDENTITY_TYPE_TERMINAL)
return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
command->destructor = destroy_declare_service;
return parse_dataobj(iter, STK_DATA_OBJECT_TYPE_SERVICE_RECORD,
DATAOBJ_FLAG_MANDATORY | DATAOBJ_FLAG_MINIMUM,
&obj->serv_rec,
STK_DATA_OBJECT_TYPE_UICC_TE_INTERFACE, 0,
&obj->intf,
STK_DATA_OBJECT_TYPE_INVALID);
}
static enum stk_command_parse_result parse_set_frames(
struct stk_command *command,
struct comprehension_tlv_iter *iter)
{
struct stk_command_set_frames *obj = &command->set_frames;
if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC)
return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
if (command->dst != STK_DEVICE_IDENTITY_TYPE_TERMINAL)
return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
return parse_dataobj(iter, STK_DATA_OBJECT_TYPE_FRAME_ID,
DATAOBJ_FLAG_MANDATORY | DATAOBJ_FLAG_MINIMUM,
&obj->frame_id,
STK_DATA_OBJECT_TYPE_FRAME_LAYOUT, 0,
&obj->frame_layout,
STK_DATA_OBJECT_TYPE_FRAME_ID, 0,
&obj->frame_id_default,
STK_DATA_OBJECT_TYPE_INVALID);
}
static enum stk_command_parse_result parse_get_frames_status(
struct stk_command *command,
struct comprehension_tlv_iter *iter)
{
if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC)
return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
if (command->dst != STK_DEVICE_IDENTITY_TYPE_TERMINAL)
return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
return STK_PARSE_RESULT_OK;
}
static void destroy_retrieve_mms(struct stk_command *command)
{
l_free(command->retrieve_mms.alpha_id);
l_queue_destroy(command->retrieve_mms.mms_rec_files, l_free);
}
static enum stk_command_parse_result parse_retrieve_mms(
struct stk_command *command,
struct comprehension_tlv_iter *iter)
{
struct stk_command_retrieve_mms *obj = &command->retrieve_mms;
enum stk_command_parse_result status;
if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC)
return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
if (command->dst != STK_DEVICE_IDENTITY_TYPE_NETWORK)
return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
command->destructor = destroy_retrieve_mms;
status = parse_dataobj(iter, STK_DATA_OBJECT_TYPE_ALPHA_ID, 0,
&obj->alpha_id,
STK_DATA_OBJECT_TYPE_ICON_ID, 0,
&obj->icon_id,
STK_DATA_OBJECT_TYPE_MMS_REFERENCE,
DATAOBJ_FLAG_MANDATORY | DATAOBJ_FLAG_MINIMUM,
&obj->mms_ref,
STK_DATA_OBJECT_TYPE_FILE_LIST,
DATAOBJ_FLAG_MANDATORY | DATAOBJ_FLAG_MINIMUM,
&obj->mms_rec_files,
STK_DATA_OBJECT_TYPE_MMS_CONTENT_ID,
DATAOBJ_FLAG_MANDATORY | DATAOBJ_FLAG_MINIMUM,
&obj->mms_content_id,
STK_DATA_OBJECT_TYPE_MMS_ID, 0,
&obj->mms_id,
STK_DATA_OBJECT_TYPE_TEXT_ATTRIBUTE, 0,
&obj->text_attr,
STK_DATA_OBJECT_TYPE_FRAME_ID, 0,
&obj->frame_id,
STK_DATA_OBJECT_TYPE_INVALID);
CHECK_TEXT_AND_ICON(obj->alpha_id, obj->icon_id.id);
return status;
}
static void destroy_submit_mms(struct stk_command *command)
{
l_free(command->submit_mms.alpha_id);
l_queue_destroy(command->submit_mms.mms_subm_files, l_free);
}
static enum stk_command_parse_result parse_submit_mms(
struct stk_command *command,
struct comprehension_tlv_iter *iter)
{
struct stk_command_submit_mms *obj = &command->submit_mms;
enum stk_command_parse_result status;
if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC)
return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
if (command->dst != STK_DEVICE_IDENTITY_TYPE_NETWORK)
return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
command->destructor = destroy_submit_mms;
status = parse_dataobj(iter, STK_DATA_OBJECT_TYPE_ALPHA_ID, 0,
&obj->alpha_id,
STK_DATA_OBJECT_TYPE_ICON_ID, 0,
&obj->icon_id,
STK_DATA_OBJECT_TYPE_FILE_LIST,
DATAOBJ_FLAG_MANDATORY | DATAOBJ_FLAG_MINIMUM,
&obj->mms_subm_files,
STK_DATA_OBJECT_TYPE_MMS_ID, 0,
&obj->mms_id,
STK_DATA_OBJECT_TYPE_TEXT_ATTRIBUTE, 0,
&obj->text_attr,
STK_DATA_OBJECT_TYPE_FRAME_ID, 0,
&obj->frame_id,
STK_DATA_OBJECT_TYPE_INVALID);
CHECK_TEXT_AND_ICON(obj->alpha_id, obj->icon_id.id);
return status;
}
static void destroy_display_mms(struct stk_command *command)
{
l_queue_destroy(command->display_mms.mms_subm_files, l_free);
}
static enum stk_command_parse_result parse_display_mms(
struct stk_command *command,
struct comprehension_tlv_iter *iter)
{
struct stk_command_display_mms *obj = &command->display_mms;
if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC)
return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
if (command->dst != STK_DEVICE_IDENTITY_TYPE_TERMINAL)
return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
command->destructor = destroy_display_mms;
return parse_dataobj(iter, STK_DATA_OBJECT_TYPE_FILE_LIST,
DATAOBJ_FLAG_MANDATORY | DATAOBJ_FLAG_MINIMUM,
&obj->mms_subm_files,
STK_DATA_OBJECT_TYPE_MMS_ID,
DATAOBJ_FLAG_MANDATORY | DATAOBJ_FLAG_MINIMUM,
&obj->mms_id,
STK_DATA_OBJECT_TYPE_IMMEDIATE_RESPONSE, 0,
&obj->imd_resp,
STK_DATA_OBJECT_TYPE_FRAME_ID, 0,
&obj->frame_id,
STK_DATA_OBJECT_TYPE_INVALID);
}
static enum stk_command_parse_result parse_activate(
struct stk_command *command,
struct comprehension_tlv_iter *iter)
{
struct stk_command_activate *obj = &command->activate;
if (command->src != STK_DEVICE_IDENTITY_TYPE_UICC)
return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
if (command->dst != STK_DEVICE_IDENTITY_TYPE_TERMINAL)
return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
return parse_dataobj(iter, STK_DATA_OBJECT_TYPE_ACTIVATE_DESCRIPTOR,
DATAOBJ_FLAG_MANDATORY | DATAOBJ_FLAG_MINIMUM,
&obj->actv_desc,
STK_DATA_OBJECT_TYPE_INVALID);
}
static enum stk_command_parse_result parse_command_body(
struct stk_command *command,
struct comprehension_tlv_iter *iter)
{
switch (command->type) {
case STK_COMMAND_TYPE_DISPLAY_TEXT:
return parse_display_text(command, iter);
case STK_COMMAND_TYPE_GET_INKEY:
return parse_get_inkey(command, iter);
case STK_COMMAND_TYPE_GET_INPUT:
return parse_get_input(command, iter);
case STK_COMMAND_TYPE_MORE_TIME:
return parse_more_time(command, iter);
case STK_COMMAND_TYPE_PLAY_TONE:
return parse_play_tone(command, iter);
case STK_COMMAND_TYPE_POLL_INTERVAL:
return parse_poll_interval(command, iter);
case STK_COMMAND_TYPE_SETUP_MENU:
return parse_setup_menu(command, iter);
case STK_COMMAND_TYPE_SELECT_ITEM:
return parse_select_item(command, iter);
case STK_COMMAND_TYPE_SEND_SMS:
return parse_send_sms(command, iter);
case STK_COMMAND_TYPE_SEND_SS:
return parse_send_ss(command, iter);
case STK_COMMAND_TYPE_SEND_USSD:
return parse_send_ussd(command, iter);
case STK_COMMAND_TYPE_SETUP_CALL:
return parse_setup_call(command, iter);
case STK_COMMAND_TYPE_REFRESH:
return parse_refresh(command, iter);
case STK_COMMAND_TYPE_POLLING_OFF:
return parse_polling_off(command, iter);
case STK_COMMAND_TYPE_PROVIDE_LOCAL_INFO:
return parse_provide_local_info(command, iter);
case STK_COMMAND_TYPE_SETUP_EVENT_LIST:
return parse_setup_event_list(command, iter);
case STK_COMMAND_TYPE_PERFORM_CARD_APDU:
return parse_perform_card_apdu(command, iter);
case STK_COMMAND_TYPE_POWER_OFF_CARD:
return parse_power_off_card(command, iter);
case STK_COMMAND_TYPE_POWER_ON_CARD:
return parse_power_on_card(command, iter);
case STK_COMMAND_TYPE_GET_READER_STATUS:
return parse_get_reader_status(command, iter);
case STK_COMMAND_TYPE_TIMER_MANAGEMENT:
return parse_timer_mgmt(command, iter);
case STK_COMMAND_TYPE_SETUP_IDLE_MODE_TEXT:
return parse_setup_idle_mode_text(command, iter);
case STK_COMMAND_TYPE_RUN_AT_COMMAND:
return parse_run_at_command(command, iter);
case STK_COMMAND_TYPE_SEND_DTMF:
return parse_send_dtmf(command, iter);
case STK_COMMAND_TYPE_LANGUAGE_NOTIFICATION:
return parse_language_notification(command, iter);
case STK_COMMAND_TYPE_LAUNCH_BROWSER:
return parse_launch_browser(command, iter);
case STK_COMMAND_TYPE_OPEN_CHANNEL:
return parse_open_channel(command, iter);
case STK_COMMAND_TYPE_CLOSE_CHANNEL:
return parse_close_channel(command, iter);
case STK_COMMAND_TYPE_RECEIVE_DATA:
return parse_receive_data(command, iter);
case STK_COMMAND_TYPE_SEND_DATA:
return parse_send_data(command, iter);
case STK_COMMAND_TYPE_GET_CHANNEL_STATUS:
return parse_get_channel_status(command, iter);
case STK_COMMAND_TYPE_SERVICE_SEARCH:
return parse_service_search(command, iter);
case STK_COMMAND_TYPE_GET_SERVICE_INFO:
return parse_get_service_info(command, iter);
case STK_COMMAND_TYPE_DECLARE_SERVICE:
return parse_declare_service(command, iter);
case STK_COMMAND_TYPE_SET_FRAMES:
return parse_set_frames(command, iter);
case STK_COMMAND_TYPE_GET_FRAMES_STATUS:
return parse_get_frames_status(command, iter);
case STK_COMMAND_TYPE_RETRIEVE_MMS:
return parse_retrieve_mms(command, iter);
case STK_COMMAND_TYPE_SUBMIT_MMS:
return parse_submit_mms(command, iter);
case STK_COMMAND_TYPE_DISPLAY_MMS:
return parse_display_mms(command, iter);
case STK_COMMAND_TYPE_ACTIVATE:
return parse_activate(command, iter);
default:
return STK_PARSE_RESULT_TYPE_NOT_UNDERSTOOD;
};
}
struct stk_command *stk_command_new_from_pdu(const uint8_t *pdu,
unsigned int len)
{
struct ber_tlv_iter ber;
struct comprehension_tlv_iter iter;
const uint8_t *data;
struct stk_command *command;
ber_tlv_iter_init(&ber, pdu, len);
if (ber_tlv_iter_next(&ber) != TRUE)
return NULL;
/* We should be wrapped in a Proactive UICC Command Tag 0xD0 */
if (ber_tlv_iter_get_short_tag(&ber) != 0xD0)
return NULL;
ber_tlv_iter_recurse_comprehension(&ber, &iter);
/*
* Now parse actual command details, they come in order with
* Command Details TLV first, followed by Device Identities TLV
*/
if (comprehension_tlv_iter_next(&iter) != TRUE)
return NULL;
if (comprehension_tlv_iter_get_tag(&iter) !=
STK_DATA_OBJECT_TYPE_COMMAND_DETAILS)
return NULL;
if (comprehension_tlv_iter_get_length(&iter) != 0x03)
return NULL;
data = comprehension_tlv_iter_get_data(&iter);
command = g_new0(struct stk_command, 1);
command->number = data[0];
command->type = data[1];
command->qualifier = data[2];
if (comprehension_tlv_iter_next(&iter) != TRUE) {
command->status = STK_PARSE_RESULT_MISSING_VALUE;
goto out;
}
if (comprehension_tlv_iter_get_tag(&iter) !=
STK_DATA_OBJECT_TYPE_DEVICE_IDENTITIES) {
command->status = STK_PARSE_RESULT_MISSING_VALUE;
goto out;
}
if (comprehension_tlv_iter_get_length(&iter) != 0x02) {
command->status = STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD;
goto out;
}
data = comprehension_tlv_iter_get_data(&iter);
command->src = data[0];
command->dst = data[1];
command->status = parse_command_body(command, &iter);
out:
return command;
}
void stk_command_free(struct stk_command *command)
{
if (command->destructor)
command->destructor(command);
g_free(command);
}
static bool stk_tlv_builder_init(struct stk_tlv_builder *iter,
uint8_t *pdu, unsigned int size)
{
iter->value = NULL;
iter->len = 0;
return comprehension_tlv_builder_init(&iter->ctlv, pdu, size);
}
static bool stk_tlv_builder_recurse(struct stk_tlv_builder *iter,
struct ber_tlv_builder *btlv,
uint8_t tag)
{
iter->value = NULL;
iter->len = 0;
if (ber_tlv_builder_next(btlv, tag >> 6, (tag >> 5) & 1,
tag & 0x1f) != TRUE)
return false;
return ber_tlv_builder_recurse_comprehension(btlv, &iter->ctlv);
}
static bool stk_tlv_builder_open_container(struct stk_tlv_builder *iter,
bool cr,
uint8_t shorttag,
bool relocatable)
{
if (comprehension_tlv_builder_next(&iter->ctlv, cr, shorttag) != TRUE)
return false;
iter->len = 0;
iter->max_len = relocatable ? 0xff : 0x7f;
if (comprehension_tlv_builder_set_length(&iter->ctlv, iter->max_len) !=
TRUE)
return false;
iter->value = comprehension_tlv_builder_get_data(&iter->ctlv);
return true;
}
static bool stk_tlv_builder_close_container(struct stk_tlv_builder *iter)
{
return comprehension_tlv_builder_set_length(&iter->ctlv, iter->len);
}
static unsigned int stk_tlv_builder_get_length(struct stk_tlv_builder *iter)
{
return comprehension_tlv_builder_get_data(&iter->ctlv) -
iter->ctlv.pdu + iter->len;
}
static bool stk_tlv_builder_append_byte(struct stk_tlv_builder *iter,
uint8_t num)
{
if (iter->len >= iter->max_len)
return false;
iter->value[iter->len++] = num;
return true;
}
static bool stk_tlv_builder_append_short(struct stk_tlv_builder *iter,
uint16_t num)
{
if (iter->len + 2 > iter->max_len)
return false;
iter->value[iter->len++] = num >> 8;
iter->value[iter->len++] = num & 0xff;
return true;
}
static bool stk_tlv_builder_append_gsm_packed(struct stk_tlv_builder *iter,
const char *text)
{
unsigned int len;
uint8_t *gsm;
long written = 0;
if (text == NULL)
return true;
len = strlen(text);
gsm = convert_utf8_to_gsm(text, len, NULL, &written, 0);
if (gsm == NULL && len > 0)
return false;
if (iter->len + (written * 7 + 7) / 8 >= iter->max_len) {
l_free(gsm);
return false;
}
pack_7bit_own_buf(gsm, len, 0, false, &written, 0,
iter->value + iter->len + 1);
l_free(gsm);
if (written < 1 && len > 0)
return false;
iter->value[iter->len++] = 0x00;
iter->len += written;
return true;
}
static bool stk_tlv_builder_append_gsm_unpacked(struct stk_tlv_builder *iter,
const char *text)
{
unsigned int len;
uint8_t *gsm;
long written = 0;
if (text == NULL)
return true;
len = strlen(text);
gsm = convert_utf8_to_gsm(text, len, NULL, &written, 0);
if (gsm == NULL && len > 0)
return false;
if (iter->len + written >= iter->max_len) {
l_free(gsm);
return false;
}
iter->value[iter->len++] = 0x04;
memcpy(iter->value + iter->len, gsm, written);
iter->len += written;
l_free(gsm);
return true;
}
static bool stk_tlv_builder_append_ucs2(struct stk_tlv_builder *iter,
const char *text)
{
L_AUTO_FREE_VAR(uint8_t *, ucs2);
size_t ucs2_len;
ucs2 = l_utf8_to_ucs2be(text, &ucs2_len);
if (!ucs2)
return false;
/* Don't include terminating NULL */
ucs2_len -= 2;
if (iter->len + ucs2_len >= iter->max_len)
return false;
iter->value[iter->len++] = 0x08;
memcpy(iter->value + iter->len, ucs2, ucs2_len);
iter->len += ucs2_len;
return true;
}
static bool stk_tlv_builder_append_text(struct stk_tlv_builder *iter,
int dcs, const char *text)
{
bool ret;
switch (dcs) {
case 0x00:
return stk_tlv_builder_append_gsm_packed(iter, text);
case 0x04:
return stk_tlv_builder_append_gsm_unpacked(iter, text);
case 0x08:
return stk_tlv_builder_append_ucs2(iter, text);
case -1:
ret = stk_tlv_builder_append_gsm_unpacked(iter, text);
if (ret)
return ret;
return stk_tlv_builder_append_ucs2(iter, text);
}
return false;
}
static inline bool stk_tlv_builder_append_bytes(struct stk_tlv_builder *iter,
const uint8_t *data,
unsigned int length)
{
if (iter->len + length > iter->max_len)
return false;
memcpy(iter->value + iter->len, data, length);
iter->len += length;
return true;
}
/* Described in TS 102.223 Section 8.1 */
static bool build_dataobj_address(struct stk_tlv_builder *tlv,
const void *data, bool cr)
{
const struct stk_address *addr = data;
uint8_t tag = STK_DATA_OBJECT_TYPE_ADDRESS;
unsigned int len;
uint8_t number[128];
if (addr->number == NULL)
return true;
len = (strlen(addr->number) + 1) / 2;
sim_encode_bcd_number(addr->number, number);
return stk_tlv_builder_open_container(tlv, cr, tag, false) &&
stk_tlv_builder_append_byte(tlv, addr->ton_npi) &&
stk_tlv_builder_append_bytes(tlv, number, len) &&
stk_tlv_builder_close_container(tlv);
}
/* Described in TS 102.223 Section 8.2 */
static bool build_dataobj_alpha_id(struct stk_tlv_builder *tlv,
const void *data, bool cr)
{
uint8_t tag = STK_DATA_OBJECT_TYPE_ALPHA_ID;
int len;
uint8_t *string;
if (data == NULL)
return true;
if (strlen(data) == 0)
return stk_tlv_builder_open_container(tlv, cr, tag, false) &&
stk_tlv_builder_close_container(tlv);
string = utf8_to_sim_string(data, -1, &len);
if (string == NULL)
return false;
return stk_tlv_builder_open_container(tlv, cr, tag, true) &&
stk_tlv_builder_append_bytes(tlv, string, len) &&
stk_tlv_builder_close_container(tlv);
}
/* Described in TS 102.223 Section 8.3 */
static bool build_dataobj_subaddress(struct stk_tlv_builder *tlv,
const void *data, bool cr)
{
const struct stk_subaddress *sa = data;
uint8_t tag = STK_DATA_OBJECT_TYPE_SUBADDRESS;
if (!sa->has_subaddr)
return true;
return stk_tlv_builder_open_container(tlv, cr, tag, false) &&
stk_tlv_builder_append_bytes(tlv, sa->subaddr, sa->len) &&
stk_tlv_builder_close_container(tlv);
}
/* Described in TS 131.111 Section 8.4 */
static bool build_dataobj_ccp(struct stk_tlv_builder *tlv,
const void *data, bool cr)
{
const struct stk_ccp *ccp = data;
uint8_t tag = STK_DATA_OBJECT_TYPE_CCP;
if (ccp->len == 0)
return true;
return stk_tlv_builder_open_container(tlv, cr, tag, false) &&
stk_tlv_builder_append_byte(tlv, ccp->len) &&
stk_tlv_builder_append_bytes(tlv, ccp->ccp, ccp->len) &&
stk_tlv_builder_close_container(tlv);
}
/* Described in TS 131.111 Section 8.5 */
static bool build_dataobj_cbs_page(struct stk_tlv_builder *tlv,
const void *data, bool cr)
{
const struct cbs *page = data;
uint8_t tag = STK_DATA_OBJECT_TYPE_CBS_PAGE;
uint8_t pdu[88];
if (cbs_encode(page, NULL, pdu) == FALSE)
return false;
return stk_tlv_builder_open_container(tlv, cr, tag, true) &&
stk_tlv_builder_append_bytes(tlv, pdu, 88) &&
stk_tlv_builder_close_container(tlv);
}
/* Described in TS 102.223 Section 8.6 */
static bool build_dataobj_item_id(struct stk_tlv_builder *tlv,
const void *data, bool cr)
{
const uint8_t *item_id = data;
uint8_t tag = STK_DATA_OBJECT_TYPE_ITEM_ID;
if (*item_id == 0)
return true;
return stk_tlv_builder_open_container(tlv, cr, tag, false) &&
stk_tlv_builder_append_byte(tlv, *item_id) &&
stk_tlv_builder_close_container(tlv);
}
/* Described in TS 102.223 Section 8.8 */
static bool build_dataobj_duration(struct stk_tlv_builder *tlv,
const void *data, bool cr)
{
const struct stk_duration *duration = data;
uint8_t tag = STK_DATA_OBJECT_TYPE_DURATION;
if (duration->interval == 0x00)
return true;
return stk_tlv_builder_open_container(tlv, cr, tag, false) &&
stk_tlv_builder_append_byte(tlv, duration->unit) &&
stk_tlv_builder_append_byte(tlv, duration->interval) &&
stk_tlv_builder_close_container(tlv);
}
/* Described in TS 102.223 Section 8.12 */
static bool build_dataobj_result(struct stk_tlv_builder *tlv,
const void *data, bool cr)
{
const struct stk_result *result = data;
uint8_t tag = STK_DATA_OBJECT_TYPE_RESULT;
if (!stk_tlv_builder_open_container(tlv, cr, tag, false))
return false;
if (!stk_tlv_builder_append_byte(tlv, result->type))
return false;
if (result->additional_len > 0)
if (!stk_tlv_builder_append_bytes(tlv, result->additional,
result->additional_len))
return false;
return stk_tlv_builder_close_container(tlv);
}
/* Described in TS 131.111 Section 8.13 */
static bool build_dataobj_gsm_sms_tpdu(struct stk_tlv_builder *tlv,
const void *data, bool cr)
{
const struct sms_deliver *msg = data;
struct sms sms;
uint8_t tag = STK_DATA_OBJECT_TYPE_GSM_SMS_TPDU;
uint8_t tpdu[165];
int tpdu_len;
sms.type = SMS_TYPE_DELIVER;
memset(&sms.sc_addr, 0, sizeof(sms.sc_addr));
memcpy(&sms.deliver, msg, sizeof(sms.deliver));
if (sms_encode(&sms, NULL, &tpdu_len, tpdu) == FALSE)
return false;
return stk_tlv_builder_open_container(tlv, cr, tag, true) &&
stk_tlv_builder_append_bytes(tlv, tpdu + 1, tpdu_len) &&
stk_tlv_builder_close_container(tlv);
}
/* Described in TS 131.111 Section 8.14 */
static bool build_dataobj_ss_string(struct stk_tlv_builder *tlv,
const void *data, bool cr)
{
const struct stk_address *addr = data;
uint8_t tag = STK_DATA_OBJECT_TYPE_SS_STRING;
unsigned int len;
uint8_t number[128];
if (addr->number == NULL)
return true;
len = (strlen(addr->number) + 1) / 2;
sim_encode_bcd_number(addr->number, number);
return stk_tlv_builder_open_container(tlv, cr, tag, false) &&
stk_tlv_builder_append_byte(tlv, addr->ton_npi) &&
stk_tlv_builder_append_bytes(tlv, number, len) &&
stk_tlv_builder_close_container(tlv);
}
/* Defined in TS 102.223 Section 8.15 */
static bool build_dataobj_text(struct stk_tlv_builder *tlv,
const void *data, bool cr)
{
const struct stk_answer_text *text = data;
uint8_t tag = STK_DATA_OBJECT_TYPE_TEXT;
bool ret;
if (text->text == NULL && !text->yesno)
return true;
if (!stk_tlv_builder_open_container(tlv, cr, tag, true))
return false;
if (text->yesno) {
/*
* Section 6.8.5:
* When the terminal issues [...] command qualifier set
* to "Yes/No", it shall supply the value "01" when the
* answer is "positive" and the value '00' when the
* answer is "negative" in the text string data object.
*/
if (!stk_tlv_builder_append_byte(tlv, 0x04))
return false;
ret = stk_tlv_builder_append_byte(tlv,
text->text ? 0x01 : 0x00);
} else if (text->packed)
ret = stk_tlv_builder_append_gsm_packed(tlv, text->text);
else
ret = stk_tlv_builder_append_text(tlv, -1, text->text);
if (!ret)
return ret;
return stk_tlv_builder_close_container(tlv);
}
/* Defined in TS 102.223 Section 8.15 - USSD specific case*/
static bool build_dataobj_ussd_text(struct stk_tlv_builder *tlv,
const void *data, bool cr)
{
const struct stk_ussd_text *text = data;
uint8_t tag = STK_DATA_OBJECT_TYPE_TEXT;
if (!text->has_text)
return true;
if (!stk_tlv_builder_open_container(tlv, cr, tag, true))
return false;
if (text->len > 0) {
if (!stk_tlv_builder_append_byte(tlv, text->dcs))
return false;
if (!stk_tlv_builder_append_bytes(tlv, text->text, text->len))
return false;
}
return stk_tlv_builder_close_container(tlv);
}
/* Described in TS 131.111 Section 8.17 */
static bool build_dataobj_ussd_string(struct stk_tlv_builder *tlv,
const void *data, bool cr)
{
const struct stk_ussd_string *ussd = data;
uint8_t tag = STK_DATA_OBJECT_TYPE_USSD_STRING;
return stk_tlv_builder_open_container(tlv, cr, tag, false) &&
stk_tlv_builder_append_byte(tlv, ussd->dcs) &&
stk_tlv_builder_append_bytes(tlv, ussd->string, ussd->len) &&
stk_tlv_builder_close_container(tlv);
}
/* Described in TS 102.223 Section 8.18 */
static bool build_dataobj_file_list(struct stk_tlv_builder *tlv,
const void *data, bool cr)
{
struct l_queue *fl = (void *) data;
const struct l_queue_entry *entry = l_queue_get_entries(fl);
const struct stk_file *file;
uint8_t tag = STK_DATA_OBJECT_TYPE_FILE_LIST;
if (!stk_tlv_builder_open_container(tlv, cr, tag, true))
return false;
if (!stk_tlv_builder_append_byte(tlv, l_queue_length(fl)))
return false;
for (; entry; entry = entry->next) {
file = entry->data;
if (!stk_tlv_builder_append_bytes(tlv, file->file, file->len))
return false;
}
return stk_tlv_builder_close_container(tlv);
}
/* Shortcut for a single File element */
static bool build_dataobj_file(struct stk_tlv_builder *tlv,
const void *data, bool cr)
{
struct l_queue *fl = l_queue_new();
bool ret;
l_queue_push_tail(fl, (void *) data);
ret = build_dataobj_file_list(tlv, fl, cr);
l_queue_destroy(fl, NULL);
return ret;
}
/* Described in TS 102.223 Section 8.19 */
static bool build_dataobj_location_info(struct stk_tlv_builder *tlv,
const void *data, bool cr)
{
const struct stk_location_info *li = data;
uint8_t tag = STK_DATA_OBJECT_TYPE_LOCATION_INFO;
uint8_t mccmnc[3];
if (li->mcc[0] == '\0')
return true;
sim_encode_mcc_mnc(mccmnc, li->mcc, li->mnc);
if (!stk_tlv_builder_open_container(tlv, cr, tag, false))
return false;
if (!stk_tlv_builder_append_bytes(tlv, mccmnc, 3))
return false;
if (!stk_tlv_builder_append_short(tlv, li->lac_tac))
return false;
if (li->has_ci && !stk_tlv_builder_append_short(tlv, li->ci))
return false;
if (li->has_ext_ci && !stk_tlv_builder_append_short(tlv, li->ext_ci))
return false;
if (li->has_eutran_ci) {
if (!stk_tlv_builder_append_short(tlv, li->eutran_ci >> 12))
return false;
if (!stk_tlv_builder_append_short(tlv,
(li->eutran_ci << 4) | 0xf))
return false;
}
return stk_tlv_builder_close_container(tlv);
}
static bool build_empty_dataobj_location_info(struct stk_tlv_builder *tlv,
const void *data, bool cr)
{
uint8_t tag = STK_DATA_OBJECT_TYPE_LOCATION_INFO;
return stk_tlv_builder_open_container(tlv, cr, tag, false) &&
stk_tlv_builder_close_container(tlv);
}
/*
* Described in TS 102.223 Section 8.20
*
* See format note in parse_dataobj_imei.
*/
static bool build_dataobj_imei(struct stk_tlv_builder *tlv,
const void *data, bool cr)
{
char byte0[3];
const char *imei = data;
uint8_t tag = STK_DATA_OBJECT_TYPE_IMEI;
uint8_t value[8];
if (imei == NULL)
return true;
if (strlen(imei) != 15)
return false;
byte0[0] = '*';
byte0[1] = imei[0];
byte0[2] = '\0';
sim_encode_bcd_number(byte0, value);
sim_encode_bcd_number(imei + 1, value + 1);
return stk_tlv_builder_open_container(tlv, cr, tag, false) &&
stk_tlv_builder_append_bytes(tlv, value, 8) &&
stk_tlv_builder_close_container(tlv);
}
/* Described in TS 102.223 Section 8.21 */
static bool build_dataobj_help_request(struct stk_tlv_builder *tlv,
const void *data, bool cr)
{
const bool *help = data;
uint8_t tag = STK_DATA_OBJECT_TYPE_HELP_REQUEST;
if (*help != true)
return true;
return stk_tlv_builder_open_container(tlv, cr, tag, false) &&
stk_tlv_builder_close_container(tlv);
}
/* Described in TS 102.223 Section 8.22 */
static bool build_dataobj_network_measurement_results(
struct stk_tlv_builder *tlv,
const void *data, bool cr)
{
const struct stk_common_byte_array *nmr = data;
uint8_t tag = STK_DATA_OBJECT_TYPE_NETWORK_MEASUREMENT_RESULTS;
if (!stk_tlv_builder_open_container(tlv, cr, tag, false))
return false;
if (nmr->len > 0 && !stk_tlv_builder_append_bytes(tlv,
nmr->array, nmr->len))
return false;
return stk_tlv_builder_close_container(tlv);
}
/* Described in TS 102.223 Section 8.25 */
static bool build_dataobj_event_list(struct stk_tlv_builder *tlv,
const void *data, bool cr)
{
const struct stk_event_list *list = data;
uint8_t tag = STK_DATA_OBJECT_TYPE_EVENT_LIST;
return stk_tlv_builder_open_container(tlv, cr, tag, false) &&
stk_tlv_builder_append_bytes(tlv, list->list, list->len) &&
stk_tlv_builder_close_container(tlv);
}
/* Shortcut for a single Event type */
static bool build_dataobj_event_type(struct stk_tlv_builder *tlv,
const void *data, bool cr)
{
const struct stk_event_list list = {
.list = { *(enum stk_event_type *) data },
.len = 1,
};
return build_dataobj_event_list(tlv, &list, cr);
}
/* Described in TS 102.223 Section 8.26 */
static bool build_dataobj_cause(struct stk_tlv_builder *tlv,
const void *data, bool cr)
{
const struct stk_cause *cause = data;
uint8_t tag = STK_DATA_OBJECT_TYPE_CAUSE;
if (!cause->has_cause)
return true;
return stk_tlv_builder_open_container(tlv, cr, tag, false) &&
stk_tlv_builder_append_bytes(tlv, cause->cause, cause->len) &&
stk_tlv_builder_close_container(tlv);
}
/* Described in TS 102.223 Section 8.27 */
static bool build_dataobj_location_status(struct stk_tlv_builder *tlv,
const void *data, bool cr)
{
const enum stk_service_state *state = data;
uint8_t tag = STK_DATA_OBJECT_TYPE_LOCATION_STATUS;
return stk_tlv_builder_open_container(tlv, cr, tag, false) &&
stk_tlv_builder_append_byte(tlv, *state) &&
stk_tlv_builder_close_container(tlv);
}
/* Described in TS 131.111 Section 8.28 */
static bool build_dataobj_transaction_ids(struct stk_tlv_builder *tlv,
const void *data, bool cr)
{
const struct stk_transaction_id *id = data;
uint8_t tag = STK_DATA_OBJECT_TYPE_TRANSACTION_ID;
return stk_tlv_builder_open_container(tlv, cr, tag, false) &&
stk_tlv_builder_append_bytes(tlv, id->list, id->len) &&
stk_tlv_builder_close_container(tlv);
}
/* Shortcut for a single Transaction ID */
static bool build_dataobj_transaction_id(struct stk_tlv_builder *tlv,
const void *data, bool cr)
{
const struct stk_transaction_id ids = {
.list = { *(uint8_t *) data },
.len = 1,
};
return build_dataobj_transaction_ids(tlv, &ids, cr);
}
/* Described in 3GPP 31.111 Section 8.29 */
static bool build_dataobj_bcch_channel_list(struct stk_tlv_builder *tlv,
const void *data, bool cr)
{
const struct stk_bcch_channel_list *list = data;
uint8_t tag = STK_DATA_OBJECT_TYPE_BCCH_CHANNEL_LIST;
unsigned int i, bytes, pos, shift;
uint8_t value;
if (!list->has_list)
return true;
if (!stk_tlv_builder_open_container(tlv, cr, tag, true))
return false;
bytes = (list->num * 10 + 7) / 8;
for (i = 0; i < bytes; i++) {
pos = (i * 8 + 7) / 10;
shift = pos * 10 + 10 - i * 8 - 8;
value = 0;
if (pos < list->num)
value |= list->channels[pos] >> shift;
if (shift > 2)
value |= list->channels[pos - 1] << (10 - shift);
if (!stk_tlv_builder_append_byte(tlv, value))
return false;
}
return stk_tlv_builder_close_container(tlv);
}
/* Described in TS 102.223 Section 8.30 */
static bool build_dataobj_cc_requested_action(struct stk_tlv_builder *tlv,
const void *data, bool cr)
{
const struct stk_common_byte_array *action = data;
uint8_t tag = STK_DATA_OBJECT_TYPE_CALL_CONTROL_REQUESTED_ACTION;
if (action->array == NULL)
return true;
return stk_tlv_builder_open_container(tlv, cr, tag, false) &&
stk_tlv_builder_append_bytes(tlv, action->array, action->len) &&
stk_tlv_builder_close_container(tlv);
}
/* Described in TS 102.223 Section 8.33 */
static bool build_dataobj_card_reader_status(struct stk_tlv_builder *tlv,
const void *data, bool cr)
{
const struct stk_reader_status *status = data;
uint8_t tag = STK_DATA_OBJECT_TYPE_CARD_READER_STATUS;
uint8_t byte;
byte = status->id |
(status->removable << 3) |
(status->present << 4) |
(status->id1_size << 5) |
(status->card_present << 6) |
(status->card_powered << 7);
return stk_tlv_builder_open_container(tlv, cr, tag, false) &&
stk_tlv_builder_append_byte(tlv, byte) &&
stk_tlv_builder_close_container(tlv);
}
/* Described in TS 102.223 Section 8.37 */
static bool build_dataobj_timer_id(struct stk_tlv_builder *tlv,
const void *data, bool cr)
{
const uint8_t *id = data;
uint8_t tag = STK_DATA_OBJECT_TYPE_TIMER_ID;
if (id[0] == 0)
return true;
return stk_tlv_builder_open_container(tlv, cr, tag, false) &&
stk_tlv_builder_append_byte(tlv, id[0]) &&
stk_tlv_builder_close_container(tlv);
}
/* Described in TS 102.223 Section 8.38 */
static bool build_dataobj_timer_value(struct stk_tlv_builder *tlv,
const void *data, bool cr)
{
const struct stk_timer_value *value = data;
uint8_t tag = STK_DATA_OBJECT_TYPE_TIMER_VALUE;
if (!value->has_value)
return true;
#define TO_BCD(bin) ((((bin) / 10) & 0xf) | (((bin) % 10) << 4))
return stk_tlv_builder_open_container(tlv, cr, tag, false) &&
stk_tlv_builder_append_byte(tlv, TO_BCD(value->hour)) &&
stk_tlv_builder_append_byte(tlv, TO_BCD(value->minute)) &&
stk_tlv_builder_append_byte(tlv, TO_BCD(value->second)) &&
stk_tlv_builder_close_container(tlv);
#undef TO_BCD
}
/* Described in TS 102.223 Section 8.39 */
static bool build_dataobj_datetime_timezone(struct stk_tlv_builder *tlv,
const void *data, bool cr)
{
const struct sms_scts *scts = data;
uint8_t value[7];
int offset = 0;
uint8_t tag = STK_DATA_OBJECT_TYPE_DATETIME_TIMEZONE;
if (scts->month == 0 && scts->day == 0)
return true;
if (sms_encode_scts(scts, value, &offset) != TRUE)
return false;
return stk_tlv_builder_open_container(tlv, cr, tag, false) &&
stk_tlv_builder_append_bytes(tlv, value, 7) &&
stk_tlv_builder_close_container(tlv);
}
/* Described in TS 102.223 Section 8.41 */
static bool build_dataobj_at_response(struct stk_tlv_builder *tlv,
const void *data, bool cr)
{
uint8_t tag = STK_DATA_OBJECT_TYPE_AT_RESPONSE;
int len;
if (data == NULL)
return true;
/*
* "If the AT Response string is longer than the maximum length
* capable of being transmitted to the UICC then the AT Response
* string shall be truncated to this length by the terminal."
*/
len = strlen(data);
if (len > 240) /* Safe pick */
len = 240;
return stk_tlv_builder_open_container(tlv, cr, tag, true) &&
stk_tlv_builder_append_bytes(tlv, data, len) &&
stk_tlv_builder_close_container(tlv);
}
/* Described in TS 131.111 Section 8.42 */
static bool build_dataobj_bc_repeat(struct stk_tlv_builder *tlv,
const void *data, bool cr)
{
uint8_t tag = STK_DATA_OBJECT_TYPE_BC_REPEAT_INDICATOR;
const struct stk_bc_repeat *bcr = data;
if (!bcr->has_bc_repeat)
return true;
return stk_tlv_builder_open_container(tlv, cr, tag, true) &&
stk_tlv_builder_append_byte(tlv, bcr->value) &&
stk_tlv_builder_close_container(tlv);
}
/* Described in TS 102.223 Section 8.45 */
static bool build_dataobj_language(struct stk_tlv_builder *tlv,
const void *data, bool cr)
{
uint8_t tag = STK_DATA_OBJECT_TYPE_LANGUAGE;
if (data == NULL)
return true;
/*
* Coded as two GSM 7-bit characters with eighth bit clear. Since
* ISO 639-2 codes use only english alphabet letters, no conversion
* from UTF-8 to GSM is needed.
*/
return stk_tlv_builder_open_container(tlv, cr, tag, false) &&
stk_tlv_builder_append_bytes(tlv, data, 2) &&
stk_tlv_builder_close_container(tlv);
}
/* Described in 3GPP TS 31.111 Section 8.46 */
static bool build_dataobj_timing_advance(struct stk_tlv_builder *tlv,
const void *data, bool cr)
{
const struct stk_timing_advance *tadv = data;
uint8_t tag = STK_DATA_OBJECT_TYPE_TIMING_ADVANCE;
return stk_tlv_builder_open_container(tlv, cr, tag, false) &&
stk_tlv_builder_append_byte(tlv, tadv->status) &&
stk_tlv_builder_append_byte(tlv, tadv->advance) &&
stk_tlv_builder_close_container(tlv);
}
/* Described in TS 102.223 Section 8.51 */
static bool build_dataobj_browser_termination_cause(
struct stk_tlv_builder *tlv,
const void *data, bool cr)
{
const enum stk_browser_termination_cause *cause = data;
uint8_t tag = STK_DATA_OBJECT_TYPE_BROWSER_TERMINATION_CAUSE;
return stk_tlv_builder_open_container(tlv, cr, tag, false) &&
stk_tlv_builder_append_byte(tlv, *cause) &&
stk_tlv_builder_close_container(tlv);
}
/* Described in TS 102.223 Section 8.52 */
static bool build_dataobj_bearer_description(struct stk_tlv_builder *tlv,
const void *data, bool cr)
{
const struct stk_bearer_description *bd = data;
uint8_t tag = STK_DATA_OBJECT_TYPE_BEARER_DESCRIPTION;
if (bd->type != STK_BEARER_TYPE_GPRS_UTRAN)
return true;
return stk_tlv_builder_open_container(tlv, cr, tag, false) &&
stk_tlv_builder_append_byte(tlv, bd->type) &&
stk_tlv_builder_append_byte(tlv,
bd->gprs.precedence) &&
stk_tlv_builder_append_byte(tlv,
bd->gprs.delay) &&
stk_tlv_builder_append_byte(tlv,
bd->gprs.reliability) &&
stk_tlv_builder_append_byte(tlv,
bd->gprs.peak) &&
stk_tlv_builder_append_byte(tlv,
bd->gprs.mean) &&
stk_tlv_builder_append_byte(tlv,
bd->gprs.pdp_type) &&
stk_tlv_builder_close_container(tlv);
}
/* Described in TS 102.223 Section 8.53 */
static bool build_dataobj_channel_data(struct stk_tlv_builder *tlv,
const void *data, bool cr)
{
const struct stk_common_byte_array *cd = data;
uint8_t tag = STK_DATA_OBJECT_TYPE_CHANNEL_DATA;
return stk_tlv_builder_open_container(tlv, cr, tag, true) &&
stk_tlv_builder_append_bytes(tlv, cd->array, cd->len) &&
stk_tlv_builder_close_container(tlv);
}
/* Described in TS 102.223 Section 8.54 */
static bool build_dataobj_channel_data_length(
struct stk_tlv_builder *tlv,
const void *data, bool cr)
{
const uint16_t *length = data;
uint8_t tag = STK_DATA_OBJECT_TYPE_CHANNEL_DATA_LENGTH;
return stk_tlv_builder_open_container(tlv, cr, tag, false) &&
stk_tlv_builder_append_byte(tlv, MIN(*length, 255)) &&
stk_tlv_builder_close_container(tlv);
}
/* Described in TS 102.223 Section 8.55 */
static bool build_dataobj_buffer_size(struct stk_tlv_builder *tlv,
const void *data, bool cr)
{
const uint16_t *buf_size = data;
uint8_t tag = STK_DATA_OBJECT_TYPE_BUFFER_SIZE;
return stk_tlv_builder_open_container(tlv, cr, tag, false) &&
stk_tlv_builder_append_short(tlv, *buf_size) &&
stk_tlv_builder_close_container(tlv);
}
/* Described in TS 102.223 Section 8.56 */
static bool build_dataobj_channel_status(struct stk_tlv_builder *tlv,
const void *data, bool cr)
{
const struct stk_channel *channel = data;
uint8_t tag = STK_DATA_OBJECT_TYPE_CHANNEL_STATUS;
uint8_t byte[2];
switch (channel->status) {
case STK_CHANNEL_PACKET_DATA_SERVICE_NOT_ACTIVATED:
case STK_CHANNEL_TCP_IN_CLOSED_STATE:
byte[0] = channel->id;
byte[1] = 0x00;
break;
case STK_CHANNEL_PACKET_DATA_SERVICE_ACTIVATED:
case STK_CHANNEL_TCP_IN_ESTABLISHED_STATE:
byte[0] = channel->id | 0x80;
byte[1] = 0x00;
break;
case STK_CHANNEL_TCP_IN_LISTEN_STATE:
byte[0] = channel->id | 0x40;
byte[1] = 0x00;
break;
case STK_CHANNEL_LINK_DROPPED:
byte[0] = channel->id;
byte[1] = 0x05;
break;
}
return stk_tlv_builder_open_container(tlv, cr, tag, false) &&
stk_tlv_builder_append_bytes(tlv, byte, 2) &&
stk_tlv_builder_close_container(tlv);
}
/* Described in TS 102.223 Section 8.58 */
static bool build_dataobj_other_address(struct stk_tlv_builder *tlv,
const void *data, bool cr)
{
const struct stk_other_address *addr = data;
uint8_t tag = STK_DATA_OBJECT_TYPE_OTHER_ADDRESS;
bool ok = false;
if (!addr->type)
return true;
if (!stk_tlv_builder_open_container(tlv, cr, tag, false))
return false;
switch (addr->type) {
case STK_ADDRESS_AUTO:
ok = true;
break;
case STK_ADDRESS_IPV4:
ok = stk_tlv_builder_append_byte(tlv, addr->type) &&
stk_tlv_builder_append_bytes(tlv,
(const uint8_t *) &addr->addr.ipv4, 4);
break;
case STK_ADDRESS_IPV6:
ok = stk_tlv_builder_append_byte(tlv, addr->type) &&
stk_tlv_builder_append_bytes(tlv, addr->addr.ipv6, 16);
break;
}
if (!ok)
return false;
return stk_tlv_builder_close_container(tlv);
}
/* Described in TS 102.223 Section 8.59 */
static bool build_dataobj_uicc_te_interface(struct stk_tlv_builder *tlv,
const void *data, bool cr)
{
const struct stk_uicc_te_interface *iface = data;
uint8_t tag = STK_DATA_OBJECT_TYPE_UICC_TE_INTERFACE;
if (iface->protocol == 0 && iface->port == 0)
return true;
return stk_tlv_builder_open_container(tlv, cr, tag, false) &&
stk_tlv_builder_append_byte(tlv, iface->protocol) &&
stk_tlv_builder_append_short(tlv, iface->port) &&
stk_tlv_builder_close_container(tlv);
}
/* Described in TS 102.223 Section 8.61 */
static bool build_dataobj_access_technologies(struct stk_tlv_builder *tlv,
const void *data, bool cr)
{
const struct stk_access_technologies *techs = data;
uint8_t tag = STK_DATA_OBJECT_TYPE_ACCESS_TECHNOLOGY;
int i;
if (!stk_tlv_builder_open_container(tlv, cr, tag, false))
return false;
for (i = 0; i < techs->length; i++)
if (!stk_tlv_builder_append_byte(tlv, techs->techs[i]))
return false;
return stk_tlv_builder_close_container(tlv);
}
/* Shortcut for a single Access Technology */
static bool build_dataobj_access_technology(struct stk_tlv_builder *tlv,
const void *data, bool cr)
{
const struct stk_access_technologies techs = {
.techs = data,
.length = 1,
};
return build_dataobj_access_technologies(tlv, &techs, cr);
}
/* Described in TS 102.223 Section 8.62 */
static bool build_dataobj_display_parameters(struct stk_tlv_builder *tlv,
const void *data, bool cr)
{
const struct stk_display_parameters *params = data;
uint8_t tag = STK_DATA_OBJECT_TYPE_DISPLAY_PARAMETERS;
return stk_tlv_builder_open_container(tlv, cr, tag, false) &&
stk_tlv_builder_append_byte(tlv, params->height) &&
stk_tlv_builder_append_byte(tlv, params->width) &&
stk_tlv_builder_append_byte(tlv, params->effects) &&
stk_tlv_builder_close_container(tlv);
}
/* Described in TS 102.223 Section 8.63 */
static bool build_dataobj_service_record(struct stk_tlv_builder *tlv,
const void *data, bool cr)
{
const struct stk_service_record *rec = data;
uint8_t tag = STK_DATA_OBJECT_TYPE_SERVICE_RECORD;
return stk_tlv_builder_open_container(tlv, cr, tag, true) &&
stk_tlv_builder_append_byte(tlv, rec->tech_id) &&
stk_tlv_builder_append_byte(tlv, rec->serv_id) &&
stk_tlv_builder_append_bytes(tlv, rec->serv_rec, rec->len) &&
stk_tlv_builder_close_container(tlv);
}
/* Described in TS 102.223 Section 8.68 */
static bool build_dataobj_remote_entity_address(struct stk_tlv_builder *tlv,
const void *data, bool cr)
{
const struct stk_remote_entity_address *addr = data;
uint8_t tag = STK_DATA_OBJECT_TYPE_REMOTE_ENTITY_ADDRESS;
bool ok = false;
if (!addr->has_address)
return true;
if (!stk_tlv_builder_open_container(tlv, cr, tag, true))
return false;
if (!stk_tlv_builder_append_byte(tlv, addr->coding_type))
return false;
switch (addr->coding_type) {
case 0x00:
ok = stk_tlv_builder_append_bytes(tlv, addr->addr.ieee802, 6);
break;
case 0x01:
ok = stk_tlv_builder_append_bytes(tlv, addr->addr.irda, 4);
break;
}
if (!ok)
return false;
return stk_tlv_builder_close_container(tlv);
}
/* Described in TS 102.223 Section 8.69 */
static bool build_dataobj_esn(struct stk_tlv_builder *tlv,
const void *data, bool cr)
{
const uint32_t *esn = data;
uint8_t tag = STK_DATA_OBJECT_TYPE_ESN;
return stk_tlv_builder_open_container(tlv, cr, tag, false) &&
stk_tlv_builder_append_short(tlv, *esn >> 16) &&
stk_tlv_builder_append_short(tlv, *esn >> 0) &&
stk_tlv_builder_close_container(tlv);
}
/* Described in TS 131.111 Section 8.72, 3GPP 24.008 Section 9.5.7 */
static bool build_dataobj_pdp_context_params(struct stk_tlv_builder *tlv,
const void *data, bool cr)
{
const struct stk_common_byte_array *params = data;
uint8_t tag = STK_DATA_OBJECT_TYPE_PDP_ACTIVATION_PARAMETER;
if (params->len < 1)
return true;
if (params->len > 0x7f)
return false;
return stk_tlv_builder_open_container(tlv, cr, tag, false) &&
stk_tlv_builder_append_bytes(tlv, params->array, params->len) &&
stk_tlv_builder_close_container(tlv);
}
/*
* Described in TS 102.223 Section 8.74
*
* See format note in parse_dataobj_imeisv.
*/
static bool build_dataobj_imeisv(struct stk_tlv_builder *tlv,
const void *data, bool cr)
{
char byte0[3];
const char *imeisv = data;
uint8_t value[9];
uint8_t tag = STK_DATA_OBJECT_TYPE_IMEISV;
if (imeisv == NULL)
return true;
if (strlen(imeisv) != 16)
return false;
byte0[0] = '3';
byte0[1] = imeisv[0];
byte0[2] = '\0';
sim_encode_bcd_number(byte0, value);
sim_encode_bcd_number(imeisv + 1, value + 1);
return stk_tlv_builder_open_container(tlv, cr, tag, false) &&
stk_tlv_builder_append_bytes(tlv, value, 9) &&
stk_tlv_builder_close_container(tlv);
}
/* Described in TS 102.223 Section 8.75 */
static bool build_dataobj_network_search_mode(struct stk_tlv_builder *tlv,
const void *data, bool cr)
{
const enum stk_network_search_mode *mode = data;
uint8_t tag = STK_DATA_OBJECT_TYPE_NETWORK_SEARCH_MODE;
return stk_tlv_builder_open_container(tlv, cr, tag, false) &&
stk_tlv_builder_append_byte(tlv, *mode) &&
stk_tlv_builder_close_container(tlv);
}
/* Described in TS 102.223 Section 8.76 */
static bool build_dataobj_battery_state(struct stk_tlv_builder *tlv,
const void *data, bool cr)
{
const enum stk_battery_state *state = data;
uint8_t tag = STK_DATA_OBJECT_TYPE_BATTERY_STATE;
return stk_tlv_builder_open_container(tlv, cr, tag, false) &&
stk_tlv_builder_append_byte(tlv, *state) &&
stk_tlv_builder_close_container(tlv);
}
/* Described in TS 102.223 Section 8.77 */
static bool build_dataobj_browsing_status(struct stk_tlv_builder *tlv,
const void *data, bool cr)
{
const struct stk_common_byte_array *bs = data;
uint8_t tag = STK_DATA_OBJECT_TYPE_BROWSING_STATUS;
return stk_tlv_builder_open_container(tlv, cr, tag, true) &&
stk_tlv_builder_append_bytes(tlv, bs->array, bs->len) &&
stk_tlv_builder_close_container(tlv);
}
/* Described in TS 102.223 Section 8.79 */
static bool build_dataobj_frames_information(struct stk_tlv_builder *tlv,
const void *data, bool cr)
{
const struct stk_frames_info *info = data;
uint8_t tag = STK_DATA_OBJECT_TYPE_FRAMES_INFO;
unsigned int i;
if (!stk_tlv_builder_open_container(tlv, cr, tag, false))
return false;
if (!stk_tlv_builder_append_byte(tlv, info->id))
return false;
for (i = 0; i < info->len; i++) {
if (!stk_tlv_builder_append_byte(tlv, info->list[i].height))
return false;
if (!stk_tlv_builder_append_byte(tlv, info->list[i].width))
return false;
}
return stk_tlv_builder_close_container(tlv);
}
/* Described in TS 102.223 Section 8.81 */
static bool build_dataobj_meid(struct stk_tlv_builder *tlv,
const void *data, bool cr)
{
const char *meid = data;
uint8_t value[8];
uint8_t tag = STK_DATA_OBJECT_TYPE_MEID;
if (meid == NULL)
return true;
if (strlen(meid) != 16)
return false;
sim_encode_bcd_number(meid, value);
return stk_tlv_builder_open_container(tlv, cr, tag, false) &&
stk_tlv_builder_append_bytes(tlv, value, 8) &&
stk_tlv_builder_close_container(tlv);
}
/* Described in TS 102.223 Section 8.83 */
static bool build_dataobj_mms_id(struct stk_tlv_builder *tlv,
const void *data, bool cr)
{
const struct stk_mms_id *id = data;
uint8_t tag = STK_DATA_OBJECT_TYPE_MMS_ID;
/* Assume the length is never 0 for a valid ID, however the whole
* data object's presence is conditional. */
if (id->len == 0)
return true;
return stk_tlv_builder_open_container(tlv, cr, tag, false) &&
stk_tlv_builder_append_bytes(tlv, id->id, id->len) &&
stk_tlv_builder_close_container(tlv);
}
/* Described in TS 102.223 Section 8.84 */
static bool build_dataobj_mms_transfer_status(struct stk_tlv_builder *tlv,
const void *data, bool cr)
{
const struct stk_mms_transfer_status *mts = data;
uint8_t tag = STK_DATA_OBJECT_TYPE_MMS_TRANSFER_STATUS;
/*
* Assume the length is never 0 for a valid Result message, however
* the whole data object's presence is conditional.
*/
if (mts->len == 0)
return true;
return stk_tlv_builder_open_container(tlv, cr, tag, false) &&
stk_tlv_builder_append_bytes(tlv, mts->status, mts->len) &&
stk_tlv_builder_close_container(tlv);
}
/* Described in TS 131.111 Section 8.84 */
static bool build_dataobj_i_wlan_access_status(struct stk_tlv_builder *tlv,
const void *data, bool cr)
{
const enum stk_i_wlan_access_status *status = data;
uint8_t tag = STK_DATA_OBJECT_TYPE_I_WLAN_ACCESS_STATUS;
return stk_tlv_builder_open_container(tlv, cr, tag, false) &&
stk_tlv_builder_append_byte(tlv, *status) &&
stk_tlv_builder_close_container(tlv);
}
/* Described in TS 102.223 Section 8.86 */
static bool build_dataobj_mms_notification(struct stk_tlv_builder *tlv,
const void *data, bool cr)
{
const struct stk_common_byte_array *msg = data;
uint8_t tag = STK_DATA_OBJECT_TYPE_MMS_NOTIFICATION;
return stk_tlv_builder_open_container(tlv, cr, tag, true) &&
stk_tlv_builder_append_bytes(tlv, msg->array, msg->len) &&
stk_tlv_builder_close_container(tlv);
}
/* Described in TS 102.223 Section 8.87 */
static bool build_dataobj_last_envelope(struct stk_tlv_builder *tlv,
const void *data, bool cr)
{
const ofono_bool_t *last = data;
uint8_t tag = STK_DATA_OBJECT_TYPE_LAST_ENVELOPE;
if (!*last)
return true;
return stk_tlv_builder_open_container(tlv, cr, tag, false) &&
stk_tlv_builder_close_container(tlv);
}
/* Described in TS 102.223 Section 8.88 */
static bool build_dataobj_registry_application_data(
struct stk_tlv_builder *tlv,
const void *data, bool cr)
{
const struct stk_registry_application_data *rad = data;
uint8_t tag = STK_DATA_OBJECT_TYPE_REGISTRY_APPLICATION_DATA;
uint8_t dcs;
L_AUTO_FREE_VAR(uint8_t *, name);
size_t len;
long gsmlen;
name = convert_utf8_to_gsm(rad->name, -1, NULL, &gsmlen, 0);
len = gsmlen;
dcs = 0x04;
if (!name) {
name = l_utf8_to_ucs2be(rad->name, &len);
if (!name)
return false;
/* len includes null terminator, so strip it */
len -= 2;
dcs = 0x08;
}
return stk_tlv_builder_open_container(tlv, cr, tag, true) &&
stk_tlv_builder_append_short(tlv, rad->port) &&
stk_tlv_builder_append_byte(tlv, dcs) &&
stk_tlv_builder_append_byte(tlv, rad->type) &&
stk_tlv_builder_append_bytes(tlv, name, len) &&
stk_tlv_builder_close_container(tlv);
}
/* Described in TS 102.223 Section 8.90 */
static bool build_dataobj_broadcast_network_information(
struct stk_tlv_builder *tlv,
const void *data, bool cr)
{
const struct stk_broadcast_network_information *bni = data;
uint8_t tag = STK_DATA_OBJECT_TYPE_BROADCAST_NETWORK_INFO;
return stk_tlv_builder_open_container(tlv, cr, tag, false) &&
stk_tlv_builder_append_byte(tlv, bni->tech) &&
stk_tlv_builder_append_bytes(tlv, bni->loc_info, bni->len) &&
stk_tlv_builder_close_container(tlv);
}
/* Described in TS 131.111 Section 8.91 / 3GPP 24.008 Section 10.5.5.15 */
static bool build_dataobj_routing_area_id(struct stk_tlv_builder *tlv,
const void *data, bool cr)
{
const struct stk_routing_area_info *rai = data;
uint8_t tag = STK_DATA_OBJECT_TYPE_ROUTING_AREA_INFO;
uint8_t mccmnc[3];
if (rai->mcc[0] == 0)
return true;
sim_encode_mcc_mnc(mccmnc, rai->mcc, rai->mnc);
return stk_tlv_builder_open_container(tlv, cr, tag, false) &&
stk_tlv_builder_append_bytes(tlv, mccmnc, 3) &&
stk_tlv_builder_append_short(tlv, rai->lac) &&
stk_tlv_builder_append_byte(tlv, rai->rac) &&
stk_tlv_builder_close_container(tlv);
}
/* Described in TS 131.111 Section 8.92 */
static bool build_dataobj_update_attach_type(struct stk_tlv_builder *tlv,
const void *data, bool cr)
{
const enum stk_update_attach_type *type = data;
uint8_t tag = STK_DATA_OBJECT_TYPE_UPDATE_ATTACH_TYPE;
return stk_tlv_builder_open_container(tlv, cr, tag, false) &&
stk_tlv_builder_append_byte(tlv, *type) &&
stk_tlv_builder_close_container(tlv);
}
/* Described in TS 131.111 Section 8.93 */
static bool build_dataobj_rejection_cause_code(struct stk_tlv_builder *tlv,
const void *data, bool cr)
{
const enum stk_rejection_cause_code *cause = data;
uint8_t tag = STK_DATA_OBJECT_TYPE_REJECTION_CAUSE_CODE;
return stk_tlv_builder_open_container(tlv, cr, tag, false) &&
stk_tlv_builder_append_byte(tlv, *cause) &&
stk_tlv_builder_close_container(tlv);
}
/* Described in TS 131.111 Section 8.98, 3GPP 24.301 Section 6.5.1 */
static bool build_dataobj_eps_pdn_conn_params(struct stk_tlv_builder *tlv,
const void *data, bool cr)
{
const struct stk_common_byte_array *params = data;
uint8_t tag = STK_DATA_OBJECT_TYPE_EPS_PDN_CONN_ACTIVATION_REQ;
if (params->len < 1)
return true;
if (params->len > 0x7f)
return false;
return stk_tlv_builder_open_container(tlv, cr, tag, false) &&
stk_tlv_builder_append_bytes(tlv, params->array, params->len) &&
stk_tlv_builder_close_container(tlv);
}
/* Described in TS 131.111 Section 8.99 / 3GPP 24.301 Section 9.9.3.32 */
static bool build_dataobj_tracking_area_id(struct stk_tlv_builder *tlv,
const void *data, bool cr)
{
const struct stk_tracking_area_id *tai = data;
uint8_t tag = STK_DATA_OBJECT_TYPE_TRACKING_AREA_ID;
uint8_t mccmnc[3];
if (tai->mcc[0] == 0)
return true;
sim_encode_mcc_mnc(mccmnc, tai->mcc, tai->mnc);
return stk_tlv_builder_open_container(tlv, cr, tag, false) &&
stk_tlv_builder_append_bytes(tlv, mccmnc, 3) &&
stk_tlv_builder_append_short(tlv, tai->tac) &&
stk_tlv_builder_close_container(tlv);
}
static bool build_dataobj(struct stk_tlv_builder *tlv,
dataobj_writer builder_func, ...)
{
va_list args;
va_start(args, builder_func);
while (builder_func) {
unsigned int flags = va_arg(args, enum stk_data_object_flag);
const void *data = va_arg(args, const void *);
bool cr = (flags & DATAOBJ_FLAG_CR) ? true : false;
if (!builder_func(tlv, data, cr)) {
va_end(args);
return false;
}
builder_func = va_arg(args, dataobj_writer);
}
va_end(args);
return true;
}
static bool build_setup_call(struct stk_tlv_builder *builder,
const struct stk_response *response)
{
if (response->set_up_call.modified_result.cc_modified)
return build_dataobj(builder,
build_dataobj_cc_requested_action,
DATAOBJ_FLAG_CR,
&response->set_up_call.cc_requested_action,
build_dataobj_result,
DATAOBJ_FLAG_CR,
&response->set_up_call.modified_result.result,
NULL);
else
return build_dataobj(builder,
build_dataobj_cc_requested_action,
DATAOBJ_FLAG_CR,
&response->set_up_call.cc_requested_action,
NULL);
}
static bool build_local_info(struct stk_tlv_builder *builder,
const struct stk_response *response)
{
const struct stk_response_local_info *info =
&response->provide_local_info;
int i;
switch (response->qualifier) {
case 0x00: /* Location Information according to current NAA */
return build_dataobj(builder,
build_dataobj_location_info,
DATAOBJ_FLAG_CR, &info->location,
NULL);
case 0x01: /* IMEI of the terminal */
return build_dataobj(builder,
build_dataobj_imei,
DATAOBJ_FLAG_CR, info->imei,
NULL);
case 0x02: /* Network Measurement results according to current NAA */
return build_dataobj(builder,
build_dataobj_network_measurement_results,
DATAOBJ_FLAG_CR, &info->nmr.nmr,
build_dataobj_bcch_channel_list,
DATAOBJ_FLAG_CR, &info->nmr.bcch_ch_list,
NULL);
case 0x03: /* Date, time and time zone */
return build_dataobj(builder,
build_dataobj_datetime_timezone,
DATAOBJ_FLAG_CR, &info->datetime,
NULL);
case 0x04: /* Language setting */
return build_dataobj(builder,
build_dataobj_language,
DATAOBJ_FLAG_CR, info->language,
NULL);
case 0x05: /* Timing Advance */
return build_dataobj(builder,
build_dataobj_timing_advance,
DATAOBJ_FLAG_CR, &info->tadv,
NULL);
case 0x06: /* Access Technology (single access technology) */
return build_dataobj(builder,
build_dataobj_access_technology,
0, &info->access_technology,
NULL);
case 0x07: /* ESN of the terminal */
return build_dataobj(builder,
build_dataobj_esn,
DATAOBJ_FLAG_CR, &info->esn,
NULL);
case 0x08: /* IMEISV of the terminal */
return build_dataobj(builder,
build_dataobj_imeisv,
DATAOBJ_FLAG_CR, info->imeisv,
NULL);
case 0x09: /* Search Mode */
return build_dataobj(builder,
build_dataobj_network_search_mode,
0, &info->search_mode,
NULL);
case 0x0a: /* Charge State of the Battery */
return build_dataobj(builder,
build_dataobj_battery_state,
DATAOBJ_FLAG_CR, &info->battery_charge,
NULL);
case 0x0b: /* MEID of the terminal */
return build_dataobj(builder,
build_dataobj_meid,
0, info->meid,
NULL);
case 0x0d: /* Broadcast Network Information according to current tech */
return build_dataobj(builder,
build_dataobj_broadcast_network_information,
0, &info->broadcast_network_info,
NULL);
case 0x0e: /* Multiple Access Technologies */
return build_dataobj(builder,
build_dataobj_access_technologies,
0, &info->access_technologies,
NULL);
case 0x0f: /* Location Information for multiple NAAs */
if (!build_dataobj(builder,
build_dataobj_access_technologies,
0, &info->location_infos.access_techs,
NULL))
return false;
for (i = 0; i < info->location_infos.access_techs.length; i++) {
dataobj_writer location = build_dataobj_location_info;
/*
* "If no location information is available for an
* access technology, the respective data object
* shall have length zero."
*/
if (info->location_infos.locations[i].mcc[0] == '\0')
location = build_empty_dataobj_location_info;
if (!build_dataobj(builder,
location,
0, &info->location_infos.locations[i],
NULL))
return false;
}
return true;
case 0x10: /* Network Measurement results for multiple NAAs */
if (!build_dataobj(builder,
build_dataobj_access_technologies,
0, &info->nmrs.access_techs,
NULL))
return false;
for (i = 0; i < info->nmrs.access_techs.length; i++)
if (!build_dataobj(builder,
build_dataobj_network_measurement_results,
0, &info->nmrs.nmrs[i].nmr,
build_dataobj_bcch_channel_list,
0, &info->nmrs.nmrs[i].bcch_ch_list,
NULL))
return false;
return true;
}
return false;
}
static bool build_open_channel(struct stk_tlv_builder *builder,
const struct stk_response *response)
{
const struct stk_response_open_channel *open_channel =
&response->open_channel;
/* insert channel identifier only in case of success */
if (response->result.type == STK_RESULT_TYPE_SUCCESS) {
if (!build_dataobj(builder,
build_dataobj_channel_status,
0, &open_channel->channel,
NULL))
return false;
}
return build_dataobj(builder,
build_dataobj_bearer_description,
0, &open_channel->bearer_desc,
build_dataobj_buffer_size,
0, &open_channel->buf_size,
NULL);
}
static bool build_receive_data(struct stk_tlv_builder *builder,
const struct stk_response *response)
{
const struct stk_response_receive_data *receive_data =
&response->receive_data;
if (response->result.type != STK_RESULT_TYPE_SUCCESS &&
response->result.type != STK_RESULT_TYPE_MISSING_INFO)
return true;
if (receive_data->rx_data.len) {
if (!build_dataobj(builder,
build_dataobj_channel_data,
DATAOBJ_FLAG_CR,
&response->receive_data.rx_data,
NULL))
return false;
}
return build_dataobj(builder,
build_dataobj_channel_data_length,
DATAOBJ_FLAG_CR,
&response->receive_data.rx_remaining,
NULL);
}
static bool build_send_data(struct stk_tlv_builder *builder,
const struct stk_response *response)
{
if (response->result.type != STK_RESULT_TYPE_SUCCESS)
return true;
return build_dataobj(builder,
build_dataobj_channel_data_length,
DATAOBJ_FLAG_CR,
&response->send_data.tx_avail,
NULL);
}
const uint8_t *stk_pdu_from_response(const struct stk_response *response,
unsigned int *out_length)
{
struct stk_tlv_builder builder;
bool ok = true;
uint8_t tag;
static uint8_t pdu[512];
stk_tlv_builder_init(&builder, pdu, sizeof(pdu));
/*
* Encode command details, they come in order with
* Command Details TLV first, followed by Device Identities TLV
* and the Result TLV. Comprehension required everywhere.
*/
tag = STK_DATA_OBJECT_TYPE_COMMAND_DETAILS;
if (!stk_tlv_builder_open_container(&builder, true, tag, false))
return NULL;
if (!stk_tlv_builder_append_byte(&builder, response->number))
return NULL;
if (!stk_tlv_builder_append_byte(&builder, response->type))
return NULL;
if (!stk_tlv_builder_append_byte(&builder, response->qualifier))
return NULL;
if (!stk_tlv_builder_close_container(&builder))
return NULL;
/*
* TS 102 223 section 6.8 states:
* "For all COMPREHENSION-TLV objects with Min = N, the terminal
* should set the CR flag to comprehension not required."
* All the data objects except "Command Details" and "Result" have
* Min = N.
*
* However comprehension required is set for many of the TLVs in
* TS 102 384 conformance tests so we set it per command and per
* data object type.
*/
tag = STK_DATA_OBJECT_TYPE_DEVICE_IDENTITIES;
if (!stk_tlv_builder_open_container(&builder, true, tag, false))
return NULL;
if (!stk_tlv_builder_append_byte(&builder, response->src))
return NULL;
if (!stk_tlv_builder_append_byte(&builder, response->dst))
return NULL;
if (!stk_tlv_builder_close_container(&builder))
return NULL;
if (!build_dataobj_result(&builder, &response->result, true))
return NULL;
switch (response->type) {
case STK_COMMAND_TYPE_DISPLAY_TEXT:
break;
case STK_COMMAND_TYPE_GET_INKEY:
ok = build_dataobj(&builder,
build_dataobj_text, DATAOBJ_FLAG_CR,
&response->get_inkey.text,
build_dataobj_duration, 0,
&response->get_inkey.duration,
NULL);
break;
case STK_COMMAND_TYPE_GET_INPUT:
ok = build_dataobj(&builder,
build_dataobj_text, DATAOBJ_FLAG_CR,
&response->get_input.text,
NULL);
break;
case STK_COMMAND_TYPE_MORE_TIME:
case STK_COMMAND_TYPE_SEND_SMS:
case STK_COMMAND_TYPE_PLAY_TONE:
break;
case STK_COMMAND_TYPE_POLL_INTERVAL:
ok = build_dataobj(&builder,
build_dataobj_duration, DATAOBJ_FLAG_CR,
&response->poll_interval.max_interval,
NULL);
break;
case STK_COMMAND_TYPE_REFRESH:
case STK_COMMAND_TYPE_SETUP_MENU:
break;
case STK_COMMAND_TYPE_SELECT_ITEM:
ok = build_dataobj(&builder,
build_dataobj_item_id, DATAOBJ_FLAG_CR,
&response->select_item.item_id,
NULL);
break;
case STK_COMMAND_TYPE_SEND_SS:
break;
case STK_COMMAND_TYPE_SETUP_CALL:
ok = build_setup_call(&builder, response);
break;
case STK_COMMAND_TYPE_POLLING_OFF:
break;
case STK_COMMAND_TYPE_PROVIDE_LOCAL_INFO:
ok = build_local_info(&builder, response);
break;
case STK_COMMAND_TYPE_SETUP_EVENT_LIST:
break;
case STK_COMMAND_TYPE_TIMER_MANAGEMENT:
ok = build_dataobj(&builder,
build_dataobj_timer_id,
DATAOBJ_FLAG_CR,
&response->timer_mgmt.id,
build_dataobj_timer_value,
DATAOBJ_FLAG_CR,
&response->timer_mgmt.value,
NULL);
break;
case STK_COMMAND_TYPE_SETUP_IDLE_MODE_TEXT:
break;
case STK_COMMAND_TYPE_RUN_AT_COMMAND:
ok = build_dataobj(&builder,
build_dataobj_at_response,
DATAOBJ_FLAG_CR,
response->run_at_command.at_response,
NULL);
break;
case STK_COMMAND_TYPE_SEND_DTMF:
case STK_COMMAND_TYPE_LANGUAGE_NOTIFICATION:
case STK_COMMAND_TYPE_LAUNCH_BROWSER:
case STK_COMMAND_TYPE_CLOSE_CHANNEL:
break;
case STK_COMMAND_TYPE_SEND_USSD:
ok = build_dataobj(&builder,
build_dataobj_ussd_text,
DATAOBJ_FLAG_CR,
&response->send_ussd.text,
NULL);
break;
case STK_COMMAND_TYPE_OPEN_CHANNEL:
ok = build_open_channel(&builder, response);
break;
case STK_COMMAND_TYPE_RECEIVE_DATA:
ok = build_receive_data(&builder, response);
break;
case STK_COMMAND_TYPE_SEND_DATA:
ok = build_send_data(&builder, response);
break;
case STK_COMMAND_TYPE_GET_CHANNEL_STATUS:
ok = build_dataobj(&builder,
build_dataobj_channel_status,
DATAOBJ_FLAG_CR,
&response->channel_status.channel,
NULL);
break;
default:
return NULL;
};
if (!ok)
return NULL;
if (out_length)
*out_length = stk_tlv_builder_get_length(&builder);
return pdu;
}
/* Described in TS 102.223 Section 8.7 */
static bool build_envelope_dataobj_device_ids(struct stk_tlv_builder *tlv,
const void *data, bool cr)
{
const struct stk_envelope *envelope = data;
uint8_t tag = STK_DATA_OBJECT_TYPE_DEVICE_IDENTITIES;
return stk_tlv_builder_open_container(tlv, cr, tag, false) &&
stk_tlv_builder_append_byte(tlv, envelope->src) &&
stk_tlv_builder_append_byte(tlv, envelope->dst) &&
stk_tlv_builder_close_container(tlv);
}
static bool build_envelope_call_control(
struct stk_tlv_builder *builder,
const struct stk_envelope *envelope)
{
const struct stk_envelope_call_control *cc = &envelope->call_control;
bool ok = false;
if (!build_dataobj(builder,
build_envelope_dataobj_device_ids,
DATAOBJ_FLAG_CR, envelope, NULL))
return false;
switch (cc->type) {
case STK_CC_TYPE_CALL_SETUP:
ok = build_dataobj(builder,
build_dataobj_address,
DATAOBJ_FLAG_CR, &cc->address, NULL);
break;
case STK_CC_TYPE_SUPPLEMENTARY_SERVICE:
ok = build_dataobj(builder,
build_dataobj_ss_string,
DATAOBJ_FLAG_CR, &cc->ss_string, NULL);
break;
case STK_CC_TYPE_USSD_OP:
ok = build_dataobj(builder,
build_dataobj_ussd_string,
DATAOBJ_FLAG_CR, &cc->ussd_string,
NULL);
break;
case STK_CC_TYPE_PDP_CTX_ACTIVATION:
ok = build_dataobj(builder,
build_dataobj_pdp_context_params,
DATAOBJ_FLAG_CR, &cc->pdp_ctx_params,
NULL);
break;
case STK_CC_TYPE_EPS_PDN_CONNECTION_ACTIVATION:
ok = build_dataobj(builder,
build_dataobj_eps_pdn_conn_params,
DATAOBJ_FLAG_CR, &cc->eps_pdn_params,
NULL);
break;
}
if (!ok)
return false;
return build_dataobj(builder,
build_dataobj_ccp, 0, &cc->ccp1,
build_dataobj_subaddress, 0, &cc->subaddress,
build_dataobj_location_info, 0, &cc->location,
build_dataobj_ccp, 0, &cc->ccp2,
build_dataobj_alpha_id, 0, cc->alpha_id,
build_dataobj_bc_repeat, 0, &cc->bc_repeat,
NULL);
}
static bool build_envelope_event_download(struct stk_tlv_builder *builder,
const struct stk_envelope *envelope)
{
const struct stk_envelope_event_download *evt =
&envelope->event_download;
if (!build_dataobj(builder,
build_dataobj_event_type, DATAOBJ_FLAG_CR,
&evt->type,
build_envelope_dataobj_device_ids,
DATAOBJ_FLAG_CR,
envelope,
NULL))
return false;
switch (evt->type) {
case STK_EVENT_TYPE_MT_CALL:
return build_dataobj(builder,
build_dataobj_transaction_id,
DATAOBJ_FLAG_CR,
&evt->mt_call.transaction_id,
build_dataobj_address, 0,
&evt->mt_call.caller_address,
build_dataobj_subaddress, 0,
&evt->mt_call.caller_subaddress,
NULL);
case STK_EVENT_TYPE_CALL_CONNECTED:
return build_dataobj(builder,
build_dataobj_transaction_id,
DATAOBJ_FLAG_CR,
&evt->call_connected.transaction_id,
NULL);
case STK_EVENT_TYPE_CALL_DISCONNECTED:
return build_dataobj(builder,
build_dataobj_transaction_ids,
DATAOBJ_FLAG_CR,
&evt->call_disconnected.transaction_ids,
build_dataobj_cause, 0,
&evt->call_disconnected.cause,
NULL);
case STK_EVENT_TYPE_LOCATION_STATUS:
return build_dataobj(builder,
build_dataobj_location_status,
DATAOBJ_FLAG_CR,
&evt->location_status.state,
build_dataobj_location_info, 0,
&evt->location_status.info,
NULL);
case STK_EVENT_TYPE_USER_ACTIVITY:
case STK_EVENT_TYPE_IDLE_SCREEN_AVAILABLE:
return true;
case STK_EVENT_TYPE_CARD_READER_STATUS:
return build_dataobj(builder,
build_dataobj_card_reader_status,
DATAOBJ_FLAG_CR,
&evt->card_reader_status,
NULL);
case STK_EVENT_TYPE_LANGUAGE_SELECTION:
return build_dataobj(builder,
build_dataobj_language, DATAOBJ_FLAG_CR,
evt->language_selection,
NULL);
case STK_EVENT_TYPE_BROWSER_TERMINATION:
return build_dataobj(builder,
build_dataobj_browser_termination_cause,
DATAOBJ_FLAG_CR,
&evt->browser_termination.cause,
NULL);
case STK_EVENT_TYPE_DATA_AVAILABLE:
return build_dataobj(builder,
build_dataobj_channel_status,
DATAOBJ_FLAG_CR,
&evt->data_available.channel,
build_dataobj_channel_data_length,
DATAOBJ_FLAG_CR,
&evt->data_available.channel_data_len,
NULL);
case STK_EVENT_TYPE_CHANNEL_STATUS:
return build_dataobj(builder,
build_dataobj_channel_status,
DATAOBJ_FLAG_CR,
&evt->channel_status.channel,
build_dataobj_bearer_description,
DATAOBJ_FLAG_CR,
&evt->channel_status.bearer_desc,
build_dataobj_other_address,
DATAOBJ_FLAG_CR,
&evt->channel_status.address,
NULL);
case STK_EVENT_TYPE_SINGLE_ACCESS_TECHNOLOGY_CHANGE:
return build_dataobj(builder,
build_dataobj_access_technology,
DATAOBJ_FLAG_CR,
&evt->access_technology_change,
NULL);
case STK_EVENT_TYPE_DISPLAY_PARAMETERS_CHANGED:
return build_dataobj(builder,
build_dataobj_display_parameters,
DATAOBJ_FLAG_CR,
&evt->display_params_changed,
NULL);
case STK_EVENT_TYPE_LOCAL_CONNECTION:
return build_dataobj(builder,
build_dataobj_service_record,
DATAOBJ_FLAG_CR,
&evt->local_connection.service_record,
build_dataobj_remote_entity_address, 0,
&evt->local_connection.remote_addr,
build_dataobj_uicc_te_interface, 0,
&evt->local_connection.transport_level,
build_dataobj_other_address,
0,
&evt->local_connection.transport_addr,
NULL);
case STK_EVENT_TYPE_NETWORK_SEARCH_MODE_CHANGE:
return build_dataobj(builder,
build_dataobj_network_search_mode,
DATAOBJ_FLAG_CR,
&evt->network_search_mode_change,
NULL);
case STK_EVENT_TYPE_BROWSING_STATUS:
return build_dataobj(builder,
build_dataobj_browsing_status,
DATAOBJ_FLAG_CR,
&evt->browsing_status,
NULL);
case STK_EVENT_TYPE_FRAMES_INFORMATION_CHANGE:
return build_dataobj(builder,
build_dataobj_frames_information,
DATAOBJ_FLAG_CR,
&evt->frames_information_change,
NULL);
case STK_EVENT_TYPE_I_WLAN_ACCESS_STATUS:
return build_dataobj(builder,
build_dataobj_i_wlan_access_status,
DATAOBJ_FLAG_CR,
&evt->i_wlan_access_status,
NULL);
case STK_EVENT_TYPE_NETWORK_REJECTION:
return build_dataobj(builder,
build_dataobj_location_info, 0,
&evt->network_rejection.location,
build_dataobj_routing_area_id, 0,
&evt->network_rejection.rai,
build_dataobj_tracking_area_id, 0,
&evt->network_rejection.tai,
build_dataobj_access_technology,
DATAOBJ_FLAG_CR,
&evt->network_rejection.access_tech,
build_dataobj_update_attach_type,
DATAOBJ_FLAG_CR,
&evt->network_rejection.update_attach,
build_dataobj_rejection_cause_code,
DATAOBJ_FLAG_CR,
&evt->network_rejection.cause,
NULL);
case STK_EVENT_TYPE_HCI_CONNECTIVITY_EVENT:
return true;
default:
return false;
}
}
static bool build_envelope_terminal_apps(struct stk_tlv_builder *builder,
const struct stk_envelope *envelope)
{
const struct stk_envelope_terminal_apps *ta = &envelope->terminal_apps;
int i;
if (!build_dataobj(builder,
build_envelope_dataobj_device_ids,
DATAOBJ_FLAG_CR, envelope, NULL))
return false;
for (i = 0; i < ta->count; i++)
if (!build_dataobj(builder,
build_dataobj_registry_application_data,
0, &ta->list[i], NULL))
return false;
return build_dataobj(builder,
build_dataobj_last_envelope,
0, &ta->last, NULL);
}
const uint8_t *stk_pdu_from_envelope(const struct stk_envelope *envelope,
unsigned int *out_length)
{
struct ber_tlv_builder btlv;
struct stk_tlv_builder builder;
bool ok = true;
static uint8_t buffer[512];
uint8_t *pdu;
if (ber_tlv_builder_init(&btlv, buffer, sizeof(buffer)) != TRUE)
return NULL;
if (!stk_tlv_builder_recurse(&builder, &btlv, envelope->type))
return NULL;
switch (envelope->type) {
case STK_ENVELOPE_TYPE_SMS_PP_DOWNLOAD:
ok = build_dataobj(&builder,
build_envelope_dataobj_device_ids,
DATAOBJ_FLAG_CR,
envelope,
build_dataobj_address, 0,
&envelope->sms_pp_download.address,
build_dataobj_gsm_sms_tpdu,
DATAOBJ_FLAG_CR,
&envelope->sms_pp_download.message,
NULL);
break;
case STK_ENVELOPE_TYPE_CBS_PP_DOWNLOAD:
ok = build_dataobj(&builder,
build_envelope_dataobj_device_ids,
DATAOBJ_FLAG_CR,
envelope,
build_dataobj_cbs_page,
DATAOBJ_FLAG_CR,
&envelope->cbs_pp_download.page,
NULL);
break;
case STK_ENVELOPE_TYPE_MENU_SELECTION:
ok = build_dataobj(&builder,
build_envelope_dataobj_device_ids,
DATAOBJ_FLAG_CR,
envelope,
build_dataobj_item_id, DATAOBJ_FLAG_CR,
&envelope->menu_selection.item_id,
build_dataobj_help_request, 0,
&envelope->menu_selection.help_request,
NULL);
break;
case STK_ENVELOPE_TYPE_CALL_CONTROL:
ok = build_envelope_call_control(&builder, envelope);
break;
case STK_ENVELOPE_TYPE_MO_SMS_CONTROL:
/*
* Comprehension Required according to the specs but not
* enabled in conformance tests in 3GPP 31.124.
*/
ok = build_dataobj(&builder,
build_envelope_dataobj_device_ids, 0,
envelope,
build_dataobj_address, 0,
&envelope->sms_mo_control.sc_address,
build_dataobj_address, 0,
&envelope->sms_mo_control.dest_address,
build_dataobj_location_info, 0,
&envelope->sms_mo_control.location,
NULL);
break;
case STK_ENVELOPE_TYPE_EVENT_DOWNLOAD:
ok = build_envelope_event_download(&builder, envelope);
break;
case STK_ENVELOPE_TYPE_TIMER_EXPIRATION:
ok = build_dataobj(&builder,
build_envelope_dataobj_device_ids,
DATAOBJ_FLAG_CR,
envelope,
build_dataobj_timer_id,
DATAOBJ_FLAG_CR,
&envelope->timer_expiration.id,
build_dataobj_timer_value,
DATAOBJ_FLAG_CR,
&envelope->timer_expiration.value,
NULL);
break;
case STK_ENVELOPE_TYPE_USSD_DOWNLOAD:
ok = build_dataobj(&builder,
build_envelope_dataobj_device_ids,
DATAOBJ_FLAG_CR,
envelope,
build_dataobj_ussd_string,
DATAOBJ_FLAG_CR,
&envelope->ussd_data_download.string,
NULL);
break;
case STK_ENVELOPE_TYPE_MMS_TRANSFER_STATUS:
ok = build_dataobj(&builder,
build_envelope_dataobj_device_ids,
DATAOBJ_FLAG_CR,
envelope,
build_dataobj_file, DATAOBJ_FLAG_CR,
&envelope->mms_status.transfer_file,
build_dataobj_mms_id, 0,
&envelope->mms_status.id,
build_dataobj_mms_transfer_status, 0,
&envelope->mms_status.transfer_status,
NULL);
break;
case STK_ENVELOPE_TYPE_MMS_NOTIFICATION:
ok = build_dataobj(&builder,
build_envelope_dataobj_device_ids,
DATAOBJ_FLAG_CR,
envelope,
build_dataobj_mms_notification,
DATAOBJ_FLAG_CR,
&envelope->mms_notification.msg,
build_dataobj_last_envelope, 0,
&envelope->mms_notification.last,
NULL);
break;
case STK_ENVELOPE_TYPE_TERMINAL_APP:
ok = build_envelope_terminal_apps(&builder, envelope);
break;
default:
return NULL;
};
if (!ok)
return NULL;
ber_tlv_builder_optimize(&btlv, &pdu, out_length);
return pdu;
}
static const char *html_colors[] = {
"#000000", /* Black */
"#808080", /* Dark Grey */
"#C11B17", /* Dark Red */
"#FBB117", /* Dark Yellow */
"#347235", /* Dark Green */
"#307D7E", /* Dark Cyan */
"#0000A0", /* Dark Blue */
"#C031C7", /* Dark Magenta */
"#C0C0C0", /* Grey */
"#FFFFFF", /* White */
"#FF0000", /* Bright Red */
"#FFFF00", /* Bright Yellow */
"#00FF00", /* Bright Green */
"#00FFFF", /* Bright Cyan */
"#0000FF", /* Bright Blue */
"#FF00FF", /* Bright Magenta */
};
#define STK_TEXT_FORMAT_ALIGN_MASK 0x03
#define STK_TEXT_FORMAT_FONT_MASK 0x0C
#define STK_TEXT_FORMAT_STYLE_MASK 0xF0
#define STK_DEFAULT_TEXT_ALIGNMENT 0x00
#define STK_TEXT_FORMAT_INIT 0x9003
/* Defined in ETSI 123 40 9.2.3.24.10.1.1 */
enum stk_text_format_code {
STK_TEXT_FORMAT_LEFT_ALIGN = 0x00,
STK_TEXT_FORMAT_CENTER_ALIGN = 0x01,
STK_TEXT_FORMAT_RIGHT_ALIGN = 0x02,
STK_TEXT_FORMAT_NO_ALIGN = 0x03,
STK_TEXT_FORMAT_FONT_SIZE_LARGE = 0x04,
STK_TEXT_FORMAT_FONT_SIZE_SMALL = 0x08,
STK_TEXT_FORMAT_FONT_SIZE_RESERVED = 0x0c,
STK_TEXT_FORMAT_STYLE_BOLD = 0x10,
STK_TEXT_FORMAT_STYLE_ITALIC = 0x20,
STK_TEXT_FORMAT_STYLE_UNDERLINED = 0x40,
STK_TEXT_FORMAT_STYLE_STRIKETHROUGH = 0x80,
};
static void end_format(struct l_string *string, uint16_t attr)
{
uint8_t code = attr & 0xFF;
uint8_t color = (attr >> 8) & 0xFF;
if ((code & ~STK_TEXT_FORMAT_ALIGN_MASK) || color)
l_string_append(string, "</span>");
if ((code & STK_TEXT_FORMAT_ALIGN_MASK) != STK_TEXT_FORMAT_NO_ALIGN)
l_string_append(string, "</div>");
}
static void start_format(struct l_string *string, uint16_t attr)
{
uint8_t code = attr & 0xFF;
uint8_t color = (attr >> 8) & 0xFF;
uint8_t align = code & STK_TEXT_FORMAT_ALIGN_MASK;
uint8_t font = code & STK_TEXT_FORMAT_FONT_MASK;
uint8_t style = code & STK_TEXT_FORMAT_STYLE_MASK;
int fg = color & 0x0f;
int bg = (color >> 4) & 0x0f;
/* align formatting applies to a block of text */
if (align != STK_TEXT_FORMAT_NO_ALIGN)
l_string_append(string, "<div style=\"");
switch (align) {
case STK_TEXT_FORMAT_RIGHT_ALIGN:
l_string_append(string, "text-align: right;\">");
break;
case STK_TEXT_FORMAT_CENTER_ALIGN:
l_string_append(string, "text-align: center;\">");
break;
case STK_TEXT_FORMAT_LEFT_ALIGN:
l_string_append(string, "text-align: left;\">");
break;
}
if ((font == 0) && (style == 0) && (color == 0))
return;
/* font, style, and color are inline */
l_string_append(string, "<span style=\"");
switch (font) {
case STK_TEXT_FORMAT_FONT_SIZE_LARGE:
l_string_append(string, "font-size: big;");
break;
case STK_TEXT_FORMAT_FONT_SIZE_SMALL:
l_string_append(string, "font-size: small;");
break;
}
if (style & STK_TEXT_FORMAT_STYLE_BOLD)
l_string_append(string, "font-weight: bold;");
if (style & STK_TEXT_FORMAT_STYLE_ITALIC)
l_string_append(string, "font-style: italic;");
if (style & STK_TEXT_FORMAT_STYLE_UNDERLINED)
l_string_append(string, "text-decoration: underline;");
if (style & STK_TEXT_FORMAT_STYLE_STRIKETHROUGH)
l_string_append(string, "text-decoration: line-through;");
/* add any color */
l_string_append_printf(string, "color: %s;", html_colors[fg]);
l_string_append_printf(string, "background-color: %s;",
html_colors[bg]);
l_string_append(string, "\">");
}
char *stk_text_to_html(const char *utf8,
const uint16_t *attrs, int num_attrs)
{
long text_len = l_utf8_strlen(utf8);
struct l_string *string = l_string_new(strlen(utf8) + 1);
short *formats;
int pos, i, j;
uint16_t start, end, len, attr, prev_attr;
uint8_t code, color, align;
const char *text = utf8;
int attrs_len = num_attrs * 4;
formats = l_new(int16_t, text_len + 1);
/* we will need formatting at the position beyond the last char */
for (i = 0; i <= text_len; i++)
formats[i] = STK_TEXT_FORMAT_INIT;
for (i = 0; i < attrs_len; i += 4) {
start = attrs[i];
len = attrs[i + 1];
code = attrs[i + 2] & 0xFF;
color = attrs[i + 3] & 0xFF;
if (len == 0)
end = text_len;
else
end = start + len;
/* sanity check values */
if (start > end || end > text_len)
continue;
/*
* if the alignment is the same as either the default
* or the last alignment used, don't set any alignment
* value.
*/
if (start == 0)
align = STK_TEXT_FORMAT_NO_ALIGN;
else {
align = formats[start - 1] &
STK_TEXT_FORMAT_ALIGN_MASK;
}
if ((code & STK_TEXT_FORMAT_ALIGN_MASK) == align)
code |= STK_TEXT_FORMAT_NO_ALIGN;
attr = code | (color << 8);
for (j = start; j < end; j++)
formats[j] = attr;
}
prev_attr = STK_TEXT_FORMAT_INIT;
for (pos = 0; pos <= text_len; pos++) {
wchar_t c;
int len;
attr = formats[pos];
if (attr != prev_attr) {
if (prev_attr != STK_TEXT_FORMAT_INIT)
end_format(string, prev_attr);
if (attr != STK_TEXT_FORMAT_INIT)
start_format(string, attr);
prev_attr = attr;
}
if (pos == text_len)
break;
len = l_utf8_get_codepoint(text, 4, &c);
switch (c) {
case '\n':
l_string_append(string, "<br/>");
break;
case '\r':
/* If the next character is a newline, consume it */
if ((pos + 1 < text_len) && (text[len] == '\n')) {
pos += 1;
len += 1;
}
l_string_append(string, "<br/>");
break;
case '<':
l_string_append(string, "&lt;");
break;
case '>':
l_string_append(string, "&gt;");
break;
case '&':
l_string_append(string, "&amp;");
break;
default:
l_string_append_fixed(string, text, len);
}
text += len;
}
l_free(formats);
/* return characters from string. Caller must free char data */
return l_string_unwrap(string);
}
static const char chars_table[] = {
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C',
'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c',
'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p',
'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '+', '.' };
char *stk_image_to_xpm(const uint8_t *img, unsigned int len,
enum stk_img_scheme scheme, const uint8_t *clut,
uint16_t clut_len)
{
uint8_t width, height;
unsigned int ncolors, nbits, entry, cpp;
unsigned int i, j;
int bit, k;
struct l_string *xpm;
unsigned int pos = 0;
const char xpm_header[] = "/* XPM */\n";
const char declaration[] = "static char *xpm[] = {\n";
char c[3];
if (img == NULL)
return NULL;
/* sanity check length */
if (len < 3)
return NULL;
width = img[pos++];
height = img[pos++];
if (scheme == STK_IMG_SCHEME_BASIC) {
nbits = 1;
ncolors = 2;
} else {
/* sanity check length */
if ((pos + 4 > len) || (clut == NULL))
return NULL;
nbits = img[pos++];
ncolors = img[pos++];
/* the value of zero should be interpreted as 256 */
if (ncolors == 0)
ncolors = 256;
/* skip clut offset bytes */
pos += 2;
if ((ncolors * 3) > clut_len)
return NULL;
}
if (pos + ((width * height + 7) / 8) > len)
return NULL;
/* determine the number of chars need to represent the pixel */
cpp = ncolors > 64 ? 2 : 1;
/*
* space needed:
* header line
* declaration and beginning of assignment line
* values - max length of 19
* colors - ncolors * (cpp + whitespace + deliminators + color)
* pixels - width * height * cpp + height deliminators "",\n
* end of assignment - 2 chars "};"
*/
xpm = l_string_new(strlen(xpm_header) + strlen(declaration) +
19 + ((cpp + 14) * ncolors) +
(width * height * cpp) + (4 * height) + 2);
/* add header, declaration, values */
l_string_append(xpm, xpm_header);
l_string_append(xpm, declaration);
l_string_append_printf(xpm, "\"%d %d %d %d\",\n", width, height,
ncolors, cpp);
/* create colors */
if (scheme == STK_IMG_SCHEME_BASIC) {
l_string_append(xpm, "\"0\tc #000000\",\n");
l_string_append(xpm, "\"1\tc #FFFFFF\",\n");
} else {
for (i = 0; i < ncolors; i++) {
/* lookup char representation of this number */
if (ncolors > 64) {
c[0] = chars_table[i / 64];
c[1] = chars_table[i % 64];
c[2] = '\0';
} else {
c[0] = chars_table[i % 64];
c[1] = '\0';
}
if ((i == (ncolors - 1)) &&
scheme == STK_IMG_SCHEME_TRANSPARENCY)
l_string_append_printf(xpm,
"\"%s\tc None\",\n", c);
else
l_string_append_printf(xpm,
"\"%s\tc #%02hhX%02hhX%02hhX\",\n",
c, clut[0], clut[1], clut[2]);
clut += 3;
}
}
/* height rows of width pixels */
k = 7;
for (i = 0; i < height; i++) {
l_string_append(xpm, "\"");
for (j = 0; j < width; j++) {
entry = 0;
for (bit = nbits - 1; bit >= 0; bit--) {
entry |= (img[pos] >> k & 0x1) << bit;
k--;
/* see if we crossed a byte boundary */
if (k < 0) {
k = 7;
pos++;
}
}
/* lookup char representation of this number */
if (ncolors > 64) {
c[0] = chars_table[entry / 64];
c[1] = chars_table[entry % 64];
c[2] = '\0';
} else {
c[0] = chars_table[entry % 64];
c[1] = '\0';
}
l_string_append_printf(xpm, "%s", c);
}
l_string_append(xpm, "\",\n");
}
l_string_append(xpm, "};");
/* Caller must free char data */
return l_string_unwrap(xpm);
}