Permalink
Cannot retrieve contributors at this time
Join GitHub today
GitHub is home to over 50 million developers working together to host and review code, manage projects, and build software together.
Sign up| /* | |
| * | |
| * 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)) | |
| 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, "<"); | |
| break; | |
| case '>': | |
| l_string_append(string, ">"); | |
| break; | |
| case '&': | |
| l_string_append(string, "&"); | |
| 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); | |
| } |