/* * * 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 #endif #include #include #include #include #include #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 unsigned char *start; unsigned int pos; unsigned int max; unsigned char len; const unsigned char *file; }; struct stk_tlv_builder { struct comprehension_tlv_builder ctlv; unsigned char *value; unsigned int len; unsigned int max_len; }; typedef gboolean (*dataobj_handler)(struct comprehension_tlv_iter *, void *); typedef gboolean (*dataobj_writer)(struct stk_tlv_builder *, const void *, gboolean); /* * 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; unsigned char 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(unsigned char 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; unsigned char *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); g_free(unpacked); break; } case SMS_CHARSET_8BIT: utf8 = convert_gsm_to_utf8(data, len, NULL, NULL, 0); break; case SMS_CHARSET_UCS2: utf8 = g_convert((const gchar *) data, len, "UTF-8//TRANSLIT", "UCS-2BE", NULL, NULL, NULL); break; default: utf8 = NULL; } return utf8; } /* For data object only to indicate its existence */ static gboolean parse_dataobj_common_bool(struct comprehension_tlv_iter *iter, gboolean *out) { if (comprehension_tlv_iter_get_length(iter) != 0) return FALSE; *out = TRUE; return TRUE; } /* For data object that only has one byte */ static gboolean parse_dataobj_common_byte(struct comprehension_tlv_iter *iter, unsigned char *out) { const unsigned char *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 gboolean parse_dataobj_common_text(struct comprehension_tlv_iter *iter, char **text) { const unsigned char *data; unsigned int len = comprehension_tlv_iter_get_length(iter); if (len < 1) return FALSE; data = comprehension_tlv_iter_get_data(iter); *text = g_try_malloc(len + 1); if (*text == NULL) return FALSE; memcpy(*text, data, len); (*text)[len] = '\0'; return TRUE; } /* For data object that only has a byte array with undetermined length */ static gboolean parse_dataobj_common_byte_array( struct comprehension_tlv_iter *iter, struct stk_common_byte_array *array) { const unsigned char *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 = g_try_malloc(len); if (array->array == NULL) return FALSE; memcpy(array->array, data, len); return TRUE; } static void stk_file_iter_init(struct stk_file_iter *iter, const unsigned char *start, unsigned int len) { iter->start = start; iter->max = len; iter->pos = 0; } static gboolean stk_file_iter_next(struct stk_file_iter *iter) { unsigned int pos = iter->pos; const unsigned int max = iter->max; const unsigned char *start = iter->start; unsigned int i; unsigned char 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 gboolean parse_dataobj_address(struct comprehension_tlv_iter *iter, void *user) { struct stk_address *addr = user; const unsigned char *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 = g_try_malloc(len * 2 - 1); if (number == NULL) return FALSE; 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 gboolean parse_dataobj_alpha_id(struct comprehension_tlv_iter *iter, void *user) { char **alpha_id = user; const unsigned char *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 gboolean parse_dataobj_subaddress(struct comprehension_tlv_iter *iter, void *user) { struct stk_subaddress *subaddr = user; const unsigned char *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 gboolean parse_dataobj_ccp(struct comprehension_tlv_iter *iter, void *user) { struct stk_ccp *ccp = user; const unsigned char *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 gboolean parse_dataobj_cbs_page(struct comprehension_tlv_iter *iter, void *user) { struct stk_cbs_page *cp = user; const unsigned char *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 gboolean parse_dataobj_duration(struct comprehension_tlv_iter *iter, void *user) { struct stk_duration *duration = user; const unsigned char *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 gboolean parse_dataobj_item(struct comprehension_tlv_iter *iter, void *user) { struct stk_item *item = user; const unsigned char *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 gboolean parse_dataobj_item_id(struct comprehension_tlv_iter *iter, void *user) { unsigned char *id = user; const unsigned char *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 gboolean parse_dataobj_response_len(struct comprehension_tlv_iter *iter, void *user) { struct stk_response_length *response_len = user; const unsigned char *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 gboolean parse_dataobj_result(struct comprehension_tlv_iter *iter, void *user) { struct stk_result *result = user; const unsigned char *data; unsigned int len; unsigned char *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 = g_try_malloc(len - 1); if (additional == NULL) return FALSE; 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 gboolean parse_dataobj_gsm_sms_tpdu(struct comprehension_tlv_iter *iter, void *user) { struct gsm_sms_tpdu *tpdu = user; const unsigned char *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 gboolean parse_dataobj_ss(struct comprehension_tlv_iter *iter, void *user) { struct stk_ss *ss = user; const unsigned char *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 = g_try_malloc(len * 2 - 1); if (s == NULL) return FALSE; 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 gboolean parse_dataobj_text(struct comprehension_tlv_iter *iter, void *user) { char **text = user; unsigned int len = comprehension_tlv_iter_get_length(iter); const unsigned char *data; char *utf8; if (len <= 1) { *text = g_try_malloc0(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 gboolean parse_dataobj_tone(struct comprehension_tlv_iter *iter, void *user) { unsigned char *byte = user; return parse_dataobj_common_byte(iter, byte); } /* Defined in TS 102.223 Section 8.17 */ static gboolean 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 unsigned char *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 gboolean parse_dataobj_file_list(struct comprehension_tlv_iter *iter, void *user) { GSList **fl = user; const unsigned char *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); while (stk_file_iter_next(&sf_iter)) { sf = g_try_new0(struct stk_file, 1); if (sf == NULL) goto error; sf->len = sf_iter.len; memcpy(sf->file, sf_iter.file, sf_iter.len); *fl = g_slist_prepend(*fl, sf); } if (sf_iter.pos != sf_iter.max) goto error; *fl = g_slist_reverse(*fl); return TRUE; error: g_slist_foreach(*fl, (GFunc) g_free, NULL); g_slist_free(*fl); return FALSE; } /* Defined in TS 102.223 Section 8.19 */ static gboolean parse_dataobj_location_info(struct comprehension_tlv_iter *iter, void *user) { struct stk_location_info *li = user; const unsigned char *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 gboolean parse_dataobj_imei(struct comprehension_tlv_iter *iter, void *user) { char *imei = user; const unsigned char *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 gboolean parse_dataobj_help_request(struct comprehension_tlv_iter *iter, void *user) { gboolean *ret = user; return parse_dataobj_common_bool(iter, ret); } /* Defined in TS 102.223 Section 8.22 */ static gboolean parse_dataobj_network_measurement_results( struct comprehension_tlv_iter *iter, void *user) { unsigned char *nmr = user; const unsigned char *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 gboolean parse_dataobj_default_text(struct comprehension_tlv_iter *iter, void *user) { char **text = user; unsigned int len = comprehension_tlv_iter_get_length(iter); const unsigned char *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 gboolean parse_dataobj_items_next_action_indicator( struct comprehension_tlv_iter *iter, void *user) { struct stk_items_next_action_indicator *inai = user; const unsigned char *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 gboolean parse_dataobj_event_list(struct comprehension_tlv_iter *iter, void *user) { struct stk_event_list *el = user; const unsigned char *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 gboolean parse_dataobj_cause(struct comprehension_tlv_iter *iter, void *user) { struct stk_cause *cause = user; const unsigned char *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 gboolean parse_dataobj_location_status( struct comprehension_tlv_iter *iter, void *user) { unsigned char *byte = user; return parse_dataobj_common_byte(iter, byte); } /* Defined in TS 102.223 Section 8.28 */ static gboolean parse_dataobj_transaction_id( struct comprehension_tlv_iter *iter, void *user) { struct stk_transaction_id *ti = user; const unsigned char *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 gboolean parse_dataobj_bcch_channel_list( struct comprehension_tlv_iter *iter, void *user) { struct stk_bcch_channel_list *bcl = user; const unsigned char *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 gboolean 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 gboolean parse_dataobj_icon_id(struct comprehension_tlv_iter *iter, void *user) { struct stk_icon_id *id = user; const unsigned char *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 gboolean parse_dataobj_item_icon_id_list( struct comprehension_tlv_iter *iter, void *user) { struct stk_item_icon_id_list *iiil = user; const unsigned char *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 gboolean parse_dataobj_card_reader_status( struct comprehension_tlv_iter *iter, void *user) { unsigned char *byte = user; return parse_dataobj_common_byte(iter, byte); } /* Defined in TS 102.223 Section 8.34 */ static gboolean parse_dataobj_card_atr(struct comprehension_tlv_iter *iter, void *user) { struct stk_card_atr *ca = user; const unsigned char *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 gboolean parse_dataobj_c_apdu(struct comprehension_tlv_iter *iter, void *user) { struct stk_c_apdu *ca = user; const unsigned char *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 gboolean parse_dataobj_r_apdu(struct comprehension_tlv_iter *iter, void *user) { struct stk_r_apdu *ra = user; const unsigned char *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 gboolean parse_dataobj_timer_id(struct comprehension_tlv_iter *iter, void *user) { unsigned char *byte = user; return parse_dataobj_common_byte(iter, byte); } /* Defined in TS 102.223 Section 8.38 */ static gboolean parse_dataobj_timer_value(struct comprehension_tlv_iter *iter, void *user) { struct stk_timer_value *tv = user; const unsigned char *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 gboolean parse_dataobj_datetime_timezone( struct comprehension_tlv_iter *iter, void *user) { struct sms_scts *scts = user; const unsigned char *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 gboolean 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 gboolean 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 gboolean 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) != TRUE) return FALSE; bc_repeat->has_bc_repeat = TRUE; return TRUE; } /* Defined in 102.223 Section 8.43 */ static gboolean parse_dataobj_imm_resp(struct comprehension_tlv_iter *iter, void *user) { gboolean *ret = user; return parse_dataobj_common_bool(iter, ret); } /* Defined in 102.223 Section 8.44 */ static gboolean parse_dataobj_dtmf_string(struct comprehension_tlv_iter *iter, void *user) { char **dtmf = user; const unsigned char *data; unsigned int len = comprehension_tlv_iter_get_length(iter); if (len < 1) return FALSE; data = comprehension_tlv_iter_get_data(iter); *dtmf = g_try_malloc(len * 2 + 1); if (*dtmf == NULL) return FALSE; sim_extract_bcd_number(data, len, *dtmf); return TRUE; } /* Defined in 102.223 Section 8.45 */ static gboolean parse_dataobj_language(struct comprehension_tlv_iter *iter, void *user) { char *lang = user; const unsigned char *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 gboolean parse_dataobj_timing_advance( struct comprehension_tlv_iter *iter, void *user) { struct stk_timing_advance *ta = user; const unsigned char *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 gboolean parse_dataobj_browser_id(struct comprehension_tlv_iter *iter, void *user) { unsigned char *byte = user; if (parse_dataobj_common_byte(iter, byte) == FALSE || *byte > 4) return FALSE; return TRUE; } /* Defined in TS 102.223 Section 8.48 */ static gboolean 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 gboolean 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 gboolean parse_dataobj_provisioning_file_reference( struct comprehension_tlv_iter *iter, void *user) { struct stk_file *f = user; const unsigned char *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 gboolean parse_dataobj_browser_termination_cause( struct comprehension_tlv_iter *iter, void *user) { unsigned char *byte = user; return parse_dataobj_common_byte(iter, byte); } /* Defined in TS 102.223 Section 8.52 */ static gboolean parse_dataobj_bearer_description( struct comprehension_tlv_iter *iter, void *user) { struct stk_bearer_description *bd = user; const unsigned char *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 gboolean 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 gboolean parse_dataobj_channel_data_length( struct comprehension_tlv_iter *iter, void *user) { unsigned char *byte = user; return parse_dataobj_common_byte(iter, byte); } /* Defined in TS 102.223 Section 8.55 */ static gboolean parse_dataobj_buffer_size(struct comprehension_tlv_iter *iter, void *user) { unsigned short *size = user; const unsigned char *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 gboolean parse_dataobj_channel_status( struct comprehension_tlv_iter *iter, void *user) { unsigned char *status = user; const unsigned char *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 gboolean parse_dataobj_card_reader_id( struct comprehension_tlv_iter *iter, void *user) { struct stk_card_reader_id *cr_id = user; const unsigned char *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 gboolean parse_dataobj_other_address( struct comprehension_tlv_iter *iter, void *user) { struct stk_other_address *oa = user; const unsigned char *data; unsigned char 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 gboolean parse_dataobj_uicc_te_interface( struct comprehension_tlv_iter *iter, void *user) { struct stk_uicc_te_interface *uti = user; const unsigned char *data; unsigned char 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 gboolean parse_dataobj_aid(struct comprehension_tlv_iter *iter, void *user) { struct stk_aid *aid = user; const unsigned char *data; unsigned char 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 gboolean parse_dataobj_access_technology( struct comprehension_tlv_iter *iter, void *user) { unsigned char *byte = user; return parse_dataobj_common_byte(iter, byte); } /* Defined in TS 102.223 Section 8.62 */ static gboolean parse_dataobj_display_parameters( struct comprehension_tlv_iter *iter, void *user) { struct stk_display_parameters *dp = user; const unsigned char *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 gboolean parse_dataobj_service_record( struct comprehension_tlv_iter *iter, void *user) { struct stk_service_record *sr = user; const unsigned char *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 = g_try_malloc(sr->len); if (sr->serv_rec == NULL) return FALSE; memcpy(sr->serv_rec, data + 2, sr->len); return TRUE; } /* Defined in TS 102.223 Section 8.64 */ static gboolean parse_dataobj_device_filter(struct comprehension_tlv_iter *iter, void *user) { struct stk_device_filter *df = user; const unsigned char *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 = g_try_malloc(df->len); if (df->dev_filter == NULL) return FALSE; memcpy(df->dev_filter, data + 1, df->len); return TRUE; } /* Defined in TS 102.223 Section 8.65 */ static gboolean parse_dataobj_service_search( struct comprehension_tlv_iter *iter, void *user) { struct stk_service_search *ss = user; const unsigned char *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 = g_try_malloc(ss->len); if (ss->ser_search == NULL) return FALSE; memcpy(ss->ser_search, data + 1, ss->len); return TRUE; } /* Defined in TS 102.223 Section 8.66 */ static gboolean parse_dataobj_attribute_info( struct comprehension_tlv_iter *iter, void *user) { struct stk_attribute_info *ai = user; const unsigned char *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 = g_try_malloc(ai->len); if (ai->attr_info == NULL) return FALSE; memcpy(ai->attr_info, data + 1, ai->len); return TRUE; } /* Defined in TS 102.223 Section 8.67 */ static gboolean 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 gboolean parse_dataobj_remote_entity_address( struct comprehension_tlv_iter *iter, void *user) { struct stk_remote_entity_address *rea = user; const unsigned char *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 gboolean parse_dataobj_esn(struct comprehension_tlv_iter *iter, void *user) { unsigned char *esn = user; const unsigned char *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 gboolean parse_dataobj_network_access_name( struct comprehension_tlv_iter *iter, void *user) { char **apn = user; const unsigned char *data; unsigned int len = comprehension_tlv_iter_get_length(iter); unsigned char label_size; unsigned char 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 = g_strdup(decoded_apn); return TRUE; } /* Defined in TS 102.223 Section 8.71 */ static gboolean 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 gboolean parse_dataobj_text_attr(struct comprehension_tlv_iter *iter, void *user) { struct stk_text_attribute *attr = user; const unsigned char *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 gboolean parse_dataobj_pdp_act_par( struct comprehension_tlv_iter *iter, void *user) { struct stk_pdp_act_par *pcap = user; const unsigned char *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 gboolean parse_dataobj_item_text_attribute_list( struct comprehension_tlv_iter *iter, void *user) { struct stk_item_text_attribute_list *ital = user; const unsigned char *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 gboolean parse_dataobj_utran_meas_qualifier( struct comprehension_tlv_iter *iter, void *user) { unsigned char *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 gboolean parse_dataobj_imeisv(struct comprehension_tlv_iter *iter, void *user) { char *imeisv = user; const unsigned char *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 gboolean parse_dataobj_network_search_mode( struct comprehension_tlv_iter *iter, void *user) { unsigned char *byte = user; return parse_dataobj_common_byte(iter, byte); } /* Defined in TS 102.223 Section 8.76 */ static gboolean parse_dataobj_battery_state(struct comprehension_tlv_iter *iter, void *user) { unsigned char *byte = user; return parse_dataobj_common_byte(iter, byte); } /* Defined in TS 102.223 Section 8.77 */ static gboolean 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 gboolean parse_dataobj_frame_layout(struct comprehension_tlv_iter *iter, void *user) { struct stk_frame_layout *fl = user; const unsigned char *data; unsigned char 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 gboolean parse_dataobj_frames_info(struct comprehension_tlv_iter *iter, void *user) { struct stk_frames_info *fi = user; const unsigned char *data; unsigned char 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 gboolean parse_dataobj_frame_id(struct comprehension_tlv_iter *iter, void *user) { struct stk_frame_id *fi = user; const unsigned char *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 gboolean parse_dataobj_meid(struct comprehension_tlv_iter *iter, void *user) { unsigned char *meid = user; const unsigned char *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 gboolean parse_dataobj_mms_reference(struct comprehension_tlv_iter *iter, void *user) { struct stk_mms_reference *mr = user; const unsigned char *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 gboolean parse_dataobj_mms_id(struct comprehension_tlv_iter *iter, void *user) { struct stk_mms_id *mi = user; const unsigned char *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 gboolean parse_dataobj_mms_transfer_status( struct comprehension_tlv_iter *iter, void *user) { struct stk_mms_transfer_status *mts = user; const unsigned char *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 gboolean parse_dataobj_mms_content_id( struct comprehension_tlv_iter *iter, void *user) { struct stk_mms_content_id *mci = user; const unsigned char *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 gboolean 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 gboolean parse_dataobj_last_envelope(struct comprehension_tlv_iter *iter, void *user) { gboolean *ret = user; return parse_dataobj_common_bool(iter, ret); } /* Defined in TS 102.223 Section 8.88 */ static gboolean parse_dataobj_registry_application_data( struct comprehension_tlv_iter *iter, void *user) { struct stk_registry_application_data *rad = user; const unsigned char *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 gboolean parse_dataobj_activate_descriptor( struct comprehension_tlv_iter *iter, void *user) { unsigned char *byte = user; const unsigned char *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 gboolean parse_dataobj_broadcast_network_info( struct comprehension_tlv_iter *iter, void *user) { struct stk_broadcast_network_information *bni = user; const unsigned char *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(struct stk_item *item) { g_free(item->text); g_free(item); } static gboolean parse_item_list(struct comprehension_tlv_iter *iter, void *data) { GSList **out = data; unsigned short tag = STK_DATA_OBJECT_TYPE_ITEM; struct comprehension_tlv_iter iter_old; struct stk_item item; GSList *list = NULL; unsigned int count = 0; gboolean has_empty = FALSE; do { comprehension_tlv_iter_copy(iter, &iter_old); memset(&item, 0, sizeof(item)); count++; if (parse_dataobj_item(iter, &item) == TRUE) { if (item.id == 0) { has_empty = TRUE; continue; } list = g_slist_prepend(list, g_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) { *out = g_slist_reverse(list); return TRUE; } if (count == 1) return TRUE; g_slist_foreach(list, (GFunc) destroy_stk_item, NULL); g_slist_free(list); return FALSE; } static gboolean parse_provisioning_list(struct comprehension_tlv_iter *iter, void *data) { GSList **out = data; unsigned short tag = STK_DATA_OBJECT_TYPE_PROVISIONING_FILE_REF; struct comprehension_tlv_iter iter_old; struct stk_file file; GSList *list = NULL; do { comprehension_tlv_iter_copy(iter, &iter_old); memset(&file, 0, sizeof(file)); if (parse_dataobj_provisioning_file_reference(iter, &file) == TRUE) list = g_slist_prepend(list, g_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 = g_slist_reverse(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; gboolean minimum_set = TRUE; gboolean 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) == FALSE) 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_foreach(entries, (GFunc) g_free, NULL); g_slist_free(entries); if (minimum_set == FALSE) return STK_PARSE_RESULT_MISSING_VALUE; if (parse_error == TRUE) return STK_PARSE_RESULT_DATA_NOT_UNDERSTOOD; return STK_PARSE_RESULT_OK; } static void destroy_display_text(struct stk_command *command) { g_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) { g_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) { g_free(command->get_input.text); g_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) { g_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) { g_free(command->setup_menu.alpha_id); g_slist_foreach(command->setup_menu.items, (GFunc) destroy_stk_item, NULL); g_slist_free(command->setup_menu.items); } 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) { g_free(command->select_item.alpha_id); g_slist_foreach(command->select_item.items, (GFunc) destroy_stk_item, NULL); g_slist_free(command->select_item.items); } 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 && obj->items == NULL) 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) { g_free(command->send_sms.alpha_id); g_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) != TRUE) { 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: g_free(sc_address.number); return status; } static void destroy_send_ss(struct stk_command *command) { g_free(command->send_ss.alpha_id); g_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) { g_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) { g_free(command->setup_call.alpha_id_usr_cfm); g_free(command->setup_call.addr.number); g_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) { g_slist_foreach(command->refresh.file_list, (GFunc) g_free, NULL); g_slist_free(command->refresh.file_list); g_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) { g_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) { g_free(command->run_at_command.alpha_id); g_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) { g_free(command->send_dtmf.alpha_id); g_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) { g_free(command->launch_browser.url); g_free(command->launch_browser.bearer.array); g_slist_foreach(command->launch_browser.prov_file_refs, (GFunc) g_free, NULL); g_slist_free(command->launch_browser.prov_file_refs); g_free(command->launch_browser.text_gateway_proxy_id); g_free(command->launch_browser.alpha_id); g_free(command->launch_browser.network_name.array); g_free(command->launch_browser.text_usr); g_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) { g_free(command->open_channel.alpha_id); g_free(command->open_channel.apn); g_free(command->open_channel.text_usr); g_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) { g_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) { g_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) { g_free(command->send_data.alpha_id); g_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) { g_free(command->service_search.alpha_id); g_free(command->service_search.serv_search.ser_search); g_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) { g_free(command->get_service_info.alpha_id); g_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) { g_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) { g_free(command->retrieve_mms.alpha_id); g_slist_foreach(command->retrieve_mms.mms_rec_files, (GFunc) g_free, NULL); g_slist_free(command->retrieve_mms.mms_rec_files); } 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) { g_free(command->submit_mms.alpha_id); g_slist_foreach(command->submit_mms.mms_subm_files, (GFunc) g_free, NULL); g_slist_free(command->submit_mms.mms_subm_files); } 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) { g_slist_foreach(command->display_mms.mms_subm_files, (GFunc) g_free, NULL); g_slist_free(command->display_mms.mms_subm_files); } 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 unsigned char *pdu, unsigned int len) { struct ber_tlv_iter ber; struct comprehension_tlv_iter iter; const unsigned char *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 gboolean stk_tlv_builder_init(struct stk_tlv_builder *iter, unsigned char *pdu, unsigned int size) { iter->value = NULL; iter->len = 0; return comprehension_tlv_builder_init(&iter->ctlv, pdu, size); } static gboolean stk_tlv_builder_recurse(struct stk_tlv_builder *iter, struct ber_tlv_builder *btlv, unsigned char 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 gboolean stk_tlv_builder_open_container(struct stk_tlv_builder *iter, gboolean cr, unsigned char shorttag, gboolean 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 gboolean 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 gboolean stk_tlv_builder_append_byte(struct stk_tlv_builder *iter, unsigned char num) { if (iter->len >= iter->max_len) return FALSE; iter->value[iter->len++] = num; return TRUE; } static gboolean stk_tlv_builder_append_short(struct stk_tlv_builder *iter, unsigned short 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 gboolean stk_tlv_builder_append_gsm_packed(struct stk_tlv_builder *iter, const char *text) { unsigned int len; unsigned char *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) { g_free(gsm); return FALSE; } pack_7bit_own_buf(gsm, len, 0, FALSE, &written, 0, iter->value + iter->len + 1); g_free(gsm); if (written < 1 && len > 0) return FALSE; iter->value[iter->len++] = 0x00; iter->len += written; return TRUE; } static gboolean stk_tlv_builder_append_gsm_unpacked( struct stk_tlv_builder *iter, const char *text) { unsigned int len; unsigned char *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) { g_free(gsm); return FALSE; } iter->value[iter->len++] = 0x04; memcpy(iter->value + iter->len, gsm, written); iter->len += written; g_free(gsm); return TRUE; } static gboolean stk_tlv_builder_append_ucs2(struct stk_tlv_builder *iter, const char *text) { unsigned char *ucs2; gsize gwritten; ucs2 = (unsigned char *) g_convert((const gchar *) text, -1, "UCS-2BE", "UTF-8//TRANSLIT", NULL, &gwritten, NULL); if (ucs2 == NULL) return FALSE; if (iter->len + gwritten >= iter->max_len) { g_free(ucs2); return FALSE; } iter->value[iter->len++] = 0x08; memcpy(iter->value + iter->len, ucs2, gwritten); iter->len += gwritten; g_free(ucs2); return TRUE; } static gboolean stk_tlv_builder_append_text(struct stk_tlv_builder *iter, int dcs, const char *text) { gboolean 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 == TRUE) return ret; return stk_tlv_builder_append_ucs2(iter, text); } return FALSE; } static inline gboolean stk_tlv_builder_append_bytes(struct stk_tlv_builder *iter, const unsigned char *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 gboolean build_dataobj_address(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const struct stk_address *addr = data; unsigned char tag = STK_DATA_OBJECT_TYPE_ADDRESS; unsigned int len; unsigned char 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 gboolean build_dataobj_alpha_id(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { unsigned char tag = STK_DATA_OBJECT_TYPE_ALPHA_ID; int len; unsigned char *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 gboolean build_dataobj_subaddress(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const struct stk_subaddress *sa = data; unsigned char tag = STK_DATA_OBJECT_TYPE_SUBADDRESS; if (sa->has_subaddr == FALSE) 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 gboolean build_dataobj_ccp(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const struct stk_ccp *ccp = data; unsigned char 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 gboolean build_dataobj_cbs_page(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const struct cbs *page = data; unsigned char tag = STK_DATA_OBJECT_TYPE_CBS_PAGE; unsigned char 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 gboolean build_dataobj_item_id(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const unsigned char *item_id = data; unsigned char 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 gboolean build_dataobj_duration(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const struct stk_duration *duration = data; unsigned char 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 gboolean build_dataobj_result(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const struct stk_result *result = data; unsigned char tag = STK_DATA_OBJECT_TYPE_RESULT; if (stk_tlv_builder_open_container(tlv, cr, tag, FALSE) == FALSE) return FALSE; if (stk_tlv_builder_append_byte(tlv, result->type) == FALSE) return FALSE; if (result->additional_len > 0) if (stk_tlv_builder_append_bytes(tlv, result->additional, result->additional_len) == FALSE) return FALSE; return stk_tlv_builder_close_container(tlv); } /* Described in TS 131.111 Section 8.13 */ static gboolean build_dataobj_gsm_sms_tpdu(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const struct sms_deliver *msg = data; struct sms sms; unsigned char tag = STK_DATA_OBJECT_TYPE_GSM_SMS_TPDU; unsigned char 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 gboolean build_dataobj_ss_string(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const struct stk_address *addr = data; unsigned char tag = STK_DATA_OBJECT_TYPE_SS_STRING; unsigned int len; unsigned char 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 gboolean build_dataobj_text(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const struct stk_answer_text *text = data; unsigned char tag = STK_DATA_OBJECT_TYPE_TEXT; gboolean ret; if (text->text == NULL && !text->yesno) return TRUE; if (stk_tlv_builder_open_container(tlv, cr, tag, TRUE) != TRUE) return FALSE; if (text->yesno == TRUE) { /* * 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) != TRUE) 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 != TRUE) return ret; return stk_tlv_builder_close_container(tlv); } /* Defined in TS 102.223 Section 8.15 - USSD specific case*/ static gboolean build_dataobj_ussd_text(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const struct stk_ussd_text *text = data; unsigned char tag = STK_DATA_OBJECT_TYPE_TEXT; if (text->has_text == FALSE) return TRUE; if (stk_tlv_builder_open_container(tlv, cr, tag, TRUE) != TRUE) return FALSE; if (text->len > 0) { if (stk_tlv_builder_append_byte(tlv, text->dcs) != TRUE) return FALSE; if (stk_tlv_builder_append_bytes(tlv, text->text, text->len) != TRUE) return FALSE; } return stk_tlv_builder_close_container(tlv); } /* Described in TS 131.111 Section 8.17 */ static gboolean build_dataobj_ussd_string(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const struct stk_ussd_string *ussd = data; unsigned char tag = STK_DATA_OBJECT_TYPE_USSD_STRING; if (ussd->string == NULL) return TRUE; 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 gboolean build_dataobj_file_list(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { GSList *l = (void *) data; const struct stk_file *file; unsigned char tag = STK_DATA_OBJECT_TYPE_FILE_LIST; if (stk_tlv_builder_open_container(tlv, cr, tag, TRUE) != TRUE) return FALSE; if (stk_tlv_builder_append_byte(tlv, g_slist_length(l)) != TRUE) return FALSE; for (; l; l = l->next) { file = l->data; if (stk_tlv_builder_append_bytes(tlv, file->file, file->len) != TRUE) return FALSE; } return stk_tlv_builder_close_container(tlv); } /* Shortcut for a single File element */ static gboolean build_dataobj_file(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { GSList l = { .data = (void *) data, .next = NULL, }; return build_dataobj_file_list(tlv, &l, cr); } /* Described in TS 102.223 Section 8.19 */ static gboolean build_dataobj_location_info(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const struct stk_location_info *li = data; unsigned char tag = STK_DATA_OBJECT_TYPE_LOCATION_INFO; guint8 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) == FALSE) return FALSE; if (stk_tlv_builder_append_bytes(tlv, mccmnc, 3) == FALSE) return FALSE; if (stk_tlv_builder_append_short(tlv, li->lac_tac) == FALSE) return FALSE; if (li->has_ci && stk_tlv_builder_append_short(tlv, li->ci) == FALSE) return FALSE; if (li->has_ext_ci && stk_tlv_builder_append_short(tlv, li->ext_ci) == FALSE) return FALSE; if (li->has_eutran_ci) { if (stk_tlv_builder_append_short(tlv, li->eutran_ci >> 12) == FALSE) return FALSE; if (stk_tlv_builder_append_short(tlv, (li->eutran_ci << 4) | 0xf) == FALSE) return FALSE; } return stk_tlv_builder_close_container(tlv); } static gboolean build_empty_dataobj_location_info(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { unsigned char 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 gboolean build_dataobj_imei(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { char byte0[3]; const char *imei = data; unsigned char tag = STK_DATA_OBJECT_TYPE_IMEI; unsigned char 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 gboolean build_dataobj_help_request(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const ofono_bool_t *help = data; unsigned char 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 gboolean build_dataobj_network_measurement_results( struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const struct stk_common_byte_array *nmr = data; unsigned char tag = STK_DATA_OBJECT_TYPE_NETWORK_MEASUREMENT_RESULTS; if (stk_tlv_builder_open_container(tlv, cr, tag, FALSE) == FALSE) return FALSE; if (nmr->len > 0 && stk_tlv_builder_append_bytes(tlv, nmr->array, nmr->len) == FALSE) return FALSE; return stk_tlv_builder_close_container(tlv); } /* Described in TS 102.223 Section 8.25 */ static gboolean build_dataobj_event_list(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const struct stk_event_list *list = data; unsigned char 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 gboolean build_dataobj_event_type(struct stk_tlv_builder *tlv, const void *data, gboolean 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 gboolean build_dataobj_cause(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const struct stk_cause *cause = data; unsigned char tag = STK_DATA_OBJECT_TYPE_CAUSE; if (cause->has_cause == FALSE) 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 gboolean build_dataobj_location_status(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const enum stk_service_state *state = data; unsigned char 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 gboolean build_dataobj_transaction_ids(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const struct stk_transaction_id *id = data; unsigned char 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 gboolean build_dataobj_transaction_id(struct stk_tlv_builder *tlv, const void *data, gboolean 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 gboolean build_dataobj_bcch_channel_list(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const struct stk_bcch_channel_list *list = data; unsigned char tag = STK_DATA_OBJECT_TYPE_BCCH_CHANNEL_LIST; unsigned int i, bytes, pos, shift; unsigned char value; if (list->has_list == FALSE) return TRUE; if (stk_tlv_builder_open_container(tlv, cr, tag, TRUE) != 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) != TRUE) return FALSE; } return stk_tlv_builder_close_container(tlv); } /* Described in TS 102.223 Section 8.30 */ static gboolean build_dataobj_cc_requested_action(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const struct stk_common_byte_array *action = data; unsigned char 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 gboolean build_dataobj_card_reader_status(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const struct stk_reader_status *status = data; unsigned char tag = STK_DATA_OBJECT_TYPE_CARD_READER_STATUS; unsigned char 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 gboolean build_dataobj_timer_id(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const unsigned char *id = data; unsigned char 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 gboolean build_dataobj_timer_value(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const struct stk_timer_value *value = data; unsigned char tag = STK_DATA_OBJECT_TYPE_TIMER_VALUE; if (value->has_value == FALSE) 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 gboolean build_dataobj_datetime_timezone(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const struct sms_scts *scts = data; unsigned char value[7]; int offset = 0; unsigned char 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 gboolean build_dataobj_at_response(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { unsigned char 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 gboolean build_dataobj_bc_repeat(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { unsigned char tag = STK_DATA_OBJECT_TYPE_BC_REPEAT_INDICATOR; const struct stk_bc_repeat *bcr = data; if (bcr->has_bc_repeat == FALSE) 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 gboolean build_dataobj_language(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { unsigned char 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 gboolean build_dataobj_timing_advance(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const struct stk_timing_advance *tadv = data; unsigned char 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 gboolean build_dataobj_browser_termination_cause( struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const enum stk_browser_termination_cause *cause = data; unsigned char 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 gboolean build_dataobj_bearer_description(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const struct stk_bearer_description *bd = data; unsigned char 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 gboolean build_dataobj_channel_data(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const struct stk_common_byte_array *cd = data; unsigned char 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 gboolean build_dataobj_channel_data_length( struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const unsigned short *length = data; unsigned char 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 gboolean build_dataobj_buffer_size(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const unsigned short *buf_size = data; unsigned char 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 gboolean build_dataobj_channel_status(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const struct stk_channel *channel = data; unsigned char tag = STK_DATA_OBJECT_TYPE_CHANNEL_STATUS; unsigned char 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 gboolean build_dataobj_other_address(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const struct stk_other_address *addr = data; unsigned char tag = STK_DATA_OBJECT_TYPE_OTHER_ADDRESS; gboolean ok = FALSE; if (!addr->type) return TRUE; if (stk_tlv_builder_open_container(tlv, cr, tag, FALSE) == 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 guint8 *) &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 gboolean build_dataobj_uicc_te_interface(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const struct stk_uicc_te_interface *iface = data; unsigned char 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 gboolean build_dataobj_access_technologies(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const struct stk_access_technologies *techs = data; unsigned char tag = STK_DATA_OBJECT_TYPE_ACCESS_TECHNOLOGY; int i; if (stk_tlv_builder_open_container(tlv, cr, tag, FALSE) != TRUE) return FALSE; for (i = 0; i < techs->length; i++) if (stk_tlv_builder_append_byte(tlv, techs->techs[i]) != TRUE) return FALSE; return stk_tlv_builder_close_container(tlv); } /* Shortcut for a single Access Technology */ static gboolean build_dataobj_access_technology(struct stk_tlv_builder *tlv, const void *data, gboolean 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 gboolean build_dataobj_display_parameters(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const struct stk_display_parameters *params = data; unsigned char 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 gboolean build_dataobj_service_record(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const struct stk_service_record *rec = data; unsigned char 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 gboolean build_dataobj_remote_entity_address(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const struct stk_remote_entity_address *addr = data; unsigned char tag = STK_DATA_OBJECT_TYPE_REMOTE_ENTITY_ADDRESS; gboolean ok = FALSE; if (addr->has_address != TRUE) return TRUE; if (stk_tlv_builder_open_container(tlv, cr, tag, TRUE) != TRUE) return FALSE; if (stk_tlv_builder_append_byte(tlv, addr->coding_type) != TRUE) 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 gboolean build_dataobj_esn(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const guint32 *esn = data; unsigned char 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 gboolean build_dataobj_pdp_context_params(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const struct stk_common_byte_array *params = data; unsigned char 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 gboolean build_dataobj_imeisv(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { char byte0[3]; const char *imeisv = data; unsigned char value[9]; unsigned char 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 gboolean build_dataobj_network_search_mode(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const enum stk_network_search_mode *mode = data; unsigned char 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 gboolean build_dataobj_battery_state(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const enum stk_battery_state *state = data; unsigned char 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 gboolean build_dataobj_browsing_status(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const struct stk_common_byte_array *bs = data; unsigned char 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 gboolean build_dataobj_frames_information(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const struct stk_frames_info *info = data; unsigned char tag = STK_DATA_OBJECT_TYPE_FRAMES_INFO; unsigned int i; if (stk_tlv_builder_open_container(tlv, cr, tag, FALSE) != TRUE) return FALSE; if (stk_tlv_builder_append_byte(tlv, info->id) != TRUE) return FALSE; for (i = 0; i < info->len; i++) { if (stk_tlv_builder_append_byte(tlv, info->list[i].height) != TRUE) return FALSE; if (stk_tlv_builder_append_byte(tlv, info->list[i].width) != TRUE) return FALSE; } return stk_tlv_builder_close_container(tlv); } /* Described in TS 102.223 Section 8.81 */ static gboolean build_dataobj_meid(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const char *meid = data; unsigned char value[8]; unsigned char 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 gboolean build_dataobj_mms_id(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const struct stk_mms_id *id = data; unsigned char 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 gboolean build_dataobj_mms_transfer_status(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const struct stk_mms_transfer_status *mts = data; unsigned char 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 gboolean build_dataobj_i_wlan_access_status(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const enum stk_i_wlan_access_status *status = data; unsigned char 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 gboolean build_dataobj_mms_notification(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const struct stk_common_byte_array *msg = data; unsigned char 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 gboolean build_dataobj_last_envelope(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const ofono_bool_t *last = data; unsigned char 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 gboolean build_dataobj_registry_application_data( struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const struct stk_registry_application_data *rad = data; unsigned char tag = STK_DATA_OBJECT_TYPE_REGISTRY_APPLICATION_DATA; guint8 dcs, *name; gsize len; long gsmlen; name = convert_utf8_to_gsm(rad->name, -1, NULL, &gsmlen, 0); len = gsmlen; dcs = 0x04; if (name == NULL) { name = (guint8 *) g_convert((const gchar *) rad->name, -1, "UCS-2BE", "UTF-8//TRANSLIT", NULL, &len, NULL); dcs = 0x08; if (name == NULL) return FALSE; } 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 gboolean build_dataobj_broadcast_network_information( struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const struct stk_broadcast_network_information *bni = data; unsigned char 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 gboolean build_dataobj_routing_area_id(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const struct stk_routing_area_info *rai = data; unsigned char tag = STK_DATA_OBJECT_TYPE_ROUTING_AREA_INFO; guint8 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 gboolean build_dataobj_update_attach_type(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const enum stk_update_attach_type *type = data; unsigned char 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 gboolean build_dataobj_rejection_cause_code(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const enum stk_rejection_cause_code *cause = data; unsigned char 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 gboolean build_dataobj_eps_pdn_conn_params(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const struct stk_common_byte_array *params = data; unsigned char 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 gboolean build_dataobj_tracking_area_id(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const struct stk_tracking_area_id *tai = data; unsigned char tag = STK_DATA_OBJECT_TYPE_TRACKING_AREA_ID; guint8 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 gboolean 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 *); gboolean cr = (flags & DATAOBJ_FLAG_CR) ? TRUE : FALSE; if (builder_func(tlv, data, cr) != TRUE) return FALSE; builder_func = va_arg(args, dataobj_writer); } va_end(args); return TRUE; } static gboolean 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 gboolean 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) != TRUE) 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) != TRUE) 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) != TRUE) 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) != TRUE) return FALSE; return TRUE; } return FALSE; } static gboolean 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) != TRUE) 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 gboolean 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) != TRUE) return FALSE; } return build_dataobj(builder, build_dataobj_channel_data_length, DATAOBJ_FLAG_CR, &response->receive_data.rx_remaining, NULL); } static gboolean 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 unsigned char *stk_pdu_from_response(const struct stk_response *response, unsigned int *out_length) { struct stk_tlv_builder builder; gboolean ok = TRUE; unsigned char tag; static unsigned char 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) == FALSE) return NULL; if (stk_tlv_builder_append_byte(&builder, response->number) == FALSE) return NULL; if (stk_tlv_builder_append_byte(&builder, response->type) == FALSE) return NULL; if (stk_tlv_builder_append_byte(&builder, response->qualifier) == FALSE) return NULL; if (stk_tlv_builder_close_container(&builder) == FALSE) 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) == FALSE) return NULL; if (stk_tlv_builder_append_byte(&builder, response->src) == FALSE) return NULL; if (stk_tlv_builder_append_byte(&builder, response->dst) == FALSE) return NULL; if (stk_tlv_builder_close_container(&builder) == FALSE) return NULL; if (build_dataobj_result(&builder, &response->result, TRUE) != 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 != TRUE) return NULL; if (out_length) *out_length = stk_tlv_builder_get_length(&builder); return pdu; } /* Described in TS 102.223 Section 8.7 */ static gboolean build_envelope_dataobj_device_ids(struct stk_tlv_builder *tlv, const void *data, gboolean cr) { const struct stk_envelope *envelope = data; unsigned char 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 gboolean build_envelope_call_control( struct stk_tlv_builder *builder, const struct stk_envelope *envelope) { const struct stk_envelope_call_control *cc = &envelope->call_control; gboolean ok = FALSE; if (build_dataobj(builder, build_envelope_dataobj_device_ids, DATAOBJ_FLAG_CR, envelope, NULL) != TRUE) 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 != TRUE) 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 gboolean 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) == FALSE) 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 gboolean 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) == FALSE) return FALSE; for (i = 0; i < ta->count; i++) if (build_dataobj(builder, build_dataobj_registry_application_data, 0, &ta->list[i], NULL) == FALSE) return FALSE; return build_dataobj(builder, build_dataobj_last_envelope, 0, &ta->last, NULL); } const unsigned char *stk_pdu_from_envelope(const struct stk_envelope *envelope, unsigned int *out_length) { struct ber_tlv_builder btlv; struct stk_tlv_builder builder; gboolean ok = TRUE; static unsigned char buffer[512]; unsigned char *pdu; if (ber_tlv_builder_init(&btlv, buffer, sizeof(buffer)) != TRUE) return NULL; if (stk_tlv_builder_recurse(&builder, &btlv, envelope->type) != TRUE) 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 != TRUE) 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(GString *string, guint16 attr) { guint code = attr & 0xFF; guint color = (attr >> 8) & 0xFF; if ((code & ~STK_TEXT_FORMAT_ALIGN_MASK) || color) g_string_append(string, ""); if ((code & STK_TEXT_FORMAT_ALIGN_MASK) != STK_TEXT_FORMAT_NO_ALIGN) g_string_append(string, ""); } static void start_format(GString *string, guint16 attr) { guint8 code = attr & 0xFF; guint8 color = (attr >> 8) & 0xFF; guint8 align = code & STK_TEXT_FORMAT_ALIGN_MASK; guint8 font = code & STK_TEXT_FORMAT_FONT_MASK; guint8 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) g_string_append(string, "
"); break; case STK_TEXT_FORMAT_CENTER_ALIGN: g_string_append(string, "text-align: center;\">"); break; case STK_TEXT_FORMAT_LEFT_ALIGN: g_string_append(string, "text-align: left;\">"); break; } if ((font == 0) && (style == 0) && (color == 0)) return; /* font, style, and color are inline */ g_string_append(string, ""); } char *stk_text_to_html(const char *utf8, const unsigned short *attrs, int num_attrs) { long text_len = g_utf8_strlen(utf8, -1); GString *string = g_string_sized_new(strlen(utf8) + 1); short *formats; int pos, i, j; guint16 start, end, len, attr, prev_attr; guint8 code, color, align; const char *text = utf8; int attrs_len = num_attrs * 4; formats = g_try_new0(gint16, (text_len + 1)); if (formats == NULL) { g_string_free(string, TRUE); return NULL; } /* 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++) { 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; switch (g_utf8_get_char(text)) { case '\n': g_string_append(string, "
"); break; case '\r': { char *next = g_utf8_next_char(text); gunichar c = g_utf8_get_char(next); g_string_append(string, "
"); if ((pos + 1 < text_len) && (c == '\n')) { text = g_utf8_next_char(text); pos++; } break; } case '<': g_string_append(string, "<"); break; case '>': g_string_append(string, ">"); break; case '&': g_string_append(string, "&"); break; default: g_string_append_unichar(string, g_utf8_get_char(text)); } text = g_utf8_next_char(text); } g_free(formats); /* return characters from string. Caller must free char data */ return g_string_free(string, FALSE); } 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 unsigned char *img, unsigned int len, enum stk_img_scheme scheme, const unsigned char *clut, unsigned short clut_len) { guint8 width, height; unsigned int ncolors, nbits, entry, cpp; unsigned int i, j; int bit, k; GString *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 = g_string_sized_new(strlen(xpm_header) + strlen(declaration) + 19 + ((cpp + 14) * ncolors) + (width * height * cpp) + (4 * height) + 2); if (xpm == NULL) return NULL; /* add header, declaration, values */ g_string_append(xpm, xpm_header); g_string_append(xpm, declaration); g_string_append_printf(xpm, "\"%d %d %d %d\",\n", width, height, ncolors, cpp); /* create colors */ if (scheme == STK_IMG_SCHEME_BASIC) { g_string_append(xpm, "\"0\tc #000000\",\n"); g_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) g_string_append_printf(xpm, "\"%s\tc None\",\n", c); else g_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++) { g_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'; } g_string_append_printf(xpm, "%s", c); } g_string_append(xpm, "\",\n"); } g_string_append(xpm, "};"); /* Caller must free char data */ return g_string_free(xpm, FALSE); }