~awe/phablet-extras/ofono-lp1204683

« back to all changes in this revision

Viewing changes to src/simutil.c

  • Committer: Bazaar Package Importer
  • Author(s): Andres Salomon
  • Date: 2009-08-15 15:55:11 UTC
  • Revision ID: james.westby@ubuntu.com-20090815155511-frst06dijguhyfi4
Tags: upstream-0.3
ImportĀ upstreamĀ versionĀ 0.3

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 *
 
3
 *  oFono - Open Source Telephony
 
4
 *
 
5
 *  Copyright (C) 2008-2009  Intel Corporation. All rights reserved.
 
6
 *
 
7
 *  This program is free software; you can redistribute it and/or modify
 
8
 *  it under the terms of the GNU General Public License version 2 as
 
9
 *  published by the Free Software Foundation.
 
10
 *
 
11
 *  This program is distributed in the hope that it will be useful,
 
12
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 
13
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
14
 *  GNU General Public License for more details.
 
15
 *
 
16
 *  You should have received a copy of the GNU General Public License
 
17
 *  along with this program; if not, write to the Free Software
 
18
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 
19
 *
 
20
 */
 
21
 
 
22
#ifdef HAVE_CONFIG_H
 
23
#include <config.h>
 
24
#endif
 
25
 
 
26
#include <string.h>
 
27
 
 
28
#include <glib.h>
 
29
 
 
30
#include "driver.h"
 
31
#include "simutil.h"
 
32
#include "util.h"
 
33
#include "smsutil.h"
 
34
 
 
35
struct sim_eons {
 
36
        struct sim_eons_operator_info *pnn_list;
 
37
        GSList *opl_list;
 
38
        gboolean pnn_valid;
 
39
        int pnn_max;
 
40
};
 
41
 
 
42
struct spdi_operator {
 
43
        char mcc[OFONO_MAX_MCC_LENGTH + 1];
 
44
        char mnc[OFONO_MAX_MNC_LENGTH + 1];
 
45
};
 
46
 
 
47
struct opl_operator {
 
48
        char mcc[OFONO_MAX_MCC_LENGTH + 1];
 
49
        char mnc[OFONO_MAX_MNC_LENGTH + 1];
 
50
        guint16 lac_tac_low;
 
51
        guint16 lac_tac_high;
 
52
        guint8 id;
 
53
};
 
54
 
 
55
/* Parse ASN.1 Basic Encoding Rules TLVs per ISO/IEC 7816 */
 
56
static const guint8 *ber_tlv_find_by_tag(const guint8 *pdu, guint8 in_tag,
 
57
                                                int in_len, int *out_len)
 
58
{
 
59
        guint8 tag;
 
60
        int len;
 
61
        const guint8 *end = pdu + in_len;
 
62
 
 
63
        do {
 
64
                while (pdu < end && (*pdu == 0x00 || *pdu == 0xff))
 
65
                        pdu++;
 
66
                if (pdu == end)
 
67
                        break;
 
68
 
 
69
                tag = *pdu++;
 
70
                if (!(0x1f & ~tag))
 
71
                        while (pdu < end && (*pdu++ & 0x80))
 
72
                                ;
 
73
                if (pdu == end)
 
74
                        break;
 
75
 
 
76
                for (len = 0; pdu + 1 < end && (*pdu & 0x80);
 
77
                                len = (len | (*pdu++ & 0x7f)) << 7)
 
78
                        ;
 
79
 
 
80
                if (*pdu & 0x80)
 
81
                        break;
 
82
                len |= *pdu++;
 
83
 
 
84
                if (tag == in_tag && pdu + len <= end) {
 
85
                        if (out_len)
 
86
                                *out_len = len;
 
87
                        return pdu;
 
88
                }
 
89
 
 
90
                pdu += len;
 
91
        } while (pdu < end);
 
92
 
 
93
        return NULL;
 
94
}
 
95
 
 
96
static char *sim_network_name_parse(const unsigned char *buffer, int length,
 
97
                                        gboolean *add_ci)
 
98
{
 
99
        char *ret = NULL;
 
100
        unsigned char *endp;
 
101
        unsigned char dcs;
 
102
        int i;
 
103
        gboolean ci = FALSE;
 
104
 
 
105
        if (length < 1)
 
106
                return NULL;
 
107
 
 
108
        dcs = *buffer++;
 
109
        length--;
 
110
 
 
111
        /* "The MS should add the letters for the Country's Initials and a
 
112
         * separator (e.g. a space)" */
 
113
        if (is_bit_set(dcs, 4))
 
114
                ci = TRUE;
 
115
 
 
116
        switch (dcs & (7 << 4)) {
 
117
        case 0x00:
 
118
                endp = memchr(buffer, 0xff, length);
 
119
                if (endp)
 
120
                        length = endp - buffer;
 
121
                ret = convert_gsm_to_utf8(buffer, length,
 
122
                                NULL, NULL, 0xff);
 
123
                break;
 
124
        case 0x10:
 
125
                if ((length % 2) == 1) {
 
126
                        if (buffer[length - 1] != 0xff)
 
127
                                return NULL;
 
128
 
 
129
                        length = length - 1;
 
130
                }
 
131
 
 
132
                for (i = 0; i < length; i += 2)
 
133
                        if (buffer[i] == 0xff && buffer[i + 1] == 0xff)
 
134
                                break;
 
135
 
 
136
                ret = g_convert((const char *)buffer, length,
 
137
                                        "UTF-8//TRANSLIT", "UCS-2BE",
 
138
                                        NULL, NULL, NULL);
 
139
                break;
 
140
        }
 
141
 
 
142
        if (add_ci)
 
143
                *add_ci = ci;
 
144
 
 
145
        return ret;
 
146
}
 
147
 
 
148
static void parse_mcc_mnc(const guint8 *bcd, char *mcc, char *mnc)
 
149
{
 
150
        static const char digit_lut[] = "0123456789*#abd\0";
 
151
        guint8 digit;
 
152
 
 
153
        digit = (bcd[0] >> 0) & 0xf;
 
154
        *mcc++ = digit_lut[digit];
 
155
 
 
156
        digit = (bcd[0] >> 4) & 0xf;
 
157
        *mcc++ = digit_lut[digit];
 
158
 
 
159
        digit = (bcd[1] >> 0) & 0xf;
 
160
        *mcc++ = digit_lut[digit];
 
161
 
 
162
        digit = (bcd[2] >> 0) & 0xf;
 
163
        *mnc++ = digit_lut[digit];
 
164
 
 
165
        digit = (bcd[2] >> 4) & 0xf;
 
166
        *mnc++ = digit_lut[digit];
 
167
 
 
168
        digit = (bcd[1] >> 4) & 0xf;
 
169
        *mnc++ = digit_lut[digit];
 
170
}
 
171
 
 
172
static gint spdi_operator_compare(gconstpointer a, gconstpointer b)
 
173
{
 
174
        const struct spdi_operator *opa = a;
 
175
        const struct spdi_operator *opb = b;
 
176
        gint r = strcmp(opa->mcc, opb->mcc);
 
177
 
 
178
        if (r)
 
179
                return r;
 
180
 
 
181
        return strcmp(opa->mnc, opb->mnc);
 
182
}
 
183
 
 
184
struct sim_spdi {
 
185
        GSList *operators;
 
186
};
 
187
 
 
188
struct sim_spdi *sim_spdi_new(const guint8 *tlv, int length)
 
189
{
 
190
        const guint8 *plmn_list;
 
191
        struct sim_spdi *spdi;
 
192
        struct spdi_operator *oper;
 
193
        int tlv_length;
 
194
 
 
195
        if (length <= 5)
 
196
                return NULL;
 
197
 
 
198
        plmn_list = ber_tlv_find_by_tag(tlv, 0x80, length, &tlv_length);
 
199
 
 
200
        if (!plmn_list)
 
201
                return NULL;
 
202
 
 
203
        spdi = g_new0(struct sim_spdi, 1);
 
204
 
 
205
        for (tlv_length /= 3; tlv_length--; plmn_list += 3) {
 
206
                if ((plmn_list[0] & plmn_list[1] & plmn_list[2]) == 0xff)
 
207
                        continue;
 
208
 
 
209
                oper = g_new0(struct spdi_operator, 1);
 
210
 
 
211
                parse_mcc_mnc(plmn_list, oper->mcc, oper->mnc);
 
212
                spdi->operators = g_slist_insert_sorted(spdi->operators, oper,
 
213
                                                spdi_operator_compare);
 
214
        }
 
215
 
 
216
        return spdi;
 
217
}
 
218
 
 
219
gboolean sim_spdi_lookup(struct sim_spdi *spdi,
 
220
                                const char *mcc, const char *mnc)
 
221
{
 
222
        struct spdi_operator spdi_op;
 
223
 
 
224
        g_strlcpy(spdi_op.mcc, mcc, sizeof(spdi_op.mcc));
 
225
        g_strlcpy(spdi_op.mnc, mnc, sizeof(spdi_op.mnc));
 
226
 
 
227
        return g_slist_find_custom(spdi->operators, &spdi_op,
 
228
                                        spdi_operator_compare) != NULL;
 
229
}
 
230
 
 
231
void sim_spdi_free(struct sim_spdi *spdi)
 
232
{
 
233
        g_slist_foreach(spdi->operators, (GFunc)g_free, NULL);
 
234
        g_slist_free(spdi->operators);
 
235
        g_free(spdi);
 
236
}
 
237
 
 
238
static void pnn_operator_free(struct sim_eons_operator_info *oper)
 
239
{
 
240
        g_free(oper->info);
 
241
        g_free(oper->shortname);
 
242
        g_free(oper->longname);
 
243
}
 
244
 
 
245
struct sim_eons *sim_eons_new(int pnn_records)
 
246
{
 
247
        struct sim_eons *eons = g_new0(struct sim_eons, 1);
 
248
 
 
249
        eons->pnn_list = g_new0(struct sim_eons_operator_info, pnn_records);
 
250
        eons->pnn_max = pnn_records;
 
251
 
 
252
        return eons;
 
253
}
 
254
 
 
255
gboolean sim_eons_pnn_is_empty(struct sim_eons *eons)
 
256
{
 
257
        return !eons->pnn_valid;
 
258
}
 
259
 
 
260
void sim_eons_add_pnn_record(struct sim_eons *eons, int record,
 
261
                                const guint8 *tlv, int length)
 
262
{
 
263
        const unsigned char *name;
 
264
        int namelength;
 
265
        struct sim_eons_operator_info *oper = &eons->pnn_list[record-1];
 
266
 
 
267
        name = ber_tlv_find_by_tag(tlv, 0x43, length, &namelength);
 
268
 
 
269
        if (!name || !namelength)
 
270
                return;
 
271
 
 
272
        oper->longname = sim_network_name_parse(name, namelength,
 
273
                                                &oper->long_ci);
 
274
 
 
275
        name = ber_tlv_find_by_tag(tlv, 0x45, length, &namelength);
 
276
 
 
277
        if (name && namelength)
 
278
                oper->shortname = sim_network_name_parse(name, namelength,
 
279
                                                        &oper->short_ci);
 
280
 
 
281
        name = ber_tlv_find_by_tag(tlv, 0x80, length, &namelength);
 
282
 
 
283
        if (name && namelength)
 
284
                oper->info = sim_string_to_utf8(name, namelength);
 
285
 
 
286
        eons->pnn_valid = TRUE;
 
287
}
 
288
 
 
289
static struct opl_operator *opl_operator_alloc(const guint8 *record)
 
290
{
 
291
        struct opl_operator *oper = g_new0(struct opl_operator, 1);
 
292
 
 
293
        parse_mcc_mnc(record, oper->mcc, oper->mnc);
 
294
        record += 3;
 
295
 
 
296
        oper->lac_tac_low = (record[0] << 8) | record[1];
 
297
        record += 2;
 
298
        oper->lac_tac_high = (record[0] << 8) | record[1];
 
299
        record += 2;
 
300
 
 
301
        oper->id = record[0];
 
302
 
 
303
        return oper;
 
304
}
 
305
 
 
306
void sim_eons_add_opl_record(struct sim_eons *eons,
 
307
                                const guint8 *contents, int length)
 
308
{
 
309
        struct opl_operator *oper;
 
310
 
 
311
        oper = opl_operator_alloc(contents);
 
312
 
 
313
        if (oper->id > eons->pnn_max) {
 
314
                g_free(oper);
 
315
                return;
 
316
        }
 
317
 
 
318
        eons->opl_list = g_slist_prepend(eons->opl_list, oper);
 
319
}
 
320
 
 
321
void sim_eons_optimize(struct sim_eons *eons)
 
322
{
 
323
        eons->opl_list = g_slist_reverse(eons->opl_list);
 
324
}
 
325
 
 
326
void sim_eons_free(struct sim_eons *eons)
 
327
{
 
328
        int i;
 
329
 
 
330
        for (i = 0; i < eons->pnn_max; i++)
 
331
                pnn_operator_free(eons->pnn_list + i);
 
332
 
 
333
        g_free(eons->pnn_list);
 
334
 
 
335
        g_slist_foreach(eons->opl_list, (GFunc)g_free, NULL);
 
336
        g_slist_free(eons->opl_list);
 
337
 
 
338
        g_free(eons);
 
339
}
 
340
 
 
341
static const struct sim_eons_operator_info *
 
342
        sim_eons_lookup_common(struct sim_eons *eons,
 
343
                                const char *mcc, const char *mnc,
 
344
                                gboolean have_lac, guint16 lac)
 
345
{
 
346
        GSList *l;
 
347
        const struct opl_operator *opl;
 
348
        int i;
 
349
 
 
350
        for (l = eons->opl_list; l; l = l->next) {
 
351
                opl = l->data;
 
352
 
 
353
                for (i = 0; i < OFONO_MAX_MCC_LENGTH; i++)
 
354
                        if (mcc[i] != opl->mcc[i] &&
 
355
                                        !(opl->mcc[i] == 'b' && mcc[i]))
 
356
                                break;
 
357
                if (i < OFONO_MAX_MCC_LENGTH)
 
358
                        continue;
 
359
 
 
360
                for (i = 0; i < OFONO_MAX_MNC_LENGTH; i++)
 
361
                        if (mnc[i] != opl->mnc[i] &&
 
362
                                        !(opl->mnc[i] == 'b' && mnc[i]))
 
363
                                break;
 
364
                if (i < OFONO_MAX_MNC_LENGTH)
 
365
                        continue;
 
366
 
 
367
                if (opl->lac_tac_low == 0 && opl->lac_tac_high == 0xfffe)
 
368
                        break;
 
369
 
 
370
                if (have_lac == FALSE)
 
371
                        continue;
 
372
 
 
373
                if ((lac >= opl->lac_tac_low) && (lac <= opl->lac_tac_high))
 
374
                        break;
 
375
        }
 
376
 
 
377
        if (!l)
 
378
                return NULL;
 
379
 
 
380
        opl = l->data;
 
381
 
 
382
        /* 0 is not a valid record id */
 
383
        if (opl->id == 0)
 
384
                return NULL;
 
385
 
 
386
        return &eons->pnn_list[opl->id - 1];
 
387
}
 
388
 
 
389
const struct sim_eons_operator_info *sim_eons_lookup(struct sim_eons *eons,
 
390
                                                const char *mcc,
 
391
                                                const char *mnc)
 
392
{
 
393
        return sim_eons_lookup_common(eons, mcc, mnc, FALSE, 0);
 
394
}
 
395
 
 
396
const struct sim_eons_operator_info *sim_eons_lookup_with_lac(
 
397
                                                        struct sim_eons *eons,
 
398
                                                        const char *mcc,
 
399
                                                        const char *mnc,
 
400
                                                        guint16 lac)
 
401
{
 
402
        return sim_eons_lookup_common(eons, mcc, mnc, TRUE, lac);
 
403
}
 
404
 
 
405
gboolean sim_adn_parse(const unsigned char *data, int length,
 
406
                        struct ofono_phone_number *ph)
 
407
{
 
408
        int number_len;
 
409
        int ton_npi;
 
410
 
 
411
        if (length < 14)
 
412
                return FALSE;
 
413
 
 
414
        /* Skip Alpha-Identifier field */
 
415
        data += length - 14;
 
416
 
 
417
        number_len = *data++;
 
418
        ton_npi = *data++;
 
419
 
 
420
        if (number_len > 11 || ton_npi == 0xff)
 
421
                return FALSE;
 
422
 
 
423
        ph->type = ton_npi;
 
424
 
 
425
        /* BCD coded, however the TON/NPI is given by the first byte */
 
426
        number_len = (number_len - 1) * 2;
 
427
 
 
428
        extract_bcd_number(data, number_len, ph->number);
 
429
 
 
430
        return TRUE;
 
431
}
 
432
 
 
433
void sim_adn_build(unsigned char *data, int length,
 
434
                        const struct ofono_phone_number *ph)
 
435
{
 
436
        int number_len = strlen(ph->number);
 
437
 
 
438
        /* Alpha-Identifier field */
 
439
        if (length > 14) {
 
440
                memset(data, 0xff, length - 14);
 
441
                data += length - 14;
 
442
        }
 
443
 
 
444
        number_len = (number_len + 1) / 2;
 
445
        *data++ = number_len + 1;
 
446
 
 
447
        /* Use given number type and 'Unknown' for Numbering Plan */
 
448
        *data++ = ph->type;
 
449
 
 
450
        encode_bcd_number(ph->number, data);
 
451
        memset(data + number_len, 0xff, 10 - number_len);
 
452
        data += 10;
 
453
 
 
454
        /* CCP1 unused */
 
455
        *data++ = 0xff;
 
456
        /* Ext1 unused */
 
457
        *data++ = 0xff;
 
458
}