~binli/ubuntu/vivid/modemmanager/lp1441095

« back to all changes in this revision

Viewing changes to src/mm-modem-helpers.c

  • Committer: Package Import Robot
  • Author(s): Mathieu Trudel-Lapierre, Mathieu Trudel-Lapierre, Michael Biebl, Marius B. Kotsbak
  • Date: 2013-06-11 10:35:42 UTC
  • mfrom: (1.2.1) (16.1.20 raring-proposed)
  • Revision ID: package-import@ubuntu.com-20130611103542-xm7vvifc252viorj
Tags: 0.7.991-1
[ Mathieu Trudel-Lapierre ]
* New upstream release.
* debian/patches/handle_data07_capabilities_probing.patch,
  debian/patches/git_skip_add_utf8_check_219424a.patch,
  debian/patches/git_lte_etsi_mode_0af47c7.patch,
  debian/patches/git_lte_4g_parsing_90489ae.patch,
  debian/patches/git_lp1015328_segfault_in_clck_parser_318aaa0.patch,
  debian/patches/git_better_handle_ucs2_convert_e07c216.patch: Dropped,
  these patches were cherry-picks; included upstream
* debian/patches/ericsson_h5321gw_usbids.patch: dropped, included upstream.
* debian/control:
  - clean up build-depends: remove xsltproc, bump
    libglib2.0-dev to (>= 2.30.2), libgudev-1.0-dev to (>= 147).
  - Add gtk-doc-tools to Build-Depends.
  - Add libqmi-glib-dev to Build-Depends.
  - Add gnome-common to Build-Depends.
  - Bump debhelper Build-Depends to >= 9.
  - Make sure libmm-glib0 pre-depends on multiarch-support (lintian).
  - Make libmm-glib0 Multi-Arch: same.
  - Make sure all binary packages (except modemmanager-dbg) are priority
    optional.
  - Update short descriptions.
  - Breaks network-manager (<< 0.9.8.2-1) since otherwise NetworkManager
    will not see ModemManager with its new API.
* debian/compat: bump to compat level 9.
* debian/rules:
  - Replace --with-docs with --enable-gtk-doc.
  - Fix autoreconf to run gnome-autogen.sh.
  - Drop the override for installdocs; docs/spec.html isn't being built
    anymore.
  - Run dh_install with --fail-missing.
  - Remove test pppd plugin which we shouldn't install.
  - Drop old cruft for getting git snapshots.
  - We don't need to exclude the pppd path from makeshlibs, since nothing
    gets installed there.
* debian/patches/lp700316_usb_blacklist.patch: refreshed.
* debian/patches/arduino-blacklist.patch: refreshed.
* debian/patches/linux-default-usb-id.patch: refreshed.
* debian/*.install: make sure the files are properly installed given the new
  packages, also take into account multiarch paths.
* debian/modemmanager.install: install the new mmcli binary.
* debian/ubuntu/modemmanager.upstart: fix the name for the ModemManager
  binary, since it was changed upstream.
* debian/patches/dbus_remove_max_replies_per_connection_limit.patch: dropped,
  included upstream.

[ Michael Biebl ]
* Add symbols file for libmm-glib0.

[ Marius B. Kotsbak ]
* Added binary packages modemmanager-doc and libmm-glib-doc for gtk-docs.
* Split out modemmanager-dev package containing header files and .pc file.
  - add dependency on modemmanager-dev from libmm-glib-dev
    as stated in "mm-glib.pc"
  - add proper replaces/breaks for modemmanager-dev because of moved files
* Build-depends: added "libglib2.0-doc" for the cross references in the doc
  to work.
* debian/rules: make dh_makeshlibs override multiarch aware.
* Update standards version to current 3.9.4.

Show diffs side-by-side

added added

removed removed

Lines of Context:
11
11
 * GNU General Public License for more details:
12
12
 *
13
13
 * Copyright (C) 2008 - 2009 Novell, Inc.
14
 
 * Copyright (C) 2009 - 2011 Red Hat, Inc.
 
14
 * Copyright (C) 2009 - 2012 Red Hat, Inc.
 
15
 * Copyright (C) 2012 Google, Inc.
15
16
 */
16
17
 
17
18
#include <config.h>
21
22
#include <string.h>
22
23
#include <ctype.h>
23
24
#include <stdlib.h>
24
 
#include <errno.h>
25
 
 
26
 
#include "mm-errors.h"
 
25
#include <arpa/inet.h>
 
26
 
 
27
#include <ModemManager.h>
 
28
#define _LIBMM_INSIDE_MM
 
29
#include <libmm-glib.h>
 
30
 
 
31
#include "mm-sms-part.h"
27
32
#include "mm-modem-helpers.h"
28
33
#include "mm-log.h"
29
34
 
30
 
const char *
31
 
mm_strip_tag (const char *str, const char *cmd)
32
 
{
33
 
    const char *p = str;
 
35
/*****************************************************************************/
 
36
 
 
37
gchar *
 
38
mm_strip_quotes (gchar *str)
 
39
{
 
40
    gsize len;
 
41
 
 
42
    if (!str)
 
43
        return NULL;
 
44
 
 
45
    len = strlen (str);
 
46
    if ((len >= 2) && (str[0] == '"') && (str[len - 1] == '"')) {
 
47
        str[0] = ' ';
 
48
        str[len - 1] = ' ';
 
49
    }
 
50
 
 
51
    return g_strstrip (str);
 
52
}
 
53
 
 
54
const gchar *
 
55
mm_strip_tag (const gchar *str, const gchar *cmd)
 
56
{
 
57
    const gchar *p = str;
34
58
 
35
59
    if (p) {
36
60
        if (!strncmp (p, cmd, strlen (cmd)))
38
62
        while (isspace (*p))
39
63
            p++;
40
64
    }
 
65
 
41
66
    return p;
42
67
}
43
68
 
 
69
/*****************************************************************************/
 
70
 
 
71
guint
 
72
mm_count_bits_set (gulong number)
 
73
{
 
74
    guint c;
 
75
 
 
76
    for (c = 0; number; c++)
 
77
        number &= number - 1;
 
78
    return c;
 
79
}
 
80
 
 
81
/*****************************************************************************/
 
82
 
 
83
gchar *
 
84
mm_create_device_identifier (guint vid,
 
85
                             guint pid,
 
86
                             const gchar *ati,
 
87
                             const gchar *ati1,
 
88
                             const gchar *gsn,
 
89
                             const gchar *revision,
 
90
                             const gchar *model,
 
91
                             const gchar *manf)
 
92
{
 
93
    GString *devid, *msg = NULL;
 
94
    GChecksum *sum;
 
95
    gchar *p, *ret = NULL;
 
96
    gchar str_vid[10], str_pid[10];
 
97
 
 
98
    /* Build up the device identifier */
 
99
    devid = g_string_sized_new (50);
 
100
    if (ati)
 
101
        g_string_append (devid, ati);
 
102
    if (ati1) {
 
103
        /* Only append "ATI1" if it's differnet than "ATI" */
 
104
        if (!ati || (strcmp (ati, ati1) != 0))
 
105
            g_string_append (devid, ati1);
 
106
    }
 
107
    if (gsn)
 
108
        g_string_append (devid, gsn);
 
109
    if (revision)
 
110
        g_string_append (devid, revision);
 
111
    if (model)
 
112
        g_string_append (devid, model);
 
113
    if (manf)
 
114
        g_string_append (devid, manf);
 
115
 
 
116
    if (!strlen (devid->str)) {
 
117
        g_string_free (devid, TRUE);
 
118
        return NULL;
 
119
    }
 
120
 
 
121
    p = devid->str;
 
122
    msg = g_string_sized_new (strlen (devid->str) + 17);
 
123
 
 
124
    sum = g_checksum_new (G_CHECKSUM_SHA1);
 
125
 
 
126
    if (vid) {
 
127
        snprintf (str_vid, sizeof (str_vid) - 1, "%08x", vid);
 
128
        g_checksum_update (sum, (const guchar *) &str_vid[0], strlen (str_vid));
 
129
        g_string_append_printf (msg, "%08x", vid);
 
130
    }
 
131
    if (vid) {
 
132
        snprintf (str_pid, sizeof (str_pid) - 1, "%08x", pid);
 
133
        g_checksum_update (sum, (const guchar *) &str_pid[0], strlen (str_pid));
 
134
        g_string_append_printf (msg, "%08x", pid);
 
135
    }
 
136
 
 
137
    while (*p) {
 
138
        /* Strip spaces and linebreaks */
 
139
        if (!isblank (*p) && !isspace (*p) && isascii (*p)) {
 
140
            g_checksum_update (sum, (const guchar *) p, 1);
 
141
            g_string_append_c (msg, *p);
 
142
        }
 
143
        p++;
 
144
    }
 
145
    ret = g_strdup (g_checksum_get_string (sum));
 
146
    g_checksum_free (sum);
 
147
 
 
148
    mm_dbg ("Device ID source '%s'", msg->str);
 
149
    mm_dbg ("Device ID '%s'", ret);
 
150
    g_string_free (msg, TRUE);
 
151
    g_string_free (devid, TRUE);
 
152
 
 
153
    return ret;
 
154
}
 
155
 
 
156
/*****************************************************************************/
 
157
 
 
158
guint
 
159
mm_netmask_to_cidr (const gchar *netmask)
 
160
{
 
161
    guint32 num = 0;
 
162
 
 
163
    inet_pton (AF_INET, netmask, &num);
 
164
    return mm_count_bits_set (num);
 
165
}
 
166
 
 
167
/*****************************************************************************/
 
168
 
 
169
GArray *
 
170
mm_filter_current_bands (const GArray *supported_bands,
 
171
                         const GArray *current_bands)
 
172
{
 
173
    /* We will assure that the list given in 'current' bands maps the list
 
174
     * given in 'supported' bands, unless 'UNKNOWN' or 'ANY' is given, of
 
175
     * course */
 
176
    guint i;
 
177
    GArray *filtered;
 
178
 
 
179
    if (!supported_bands ||
 
180
        supported_bands->len == 0 ||
 
181
        !current_bands ||
 
182
        current_bands->len == 0)
 
183
        return NULL;
 
184
 
 
185
    if (supported_bands->len == 1 &&
 
186
        (g_array_index (supported_bands, MMModemBand, 0) == MM_MODEM_BAND_UNKNOWN ||
 
187
         g_array_index (supported_bands, MMModemBand, 0) == MM_MODEM_BAND_ANY))
 
188
        return NULL;
 
189
 
 
190
    if (current_bands->len == 1 &&
 
191
        (g_array_index (current_bands, MMModemBand, 0) == MM_MODEM_BAND_UNKNOWN ||
 
192
         g_array_index (current_bands, MMModemBand, 0) == MM_MODEM_BAND_ANY))
 
193
        return NULL;
 
194
 
 
195
    filtered = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), current_bands->len);
 
196
 
 
197
    for (i = 0; i < current_bands->len; i++) {
 
198
        guint j;
 
199
 
 
200
        for (j = 0; j < supported_bands->len; j++) {
 
201
            if (g_array_index (supported_bands, MMModemBand, j) == g_array_index (current_bands, MMModemBand, i)) {
 
202
                g_array_append_val (filtered, g_array_index (current_bands, MMModemBand, i));
 
203
                /* Found */
 
204
                break;
 
205
            }
 
206
        }
 
207
    }
 
208
 
 
209
    if (filtered->len == 0) {
 
210
        g_array_unref (filtered);
 
211
        return NULL;
 
212
    }
 
213
 
 
214
    return filtered;
 
215
}
 
216
 
 
217
/*****************************************************************************/
 
218
 
 
219
gchar *
 
220
mm_new_iso8601_time (guint year,
 
221
                     guint month,
 
222
                     guint day,
 
223
                     guint hour,
 
224
                     guint minute,
 
225
                     guint second,
 
226
                     gboolean have_offset,
 
227
                     gint offset_minutes)
 
228
{
 
229
    GString *str;
 
230
 
 
231
    str = g_string_sized_new (30);
 
232
    g_string_append_printf (str, "%04d-%02d-%02dT%02d:%02d:%02d",
 
233
                            year, month, day, hour, minute, second);
 
234
    if (have_offset) {
 
235
        if (offset_minutes >=0 ) {
 
236
            g_string_append_printf (str, "+%02d:%02d",
 
237
                                    offset_minutes / 60,
 
238
                                    offset_minutes % 60);
 
239
        } else {
 
240
            offset_minutes *= -1;
 
241
            g_string_append_printf (str, "-%02d:%02d",
 
242
                                    offset_minutes / 60,
 
243
                                    offset_minutes % 60);
 
244
        }
 
245
    }
 
246
    return g_string_free (str, FALSE);
 
247
}
 
248
 
 
249
/*****************************************************************************/
 
250
 
 
251
GArray *
 
252
mm_filter_supported_modes (const GArray *all,
 
253
                           const GArray *supported_combinations)
 
254
{
 
255
    MMModemModeCombination all_item;
 
256
    guint i;
 
257
    GArray *filtered_combinations;
 
258
    gboolean all_item_added = FALSE;
 
259
 
 
260
    g_return_val_if_fail (all != NULL, NULL);
 
261
    g_return_val_if_fail (all->len == 1, NULL);
 
262
    g_return_val_if_fail (supported_combinations != NULL, NULL);
 
263
 
 
264
    all_item = g_array_index (all, MMModemModeCombination, 0);
 
265
    g_return_val_if_fail (all_item.allowed != MM_MODEM_MODE_NONE, NULL);
 
266
 
 
267
    /* We will filter out all combinations which have modes not listed in 'all' */
 
268
    filtered_combinations = g_array_sized_new (FALSE, FALSE, sizeof (MMModemModeCombination), supported_combinations->len);
 
269
    for (i = 0; i < supported_combinations->len; i++) {
 
270
        MMModemModeCombination *mode;
 
271
 
 
272
        mode = &g_array_index (supported_combinations, MMModemModeCombination, i);
 
273
        if (!(mode->allowed & ~all_item.allowed)) {
 
274
            /* Compare only 'allowed', *not* preferred. If there is at least one item with allowed
 
275
             * containing all supported modes, we're already good to go. This allows us to have a
 
276
             * default with preferred != NONE (e.g. Wavecom 2G modem with allowed=CS+2G and
 
277
             * preferred=2G */
 
278
            if (all_item.allowed == mode->allowed)
 
279
                all_item_added = TRUE;
 
280
            g_array_append_val (filtered_combinations, *mode);
 
281
        }
 
282
    }
 
283
 
 
284
    if (filtered_combinations->len == 0)
 
285
        mm_warn ("All supported mode combinations were filtered out.");
 
286
 
 
287
    /* Add default entry with the generic mask including all items */
 
288
    if (!all_item_added) {
 
289
        mm_dbg ("Adding an explicit item with all supported modes allowed");
 
290
        g_array_append_val (filtered_combinations, all_item);
 
291
    }
 
292
 
 
293
    return filtered_combinations;
 
294
}
 
295
 
 
296
/*****************************************************************************/
 
297
 
 
298
GArray *
 
299
mm_filter_supported_capabilities (MMModemCapability all,
 
300
                                  const GArray *supported_combinations)
 
301
{
 
302
    guint i;
 
303
    GArray *filtered_combinations;
 
304
 
 
305
    g_return_val_if_fail (all != MM_MODEM_CAPABILITY_NONE, NULL);
 
306
    g_return_val_if_fail (supported_combinations != NULL, NULL);
 
307
 
 
308
    /* We will filter out all combinations which have modes not listed in 'all' */
 
309
    filtered_combinations = g_array_sized_new (FALSE, FALSE, sizeof (MMModemCapability), supported_combinations->len);
 
310
    for (i = 0; i < supported_combinations->len; i++) {
 
311
        MMModemCapability capability;
 
312
 
 
313
        capability = g_array_index (supported_combinations, MMModemCapability, i);
 
314
        if (!(capability & ~all))
 
315
            g_array_append_val (filtered_combinations, capability);
 
316
    }
 
317
 
 
318
    if (filtered_combinations->len == 0)
 
319
        mm_warn ("All supported capability combinations were filtered out.");
 
320
 
 
321
    return filtered_combinations;
 
322
}
 
323
 
 
324
/*****************************************************************************/
 
325
 
 
326
/* +CREG: <stat>                      (GSM 07.07 CREG=1 unsolicited) */
 
327
#define CREG1 "\\+(CREG|CGREG|CEREG):\\s*0*([0-9])"
 
328
 
 
329
/* +CREG: <n>,<stat>                  (GSM 07.07 CREG=1 solicited) */
 
330
#define CREG2 "\\+(CREG|CGREG|CEREG):\\s*0*([0-9]),\\s*0*([0-9])"
 
331
 
 
332
/* +CREG: <stat>,<lac>,<ci>           (GSM 07.07 CREG=2 unsolicited) */
 
333
#define CREG3 "\\+(CREG|CGREG|CEREG):\\s*0*([0-9]),\\s*([^,\\s]*)\\s*,\\s*([^,\\s]*)"
 
334
 
 
335
/* +CREG: <n>,<stat>,<lac>,<ci>       (GSM 07.07 solicited and some CREG=2 unsolicited) */
 
336
#define CREG4 "\\+(CREG|CGREG|CEREG):\\s*0*([0-9]),\\s*0*([0-9])\\s*,\\s*([^,]*)\\s*,\\s*([^,\\s]*)"
 
337
 
 
338
/* +CREG: <stat>,<lac>,<ci>,<AcT>     (ETSI 27.007 CREG=2 unsolicited) */
 
339
#define CREG5 "\\+(CREG|CGREG|CEREG):\\s*0*([0-9])\\s*,\\s*([^,\\s]*)\\s*,\\s*([^,\\s]*)\\s*,\\s*0*([0-9])"
 
340
 
 
341
/* +CREG: <n>,<stat>,<lac>,<ci>,<AcT> (ETSI 27.007 solicited and some CREG=2 unsolicited) */
 
342
#define CREG6 "\\+(CREG|CGREG|CEREG):\\s*0*([0-9]),\\s*0*([0-9])\\s*,\\s*([^,\\s]*)\\s*,\\s*([^,\\s]*)\\s*,\\s*0*([0-9])"
 
343
 
 
344
/* +CREG: <n>,<stat>,<lac>,<ci>,<AcT?>,<something> (Samsung Wave S8500) */
 
345
/* '<CR><LF>+CREG: 2,1,000B,2816, B, C2816<CR><LF><CR><LF>OK<CR><LF>' */
 
346
#define CREG7 "\\+(CREG|CGREG):\\s*0*([0-9]),\\s*0*([0-9])\\s*,\\s*([^,\\s]*)\\s*,\\s*([^,\\s]*)\\s*,\\s*([^,\\s]*)\\s*,\\s*[^,\\s]*"
 
347
 
 
348
/* +CREG: <stat>,<lac>,<ci>,<AcT>,<RAC> (ETSI 27.007 v9.20 CREG=2 unsolicited with RAC) */
 
349
#define CREG8 "\\+(CREG|CGREG):\\s*0*([0-9])\\s*,\\s*([^,\\s]*)\\s*,\\s*([^,\\s]*)\\s*,\\s*0*([0-9])\\s*,\\s*([^,\\s]*)"
 
350
 
 
351
/* +CEREG: <stat>,<lac>,<rac>,<ci>,<AcT>     (ETSI 27.007 v8.6 CREG=2 unsolicited with RAC) */
 
352
#define CEREG1 "\\+(CEREG):\\s*0*([0-9])\\s*,\\s*([^,\\s]*)\\s*,\\s*([^,\\s]*)\\s*,\\s*([^,\\s]*)\\s*,\\s*0*([0-9])"
 
353
 
 
354
/* +CEREG: <n>,<stat>,<lac>,<rac>,<ci>,<AcT> (ETSI 27.007 v8.6 CREG=2 solicited with RAC) */
 
355
#define CEREG2 "\\+(CEREG):\\s*0*([0-9]),\\s*0*([0-9])\\s*,\\s*([^,\\s]*)\\s*,\\s*([^,\\s]*)\\s*,\\s*([^,\\s]*)\\s*,\\s*0*([0-9])"
 
356
 
 
357
GPtrArray *
 
358
mm_3gpp_creg_regex_get (gboolean solicited)
 
359
{
 
360
    GPtrArray *array = g_ptr_array_sized_new (10);
 
361
    GRegex *regex;
 
362
 
 
363
    /* #1 */
 
364
    if (solicited)
 
365
        regex = g_regex_new (CREG1 "$", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
 
366
    else
 
367
        regex = g_regex_new ("\\r\\n" CREG1 "\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
 
368
    g_assert (regex);
 
369
    g_ptr_array_add (array, regex);
 
370
 
 
371
    /* #2 */
 
372
    if (solicited)
 
373
        regex = g_regex_new (CREG2 "$", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
 
374
    else
 
375
        regex = g_regex_new ("\\r\\n" CREG2 "\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
 
376
    g_assert (regex);
 
377
    g_ptr_array_add (array, regex);
 
378
 
 
379
    /* #3 */
 
380
    if (solicited)
 
381
        regex = g_regex_new (CREG3 "$", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
 
382
    else
 
383
        regex = g_regex_new ("\\r\\n" CREG3 "\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
 
384
    g_assert (regex);
 
385
    g_ptr_array_add (array, regex);
 
386
 
 
387
    /* #4 */
 
388
    if (solicited)
 
389
        regex = g_regex_new (CREG4 "$", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
 
390
    else
 
391
        regex = g_regex_new ("\\r\\n" CREG4 "\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
 
392
    g_assert (regex);
 
393
    g_ptr_array_add (array, regex);
 
394
 
 
395
    /* #5 */
 
396
    if (solicited)
 
397
        regex = g_regex_new (CREG5 "$", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
 
398
    else
 
399
        regex = g_regex_new ("\\r\\n" CREG5 "\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
 
400
    g_assert (regex);
 
401
    g_ptr_array_add (array, regex);
 
402
 
 
403
    /* #6 */
 
404
    if (solicited)
 
405
        regex = g_regex_new (CREG6 "$", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
 
406
    else
 
407
        regex = g_regex_new ("\\r\\n" CREG6 "\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
 
408
    g_assert (regex);
 
409
    g_ptr_array_add (array, regex);
 
410
 
 
411
    /* #7 */
 
412
    if (solicited)
 
413
        regex = g_regex_new (CREG7 "$", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
 
414
    else
 
415
        regex = g_regex_new ("\\r\\n" CREG7 "\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
 
416
    g_assert (regex);
 
417
    g_ptr_array_add (array, regex);
 
418
 
 
419
    /* #8 */
 
420
    if (solicited)
 
421
        regex = g_regex_new (CREG8 "$", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
 
422
    else
 
423
        regex = g_regex_new ("\\r\\n" CREG8 "\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
 
424
    g_assert (regex);
 
425
    g_ptr_array_add (array, regex);
 
426
 
 
427
    /* CEREG #1 */
 
428
    if (solicited)
 
429
        regex = g_regex_new (CEREG1 "$", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
 
430
    else
 
431
        regex = g_regex_new ("\\r\\n" CEREG1 "\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
 
432
    g_assert (regex);
 
433
    g_ptr_array_add (array, regex);
 
434
 
 
435
    /* CEREG #2 */
 
436
    if (solicited)
 
437
        regex = g_regex_new (CEREG2 "$", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
 
438
    else
 
439
        regex = g_regex_new ("\\r\\n" CEREG2 "\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
 
440
    g_assert (regex);
 
441
    g_ptr_array_add (array, regex);
 
442
 
 
443
    return array;
 
444
}
 
445
 
 
446
void
 
447
mm_3gpp_creg_regex_destroy (GPtrArray *array)
 
448
{
 
449
    g_ptr_array_foreach (array, (GFunc) g_regex_unref, NULL);
 
450
    g_ptr_array_free (array, TRUE);
 
451
}
 
452
 
 
453
/*************************************************************************/
 
454
 
 
455
GRegex *
 
456
mm_3gpp_ciev_regex_get (void)
 
457
{
 
458
    return g_regex_new ("\\r\\n\\+CIEV: (.*),(\\d)\\r\\n",
 
459
                        G_REGEX_RAW | G_REGEX_OPTIMIZE,
 
460
                        0,
 
461
                        NULL);
 
462
}
 
463
 
 
464
/*************************************************************************/
 
465
 
 
466
GRegex *
 
467
mm_3gpp_cusd_regex_get (void)
 
468
{
 
469
    return g_regex_new ("\\r\\n\\+CUSD:\\s*(.*)\\r\\n",
 
470
                        G_REGEX_RAW | G_REGEX_OPTIMIZE,
 
471
                        0,
 
472
                        NULL);
 
473
}
 
474
 
 
475
/*************************************************************************/
 
476
 
 
477
GRegex *
 
478
mm_3gpp_cmti_regex_get (void)
 
479
{
 
480
    return g_regex_new ("\\r\\n\\+CMTI: \"(\\S+)\",(\\d+)\\r\\n",
 
481
                        G_REGEX_RAW | G_REGEX_OPTIMIZE,
 
482
                        0,
 
483
                        NULL);
 
484
}
 
485
 
 
486
GRegex *
 
487
mm_3gpp_cds_regex_get (void)
 
488
{
 
489
    /* Example:
 
490
     * <CR><LF>+CDS: 24<CR><LF>07914356060013F10659098136395339F6219011707193802190117071938030<CR><LF>
 
491
     */
 
492
    return g_regex_new ("\\r\\n\\+CDS:\\s*(\\d+)\\r\\n(.*)\\r\\n",
 
493
                        G_REGEX_RAW | G_REGEX_OPTIMIZE,
 
494
                        0,
 
495
                        NULL);
 
496
}
 
497
 
44
498
/*************************************************************************/
45
499
 
46
500
static void
47
 
save_scan_value (GHashTable *hash, const char *key, GMatchInfo *info, guint32 num)
48
 
{
49
 
    char *quoted;
50
 
    size_t len;
51
 
 
52
 
    g_return_if_fail (info != NULL);
53
 
 
54
 
    quoted = g_match_info_fetch (info, num);
55
 
    if (!quoted)
56
 
        return;
57
 
 
58
 
    len = strlen (quoted);
59
 
 
60
 
    /* Unquote the item if needed */
61
 
    if ((len >= 2) && (quoted[0] == '"') && (quoted[len - 1] == '"')) {
62
 
        quoted[0] = ' ';
63
 
        quoted[len - 1] = ' ';
64
 
        quoted = g_strstrip (quoted);
65
 
    }
66
 
 
67
 
    if (!strlen (quoted)) {
68
 
        g_free (quoted);
69
 
        return;
70
 
    }
71
 
 
72
 
    g_hash_table_insert (hash, g_strdup (key), quoted);
73
 
}
74
 
 
75
 
/* If the response was successfully parsed (even if no valid entries were
76
 
 * found) the pointer array will be returned.
77
 
 */
78
 
GPtrArray *
79
 
mm_gsm_parse_scan_response (const char *reply, GError **error)
80
 
{
81
 
    /* Got valid reply */
82
 
    GPtrArray *results = NULL;
 
501
mm_3gpp_network_info_free (MM3gppNetworkInfo *info)
 
502
{
 
503
    g_free (info->operator_long);
 
504
    g_free (info->operator_short);
 
505
    g_free (info->operator_code);
 
506
    g_free (info);
 
507
}
 
508
 
 
509
void
 
510
mm_3gpp_network_info_list_free (GList *info_list)
 
511
{
 
512
    g_list_free_full (info_list, (GDestroyNotify) mm_3gpp_network_info_free);
 
513
}
 
514
 
 
515
static MMModemAccessTechnology
 
516
get_mm_access_tech_from_etsi_access_tech (guint act)
 
517
{
 
518
    /* See ETSI TS 27.007 */
 
519
    switch (act) {
 
520
    case 0:
 
521
        return MM_MODEM_ACCESS_TECHNOLOGY_GSM;
 
522
    case 1:
 
523
        return MM_MODEM_ACCESS_TECHNOLOGY_GSM_COMPACT;
 
524
    case 2:
 
525
        return MM_MODEM_ACCESS_TECHNOLOGY_UMTS;
 
526
    case 3:
 
527
        return MM_MODEM_ACCESS_TECHNOLOGY_EDGE;
 
528
    case 4:
 
529
        return MM_MODEM_ACCESS_TECHNOLOGY_HSDPA;
 
530
    case 5:
 
531
        return MM_MODEM_ACCESS_TECHNOLOGY_HSUPA;
 
532
    case 6:
 
533
        return MM_MODEM_ACCESS_TECHNOLOGY_HSPA;
 
534
    case 7:
 
535
        return MM_MODEM_ACCESS_TECHNOLOGY_LTE;
 
536
    default:
 
537
        return MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN;
 
538
    }
 
539
}
 
540
 
 
541
static MMModem3gppNetworkAvailability
 
542
parse_network_status (const gchar *str)
 
543
{
 
544
    /* Expecting a value between '0' and '3' inclusive */
 
545
    if (!str ||
 
546
        strlen (str) != 1 ||
 
547
        str[0] < '0' ||
 
548
        str[0] > '3') {
 
549
        mm_warn ("Cannot parse network status: '%s'", str);
 
550
        return MM_MODEM_3GPP_NETWORK_AVAILABILITY_UNKNOWN;
 
551
    }
 
552
 
 
553
    return (MMModem3gppNetworkAvailability) (str[0] - '0');
 
554
}
 
555
 
 
556
static MMModemAccessTechnology
 
557
parse_access_tech (const gchar *str)
 
558
{
 
559
    /* Recognized access technologies are between '0' and '7' inclusive... */
 
560
    if (!str ||
 
561
        strlen (str) != 1 ||
 
562
        str[0] < '0' ||
 
563
        str[0] > '7') {
 
564
        mm_warn ("Cannot parse access tech: '%s'", str);
 
565
        return MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN;
 
566
    }
 
567
 
 
568
    return get_mm_access_tech_from_etsi_access_tech (str[0] - '0');
 
569
}
 
570
 
 
571
GList *
 
572
mm_3gpp_parse_cops_test_response (const gchar *reply,
 
573
                                  GError **error)
 
574
{
83
575
    GRegex *r;
 
576
    GList *info_list = NULL;
84
577
    GMatchInfo *match_info;
85
 
    GError *err = NULL;
86
578
    gboolean umts_format = TRUE;
 
579
    GError *inner_error = NULL;
87
580
 
88
581
    g_return_val_if_fail (reply != NULL, NULL);
89
582
    if (error)
91
584
 
92
585
    if (!strstr (reply, "+COPS: ")) {
93
586
        g_set_error_literal (error,
94
 
                             MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL,
 
587
                             MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
95
588
                             "Could not parse scan results.");
96
589
        return NULL;
97
590
    }
112
605
     *       +COPS: (2,"","T-Mobile","31026",0),(1,"AT&T","AT&T","310410"),0)
113
606
     */
114
607
 
115
 
    r = g_regex_new ("\\((\\d),([^,\\)]*),([^,\\)]*),([^,\\)]*)[\\)]?,(\\d)\\)", G_REGEX_UNGREEDY, 0, &err);
116
 
    if (err) {
117
 
        mm_err ("Invalid regular expression: %s", err->message);
118
 
        g_error_free (err);
 
608
    r = g_regex_new ("\\((\\d),\"([^\"\\)]*)\",([^,\\)]*),([^,\\)]*)[\\)]?,(\\d)\\)", G_REGEX_UNGREEDY, 0, &inner_error);
 
609
    if (inner_error) {
 
610
        mm_err ("Invalid regular expression: %s", inner_error->message);
 
611
        g_error_free (inner_error);
119
612
        g_set_error_literal (error,
120
 
                             MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL,
121
 
                             "Could not parse scan results.");
 
613
                             MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
 
614
                             "Could not parse scan results");
122
615
        return NULL;
123
616
    }
124
617
 
141
634
         *       +COPS: (2,"T - Mobile",,"31026"),(1,"Einstein PCS",,"31064"),(1,"Cingular",,"31041"),,(0,1,3),(0,2)
142
635
         */
143
636
 
144
 
        r = g_regex_new ("\\((\\d),([^,\\)]*),([^,\\)]*),([^\\)]*)\\)", G_REGEX_UNGREEDY, 0, &err);
145
 
        if (err) {
146
 
            mm_err ("Invalid regular expression: %s", err->message);
147
 
            g_error_free (err);
 
637
        r = g_regex_new ("\\((\\d),([^,\\)]*),([^,\\)]*),([^\\)]*)\\)", G_REGEX_UNGREEDY, 0, &inner_error);
 
638
        if (inner_error) {
 
639
            mm_err ("Invalid regular expression: %s", inner_error->message);
 
640
            g_error_free (inner_error);
148
641
            g_set_error_literal (error,
149
 
                                 MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL,
150
 
                                 "Could not parse scan results.");
 
642
                                 MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
 
643
                                 "Could not parse scan results");
151
644
            return NULL;
152
645
        }
153
646
 
156
649
    }
157
650
 
158
651
    /* Parse the results */
159
 
    results = g_ptr_array_new ();
160
652
    while (g_match_info_matches (match_info)) {
161
 
        GHashTable *hash;
162
 
        char *access_tech = NULL;
163
 
        const char *tmp;
 
653
        MM3gppNetworkInfo *info;
 
654
        gchar *tmp;
164
655
        gboolean valid = FALSE;
165
656
 
166
 
        hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
167
 
 
168
 
        save_scan_value (hash, MM_SCAN_TAG_STATUS, match_info, 1);
169
 
        save_scan_value (hash, MM_SCAN_TAG_OPER_LONG, match_info, 2);
170
 
        save_scan_value (hash, MM_SCAN_TAG_OPER_SHORT, match_info, 3);
171
 
        save_scan_value (hash, MM_SCAN_TAG_OPER_NUM, match_info, 4);
172
 
 
173
 
        /* Only try for access technology with UMTS-format matches */
174
 
        if (umts_format)
175
 
            access_tech = g_match_info_fetch (match_info, 5);
176
 
        if (access_tech && (strlen (access_tech) == 1)) {
177
 
            /* Recognized access technologies are between '0' and '6' inclusive... */
178
 
            if ((access_tech[0] >= '0') && (access_tech[0] <= '6'))
179
 
                g_hash_table_insert (hash, g_strdup (MM_SCAN_TAG_ACCESS_TECH), access_tech);
180
 
        } else
181
 
            g_free (access_tech);
 
657
        info = g_new0 (MM3gppNetworkInfo, 1);
 
658
 
 
659
        tmp = mm_get_string_unquoted_from_match_info (match_info, 1);
 
660
        info->status = parse_network_status (tmp);
 
661
        g_free (tmp);
 
662
 
 
663
        info->operator_long = mm_get_string_unquoted_from_match_info (match_info, 2);
 
664
        info->operator_short = mm_get_string_unquoted_from_match_info (match_info, 3);
 
665
        info->operator_code = mm_get_string_unquoted_from_match_info (match_info, 4);
 
666
 
 
667
        /* Only try for access technology with UMTS-format matches.
 
668
         * If none give, assume GSM */
 
669
        tmp = (umts_format ?
 
670
               mm_get_string_unquoted_from_match_info (match_info, 5) :
 
671
               NULL);
 
672
        info->access_tech = (tmp ?
 
673
                             parse_access_tech (tmp) :
 
674
                             MM_MODEM_ACCESS_TECHNOLOGY_GSM);
 
675
        g_free (tmp);
182
676
 
183
677
        /* If the operator number isn't valid (ie, at least 5 digits),
184
678
         * ignore the scan result; it's probably the parameter stuff at the
185
679
         * end of the +COPS response.  The regex will sometimes catch this
186
680
         * but there's no good way to ignore it.
187
681
         */
188
 
        tmp = g_hash_table_lookup (hash, MM_SCAN_TAG_OPER_NUM);
189
 
        if (tmp && (strlen (tmp) >= 5)) {
 
682
        if (info->operator_code && (strlen (info->operator_code) >= 5)) {
190
683
            valid = TRUE;
 
684
            tmp = info->operator_code;
191
685
            while (*tmp) {
192
686
                if (!isdigit (*tmp) && (*tmp != '-')) {
193
687
                    valid = FALSE;
195
689
                }
196
690
                tmp++;
197
691
            }
198
 
 
199
 
            if (valid)
200
 
                g_ptr_array_add (results, hash);
201
 
        }
202
 
 
203
 
        if (!valid)
204
 
            g_hash_table_destroy (hash);
 
692
        }
 
693
 
 
694
        if (valid) {
 
695
            gchar *access_tech_str;
 
696
 
 
697
            access_tech_str = mm_modem_access_technology_build_string_from_mask (info->access_tech);
 
698
            mm_dbg ("Found network '%s' ('%s','%s'); availability: %s, access tech: %s",
 
699
                    info->operator_code,
 
700
                    info->operator_short ? info->operator_short : "no short name",
 
701
                    info->operator_long ? info->operator_long : "no long name",
 
702
                    mm_modem_3gpp_network_availability_get_string (info->status),
 
703
                    access_tech_str);
 
704
            g_free (access_tech_str);
 
705
 
 
706
            info_list = g_list_prepend (info_list, info);
 
707
        }
 
708
        else
 
709
            mm_3gpp_network_info_free (info);
205
710
 
206
711
        g_match_info_next (match_info, NULL);
207
712
    }
209
714
    g_match_info_free (match_info);
210
715
    g_regex_unref (r);
211
716
 
212
 
    return results;
213
 
}
214
 
 
215
 
void
216
 
mm_gsm_destroy_scan_data (gpointer data)
217
 
{
218
 
    GPtrArray *results = (GPtrArray *) data;
219
 
 
220
 
    g_ptr_array_foreach (results, (GFunc) g_hash_table_destroy, NULL);
221
 
    g_ptr_array_free (results, TRUE);
222
 
}
223
 
 
224
 
/*************************************************************************/
225
 
 
226
 
/* +CREG: <stat>                       (GSM 07.07 CREG=1 unsolicited) */
227
 
#define CREG1 "\\+(CREG|CGREG):\\s*(\\d{1})"
228
 
 
229
 
/* +CREG: <n>,<stat>                   (GSM 07.07 CREG=1 solicited) */
230
 
#define CREG2 "\\+(CREG|CGREG):\\s*(\\d{1}),\\s*(\\d{1})"
231
 
 
232
 
/* +CREG: <stat>,<lac>,<ci>           (GSM 07.07 CREG=2 unsolicited) */
233
 
#define CREG3 "\\+(CREG|CGREG):\\s*(\\d{1}),\\s*([^,\\s]*)\\s*,\\s*([^,\\s]*)"
234
 
 
235
 
/* +CREG: <n>,<stat>,<lac>,<ci>       (GSM 07.07 solicited and some CREG=2 unsolicited) */
236
 
#define CREG4 "\\+(CREG|CGREG):\\s*(\\d{1}),\\s*(\\d{1})\\s*,\\s*([^,]*)\\s*,\\s*([^,\\s]*)"
237
 
 
238
 
/* +CREG: <stat>,<lac>,<ci>,<AcT>     (ETSI 27.007 CREG=2 unsolicited) */
239
 
#define CREG5 "\\+(CREG|CGREG):\\s*(\\d{1})\\s*,\\s*([^,\\s]*)\\s*,\\s*([^,\\s]*)\\s*,\\s*(\\d{1,2})"
240
 
 
241
 
/* +CREG: <n>,<stat>,<lac>,<ci>,<AcT> (ETSI 27.007 solicited and some CREG=2 unsolicited) */
242
 
#define CREG6 "\\+(CREG|CGREG):\\s*(\\d{1}),\\s*(\\d{1})\\s*,\\s*([^,\\s]*)\\s*,\\s*([^,\\s]*)\\s*,\\s*(\\d{1,2})"
243
 
 
244
 
/* +CREG: <n>,<stat>,<lac>,<ci>,<AcT?>,<something> (Samsung Wave S8500) */
245
 
/* '<CR><LF>+CREG: 2,1,000B,2816, B, C2816<CR><LF><CR><LF>OK<CR><LF>' */
246
 
#define CREG7 "\\+(CREG|CGREG):\\s*(\\d{1}),\\s*(\\d{1})\\s*,\\s*([^,\\s]*)\\s*,\\s*([^,\\s]*)\\s*,\\s*([^,\\s]*)\\s*,\\s*[^,\\s]*"
247
 
 
248
 
/* +CREG: <stat>,<lac>,<ci>,<AcT>,<RAC> (ETSI 27.007 v9.20 CREG=2 unsolicited with RAC) */
249
 
#define CREG8 "\\+(CREG|CGREG):\\s*(\\d{1})\\s*,\\s*([^,\\s]*)\\s*,\\s*([^,\\s]*)\\s*,\\s*(\\d{1,2})\\s*,\\s*([^,\\s]*)"
250
 
 
251
 
GPtrArray *
252
 
mm_gsm_creg_regex_get (gboolean solicited)
253
 
{
254
 
    GPtrArray *array = g_ptr_array_sized_new (7);
255
 
    GRegex *regex;
256
 
 
257
 
    /* #1 */
258
 
    if (solicited)
259
 
        regex = g_regex_new (CREG1 "$", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
260
 
    else
261
 
        regex = g_regex_new ("\\r\\n" CREG1 "\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
262
 
    g_assert (regex);
263
 
    g_ptr_array_add (array, regex);
264
 
 
265
 
    /* #2 */
266
 
    if (solicited)
267
 
        regex = g_regex_new (CREG2 "$", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
268
 
    else
269
 
        regex = g_regex_new ("\\r\\n" CREG2 "\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
270
 
    g_assert (regex);
271
 
    g_ptr_array_add (array, regex);
272
 
 
273
 
    /* #3 */
274
 
    if (solicited)
275
 
        regex = g_regex_new (CREG3 "$", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
276
 
    else
277
 
        regex = g_regex_new ("\\r\\n" CREG3 "\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
278
 
    g_assert (regex);
279
 
    g_ptr_array_add (array, regex);
280
 
 
281
 
    /* #4 */
282
 
    if (solicited)
283
 
        regex = g_regex_new (CREG4 "$", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
284
 
    else
285
 
        regex = g_regex_new ("\\r\\n" CREG4 "\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
286
 
    g_assert (regex);
287
 
    g_ptr_array_add (array, regex);
288
 
 
289
 
    /* #5 */
290
 
    if (solicited)
291
 
        regex = g_regex_new (CREG5 "$", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
292
 
    else
293
 
        regex = g_regex_new ("\\r\\n" CREG5 "\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
294
 
    g_assert (regex);
295
 
    g_ptr_array_add (array, regex);
296
 
 
297
 
    /* #6 */
298
 
    if (solicited)
299
 
        regex = g_regex_new (CREG6 "$", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
300
 
    else
301
 
        regex = g_regex_new ("\\r\\n" CREG6 "\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
302
 
    g_assert (regex);
303
 
    g_ptr_array_add (array, regex);
304
 
 
305
 
    /* #7 */
306
 
    if (solicited)
307
 
        regex = g_regex_new (CREG7 "$", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
308
 
    else
309
 
        regex = g_regex_new ("\\r\\n" CREG7 "\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
310
 
    g_assert (regex);
311
 
    g_ptr_array_add (array, regex);
312
 
 
313
 
    /* #8 */
314
 
    if (solicited)
315
 
        regex = g_regex_new (CREG8 "$", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
316
 
    else
317
 
        regex = g_regex_new ("\\r\\n" CREG8 "\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
318
 
    g_assert (regex);
319
 
    g_ptr_array_add (array, regex);
320
 
 
321
 
    return array;
322
 
}
323
 
 
324
 
void
325
 
mm_gsm_creg_regex_destroy (GPtrArray *array)
326
 
{
327
 
    g_ptr_array_foreach (array, (GFunc) g_regex_unref, NULL);
328
 
    g_ptr_array_free (array, TRUE);
 
717
    return info_list;
 
718
}
 
719
 
 
720
/*************************************************************************/
 
721
 
 
722
 
 
723
static void
 
724
mm_3gpp_pdp_context_format_free (MM3gppPdpContextFormat *format)
 
725
{
 
726
    g_slice_free (MM3gppPdpContextFormat, format);
 
727
}
 
728
 
 
729
void
 
730
mm_3gpp_pdp_context_format_list_free (GList *pdp_format_list)
 
731
{
 
732
    g_list_free_full (pdp_format_list, (GDestroyNotify) mm_3gpp_pdp_context_format_free);
 
733
}
 
734
 
 
735
GList *
 
736
mm_3gpp_parse_cgdcont_test_response (const gchar *response,
 
737
                                     GError **error)
 
738
{
 
739
    GRegex *r;
 
740
    GMatchInfo *match_info;
 
741
    GError *inner_error = NULL;
 
742
    GList *list = NULL;
 
743
 
 
744
    if (!response || !g_str_has_prefix (response, "+CGDCONT:")) {
 
745
        g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Missing +CGDCONT prefix");
 
746
        return NULL;
 
747
    }
 
748
 
 
749
    r = g_regex_new ("\\+CGDCONT:\\s*\\((\\d+)-(\\d+)\\),\\(?\"(\\S+)\"",
 
750
                     G_REGEX_DOLLAR_ENDONLY | G_REGEX_RAW,
 
751
                     0, &inner_error);
 
752
    g_assert (r != NULL);
 
753
 
 
754
    g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, &inner_error);
 
755
    while (!inner_error && g_match_info_matches (match_info)) {
 
756
        gchar *pdp_type_str;
 
757
        guint min_cid;
 
758
        guint max_cid;
 
759
        MMBearerIpFamily pdp_type;
 
760
 
 
761
        /* Read PDP type */
 
762
        pdp_type_str = mm_get_string_unquoted_from_match_info (match_info, 3);
 
763
        pdp_type = mm_3gpp_get_ip_family_from_pdp_type (pdp_type_str);
 
764
        if (pdp_type == MM_BEARER_IP_FAMILY_NONE)
 
765
            mm_dbg ("Unhandled PDP type in CGDCONT=? reply: '%s'", pdp_type_str);
 
766
        else {
 
767
            /* Read min CID */
 
768
            if (!mm_get_uint_from_match_info (match_info, 1, &min_cid))
 
769
                mm_warn ("Invalid min CID in CGDCONT=? reply for PDP type '%s'", pdp_type_str);
 
770
            else {
 
771
                /* Read max CID */
 
772
                if (!mm_get_uint_from_match_info (match_info, 2, &max_cid))
 
773
                    mm_warn ("Invalid max CID in CGDCONT=? reply for PDP type '%s'", pdp_type_str);
 
774
                else {
 
775
                    MM3gppPdpContextFormat *format;
 
776
 
 
777
                    format = g_slice_new (MM3gppPdpContextFormat);
 
778
                    format->pdp_type = pdp_type;
 
779
                    format->min_cid = min_cid;
 
780
                    format->max_cid = max_cid;
 
781
 
 
782
                    list = g_list_prepend (list, format);
 
783
                }
 
784
            }
 
785
        }
 
786
 
 
787
        g_free (pdp_type_str);
 
788
        g_match_info_next (match_info, &inner_error);
 
789
    }
 
790
 
 
791
    g_match_info_free (match_info);
 
792
    g_regex_unref (r);
 
793
 
 
794
    if (inner_error) {
 
795
        mm_warn ("Unexpected error matching +CGDCONT response: '%s'", inner_error->message);
 
796
        g_error_free (inner_error);
 
797
    }
 
798
 
 
799
    return list;
 
800
}
 
801
 
 
802
/*************************************************************************/
 
803
 
 
804
static void
 
805
mm_3gpp_pdp_context_free (MM3gppPdpContext *pdp)
 
806
{
 
807
    g_free (pdp->apn);
 
808
    g_slice_free (MM3gppPdpContext, pdp);
 
809
}
 
810
 
 
811
void
 
812
mm_3gpp_pdp_context_list_free (GList *list)
 
813
{
 
814
    g_list_free_full (list, (GDestroyNotify) mm_3gpp_pdp_context_free);
 
815
}
 
816
 
 
817
static gint
 
818
mm_3gpp_pdp_context_cmp (MM3gppPdpContext *a,
 
819
                         MM3gppPdpContext *b)
 
820
{
 
821
    return (a->cid - b->cid);
 
822
}
 
823
 
 
824
GList *
 
825
mm_3gpp_parse_cgdcont_read_response (const gchar *reply,
 
826
                                     GError **error)
 
827
{
 
828
    GError *inner_error = NULL;
 
829
    GRegex *r;
 
830
    GMatchInfo *match_info;
 
831
    GList *list;
 
832
 
 
833
    if (!reply[0])
 
834
        /* No APNs configured, all done */
 
835
        return NULL;
 
836
 
 
837
    list = NULL;
 
838
    r = g_regex_new ("\\+CGDCONT:\\s*(\\d+)\\s*,([^,\\)]*),([^,\\)]*),([^,\\)]*)",
 
839
                     G_REGEX_DOLLAR_ENDONLY | G_REGEX_RAW,
 
840
                     0, &inner_error);
 
841
    if (r) {
 
842
        g_regex_match_full (r, reply, strlen (reply), 0, 0, &match_info, &inner_error);
 
843
 
 
844
        while (!inner_error &&
 
845
               g_match_info_matches (match_info)) {
 
846
            gchar *str;
 
847
            MMBearerIpFamily ip_family;
 
848
 
 
849
            str = mm_get_string_unquoted_from_match_info (match_info, 2);
 
850
            ip_family = mm_3gpp_get_ip_family_from_pdp_type (str);
 
851
            if (ip_family == MM_BEARER_IP_FAMILY_NONE)
 
852
                mm_dbg ("Ignoring PDP context type: '%s'", str);
 
853
            else {
 
854
                MM3gppPdpContext *pdp;
 
855
 
 
856
                pdp = g_slice_new0 (MM3gppPdpContext);
 
857
                if (!mm_get_uint_from_match_info (match_info, 1, &pdp->cid)) {
 
858
                    inner_error = g_error_new (MM_CORE_ERROR,
 
859
                                               MM_CORE_ERROR_FAILED,
 
860
                                               "Couldn't parse CID from reply: '%s'",
 
861
                                               reply);
 
862
                    break;
 
863
                }
 
864
                pdp->pdp_type = ip_family;
 
865
                pdp->apn = mm_get_string_unquoted_from_match_info (match_info, 3);
 
866
 
 
867
                list = g_list_prepend (list, pdp);
 
868
            }
 
869
 
 
870
            g_free (str);
 
871
            g_match_info_next (match_info, &inner_error);
 
872
        }
 
873
 
 
874
        g_match_info_free (match_info);
 
875
        g_regex_unref (r);
 
876
    }
 
877
 
 
878
    if (inner_error) {
 
879
        mm_3gpp_pdp_context_list_free (list);
 
880
        g_propagate_error (error, inner_error);
 
881
        g_prefix_error (error, "Couldn't properly parse list of PDP contexts. ");
 
882
        return NULL;
 
883
    }
 
884
 
 
885
    list = g_list_sort (list, (GCompareFunc)mm_3gpp_pdp_context_cmp);
 
886
 
 
887
    return list;
329
888
}
330
889
 
331
890
/*************************************************************************/
334
893
parse_uint (char *str, int base, glong nmin, glong nmax, gboolean *valid)
335
894
{
336
895
    gulong ret = 0;
337
 
    char *endquote;
 
896
    gchar *endquote;
338
897
 
339
898
    *valid = FALSE;
340
899
    if (!str)
358
917
static gboolean
359
918
item_is_lac_not_stat (GMatchInfo *info, guint32 item)
360
919
{
361
 
    char *str;
 
920
    gchar *str;
362
921
    gboolean is_lac = FALSE;
363
922
 
364
923
    /* A <stat> will always be a single digit, without quotes */
370
929
}
371
930
 
372
931
gboolean
373
 
mm_gsm_parse_creg_response (GMatchInfo *info,
374
 
                            guint32 *out_reg_state,
375
 
                            gulong *out_lac,
376
 
                            gulong *out_ci,
377
 
                            gint *out_act,
378
 
                            gboolean *out_cgreg,
379
 
                            GError **error)
 
932
mm_3gpp_parse_creg_response (GMatchInfo *info,
 
933
                             MMModem3gppRegistrationState *out_reg_state,
 
934
                             gulong *out_lac,
 
935
                             gulong *out_ci,
 
936
                             MMModemAccessTechnology *out_act,
 
937
                             gboolean *out_cgreg,
 
938
                             gboolean *out_cereg,
 
939
                             GError **error)
380
940
{
381
941
    gboolean success = FALSE, foo;
382
942
    gint n_matches, act = -1;
383
943
    gulong stat = 0, lac = 0, ci = 0;
384
944
    guint istat = 0, ilac = 0, ici = 0, iact = 0;
385
 
    char *str;
 
945
    gchar *str;
386
946
 
387
947
    g_return_val_if_fail (info != NULL, FALSE);
388
948
    g_return_val_if_fail (out_reg_state != NULL, FALSE);
390
950
    g_return_val_if_fail (out_ci != NULL, FALSE);
391
951
    g_return_val_if_fail (out_act != NULL, FALSE);
392
952
    g_return_val_if_fail (out_cgreg != NULL, FALSE);
 
953
    g_return_val_if_fail (out_cereg != NULL, FALSE);
393
954
 
394
955
    str = g_match_info_fetch (info, 1);
395
 
    if (str && strstr (str, "CGREG"))
396
 
        *out_cgreg = TRUE;
 
956
    *out_cgreg = (str && strstr (str, "CGREG")) ? TRUE : FALSE;
 
957
    *out_cereg = (str && strstr (str, "CEREG")) ? TRUE : FALSE;
397
958
    g_free (str);
398
959
 
399
960
    /* Normally the number of matches could be used to determine what each
430
991
    } else if (n_matches == 7) {
431
992
        /* CREG=2 (solicited):            +CREG: <n>,<stat>,<lac>,<ci>,<AcT>
432
993
         * CREG=2 (unsolicited with RAC): +CREG: <stat>,<lac>,<ci>,<AcT>,<RAC>
 
994
         * CEREG=2 (solicited):           +CEREG: <n>,<stat>,<lac>,<ci>,<AcT>
 
995
         * CEREG=2 (unsolicited with RAC): +CEREG: <stat>,<lac>,<rac>,<ci>,<AcT>
433
996
         */
434
997
 
435
 
        /* Check if the third item is the LAC to distinguish the two cases */
436
 
        if (item_is_lac_not_stat (info, 3)) {
437
 
            istat = 2;
438
 
            ilac = 3;
439
 
            ici = 4;
440
 
            iact = 5;
441
 
        } else {
442
 
            istat = 3;
443
 
            ilac = 4;
 
998
        if (*out_cereg) {
 
999
            /* Check if the third item is the LAC to distinguish the two cases */
 
1000
            if (item_is_lac_not_stat (info, 3)) {
 
1001
                istat = 2;
 
1002
                ilac = 3;
 
1003
            } else {
 
1004
                istat = 3;
 
1005
                ilac = 4;
 
1006
            }
444
1007
            ici = 5;
445
1008
            iact = 6;
 
1009
        } else {
 
1010
            /* Check if the third item is the LAC to distinguish the two cases */
 
1011
            if (item_is_lac_not_stat (info, 3)) {
 
1012
                istat = 2;
 
1013
                ilac = 3;
 
1014
                ici = 4;
 
1015
                iact = 5;
 
1016
            } else {
 
1017
                istat = 3;
 
1018
                ilac = 4;
 
1019
                ici = 5;
 
1020
                iact = 6;
 
1021
            }
 
1022
        }
 
1023
    } else if (n_matches == 8) {
 
1024
        /* CEREG=2 (solicited with RAC):  +CEREG: <n>,<stat>,<lac>,<rac>,<ci>,<AcT>
 
1025
         */
 
1026
        if (*out_cereg) {
 
1027
            istat = 3;
 
1028
            ilac = 4;
 
1029
            ici = 6;
 
1030
            iact = 7;
446
1031
        }
447
1032
     }
448
1033
 
452
1037
    g_free (str);
453
1038
    if (!success) {
454
1039
        g_set_error_literal (error,
455
 
                             MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL,
 
1040
                             MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
456
1041
                             "Could not parse the registration status response");
457
1042
        return FALSE;
458
1043
    }
483
1068
            act = -1;
484
1069
    }
485
1070
 
486
 
    *out_reg_state = (guint32) stat;
487
 
    if (stat != 4) {
 
1071
    /* 'roaming' is the last valid state */
 
1072
    if (stat > MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING) {
 
1073
        mm_warn ("Registration State '%lu' is unknown", stat);
 
1074
        stat = MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN;
 
1075
    }
 
1076
 
 
1077
    *out_reg_state = (MMModem3gppRegistrationState) stat;
 
1078
    if (stat != MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN) {
488
1079
        /* Don't fill in lac/ci/act if the device's state is unknown */
489
1080
        *out_lac = lac;
490
1081
        *out_ci = ci;
491
 
        *out_act = act;
492
 
    }
493
 
    return TRUE;
494
 
}
495
 
 
496
 
/*************************************************************************/
497
 
 
498
 
gboolean
499
 
mm_cdma_parse_spservice_response (const char *reply,
500
 
                                  MMModemCdmaRegistrationState *out_cdma_1x_state,
501
 
                                  MMModemCdmaRegistrationState *out_evdo_state)
502
 
{
503
 
    const char *p;
 
1082
 
 
1083
        *out_act = get_mm_access_tech_from_etsi_access_tech (act);
 
1084
    }
 
1085
    return TRUE;
 
1086
}
 
1087
 
 
1088
/*************************************************************************/
 
1089
 
 
1090
#define CMGF_TAG "+CMGF:"
 
1091
 
 
1092
gboolean
 
1093
mm_3gpp_parse_cmgf_test_response (const gchar *reply,
 
1094
                                  gboolean *sms_pdu_supported,
 
1095
                                  gboolean *sms_text_supported,
 
1096
                                  GError **error)
 
1097
{
 
1098
    GRegex *r;
 
1099
    GMatchInfo *match_info;
 
1100
    gchar *s;
 
1101
    guint32 min = -1, max = -1;
 
1102
 
 
1103
    /* Strip whitespace and response tag */
 
1104
    if (g_str_has_prefix (reply, CMGF_TAG))
 
1105
        reply += strlen (CMGF_TAG);
 
1106
    while (isspace (*reply))
 
1107
        reply++;
 
1108
 
 
1109
    r = g_regex_new ("\\(?\\s*(\\d+)\\s*[-,]?\\s*(\\d+)?\\s*\\)?", 0, 0, error);
 
1110
    if (!r)
 
1111
        return FALSE;
 
1112
 
 
1113
    if (!g_regex_match_full (r, reply, strlen (reply), 0, 0, &match_info, NULL)) {
 
1114
        g_set_error (error,
 
1115
                     MM_CORE_ERROR,
 
1116
                     MM_CORE_ERROR_FAILED,
 
1117
                     "Failed to parse CMGF query result '%s'",
 
1118
                     reply);
 
1119
        g_match_info_free (match_info);
 
1120
        g_regex_unref (r);
 
1121
        return FALSE;
 
1122
    }
 
1123
 
 
1124
    s = g_match_info_fetch (match_info, 1);
 
1125
    if (s)
 
1126
        min = atoi (s);
 
1127
    g_free (s);
 
1128
 
 
1129
    s = g_match_info_fetch (match_info, 2);
 
1130
    if (s)
 
1131
        max = atoi (s);
 
1132
    g_free (s);
 
1133
 
 
1134
    /* CMGF=0 for PDU mode */
 
1135
    *sms_pdu_supported = (min == 0);
 
1136
 
 
1137
    /* CMGF=1 for Text mode */
 
1138
    *sms_text_supported = (max >= 1);
 
1139
 
 
1140
    g_match_info_free (match_info);
 
1141
    g_regex_unref (r);
 
1142
    return TRUE;
 
1143
}
 
1144
 
 
1145
/*************************************************************************/
 
1146
 
 
1147
static MMSmsStorage
 
1148
storage_from_str (const gchar *str)
 
1149
{
 
1150
    if (g_str_equal (str, "SM"))
 
1151
        return MM_SMS_STORAGE_SM;
 
1152
    if (g_str_equal (str, "ME"))
 
1153
        return MM_SMS_STORAGE_ME;
 
1154
    if (g_str_equal (str, "MT"))
 
1155
        return MM_SMS_STORAGE_MT;
 
1156
    if (g_str_equal (str, "SR"))
 
1157
        return MM_SMS_STORAGE_SR;
 
1158
    if (g_str_equal (str, "BM"))
 
1159
        return MM_SMS_STORAGE_BM;
 
1160
    if (g_str_equal (str, "TA"))
 
1161
        return MM_SMS_STORAGE_TA;
 
1162
    return MM_SMS_STORAGE_UNKNOWN;
 
1163
}
 
1164
 
 
1165
gboolean
 
1166
mm_3gpp_parse_cpms_test_response (const gchar *reply,
 
1167
                                  GArray **mem1,
 
1168
                                  GArray **mem2,
 
1169
                                  GArray **mem3)
 
1170
{
 
1171
    GRegex *r;
 
1172
    gchar **split;
 
1173
    guint i;
 
1174
 
 
1175
    g_assert (mem1 != NULL);
 
1176
    g_assert (mem2 != NULL);
 
1177
    g_assert (mem3 != NULL);
 
1178
 
 
1179
    /*
 
1180
     * +CPMS: ("SM","ME"),("SM","ME"),("SM","ME")
 
1181
     */
 
1182
    split = g_strsplit_set (mm_strip_tag (reply, "+CPMS:"), "()", -1);
 
1183
    if (!split)
 
1184
        return FALSE;
 
1185
 
 
1186
    r = g_regex_new ("\\s*\"([^,\\)]+)\"\\s*", 0, 0, NULL);
 
1187
    g_assert (r);
 
1188
 
 
1189
    for (i = 0; split[i]; i++) {
 
1190
        GMatchInfo *match_info;
 
1191
 
 
1192
        /* Got a range group to match */
 
1193
        if (g_regex_match_full (r, split[i], strlen (split[i]), 0, 0, &match_info, NULL)) {
 
1194
            GArray *array = NULL;
 
1195
 
 
1196
            while (g_match_info_matches (match_info)) {
 
1197
                gchar *str;
 
1198
 
 
1199
                str = g_match_info_fetch (match_info, 1);
 
1200
                if (str) {
 
1201
                    MMSmsStorage storage;
 
1202
 
 
1203
                    if (!array)
 
1204
                        array = g_array_new (FALSE, FALSE, sizeof (MMSmsStorage));
 
1205
 
 
1206
                    storage = storage_from_str (str);
 
1207
                    g_array_append_val (array, storage);
 
1208
                    g_free (str);
 
1209
                }
 
1210
 
 
1211
                g_match_info_next (match_info, NULL);
 
1212
            }
 
1213
 
 
1214
            if (!*mem1)
 
1215
                *mem1 = array;
 
1216
            else if (!*mem2)
 
1217
                *mem2 = array;
 
1218
            else if (!*mem3)
 
1219
                *mem3 = array;
 
1220
        }
 
1221
        g_match_info_free (match_info);
 
1222
 
 
1223
        if (*mem3 != NULL)
 
1224
            break; /* once we got the last group, exit... */
 
1225
    }
 
1226
 
 
1227
    g_strfreev (split);
 
1228
    g_regex_unref (r);
 
1229
 
 
1230
    g_warn_if_fail (*mem1 != NULL);
 
1231
    g_warn_if_fail (*mem2 != NULL);
 
1232
    g_warn_if_fail (*mem3 != NULL);
 
1233
 
 
1234
    return (*mem1 && *mem2 && *mem3);
 
1235
}
 
1236
 
 
1237
/*************************************************************************/
 
1238
 
 
1239
gboolean
 
1240
mm_3gpp_parse_cscs_test_response (const gchar *reply,
 
1241
                                  MMModemCharset *out_charsets)
 
1242
{
 
1243
    MMModemCharset charsets = MM_MODEM_CHARSET_UNKNOWN;
 
1244
    GRegex *r;
 
1245
    GMatchInfo *match_info;
 
1246
    gchar *p, *str;
 
1247
    gboolean success = FALSE;
 
1248
 
 
1249
    g_return_val_if_fail (reply != NULL, FALSE);
 
1250
    g_return_val_if_fail (out_charsets != NULL, FALSE);
 
1251
 
 
1252
    /* Find the first '(' or '"'; the general format is:
 
1253
     *
 
1254
     * +CSCS: ("IRA","GSM","UCS2")
 
1255
     *
 
1256
     * but some devices (some Blackberries) don't include the ().
 
1257
     */
 
1258
    p = strchr (reply, '(');
 
1259
    if (p)
 
1260
        p++;
 
1261
    else {
 
1262
        p = strchr (reply, '"');
 
1263
        if (!p)
 
1264
            return FALSE;
 
1265
    }
 
1266
 
 
1267
    /* Now parse each charset */
 
1268
    r = g_regex_new ("\\s*([^,\\)]+)\\s*", 0, 0, NULL);
 
1269
    if (!r)
 
1270
        return FALSE;
 
1271
 
 
1272
    if (g_regex_match_full (r, p, strlen (p), 0, 0, &match_info, NULL)) {
 
1273
        while (g_match_info_matches (match_info)) {
 
1274
            str = g_match_info_fetch (match_info, 1);
 
1275
            charsets |= mm_modem_charset_from_string (str);
 
1276
            g_free (str);
 
1277
 
 
1278
            g_match_info_next (match_info, NULL);
 
1279
            success = TRUE;
 
1280
        }
 
1281
    }
 
1282
    g_match_info_free (match_info);
 
1283
    g_regex_unref (r);
 
1284
 
 
1285
    if (success)
 
1286
        *out_charsets = charsets;
 
1287
 
 
1288
    return success;
 
1289
}
 
1290
 
 
1291
/*************************************************************************/
 
1292
 
 
1293
gboolean
 
1294
mm_3gpp_parse_clck_test_response (const gchar *reply,
 
1295
                                  MMModem3gppFacility *out_facilities)
 
1296
{
 
1297
    GRegex *r;
 
1298
    GMatchInfo *match_info;
 
1299
 
 
1300
    g_return_val_if_fail (reply != NULL, FALSE);
 
1301
    g_return_val_if_fail (out_facilities != NULL, FALSE);
 
1302
 
 
1303
    /* the general format is:
 
1304
     *
 
1305
     * +CLCK: ("SC","AO","AI","PN")
 
1306
     */
 
1307
    reply = mm_strip_tag (reply, "+CLCK:");
 
1308
 
 
1309
    /* Now parse each facility */
 
1310
    r = g_regex_new ("\\s*\"([^,\\)]+)\"\\s*", 0, 0, NULL);
 
1311
    g_assert (r != NULL);
 
1312
 
 
1313
    *out_facilities = MM_MODEM_3GPP_FACILITY_NONE;
 
1314
    if (g_regex_match_full (r, reply, strlen (reply), 0, 0, &match_info, NULL)) {
 
1315
        while (g_match_info_matches (match_info)) {
 
1316
            gchar *str;
 
1317
 
 
1318
            str = g_match_info_fetch (match_info, 1);
 
1319
            if (str) {
 
1320
                *out_facilities |= mm_3gpp_acronym_to_facility (str);
 
1321
                g_free (str);
 
1322
            }
 
1323
 
 
1324
            g_match_info_next (match_info, NULL);
 
1325
        }
 
1326
    }
 
1327
    g_match_info_free (match_info);
 
1328
    g_regex_unref (r);
 
1329
 
 
1330
    return (*out_facilities != MM_MODEM_3GPP_FACILITY_NONE);
 
1331
}
 
1332
 
 
1333
/*************************************************************************/
 
1334
 
 
1335
gboolean
 
1336
mm_3gpp_parse_clck_write_response (const gchar *reply,
 
1337
                                   gboolean *enabled)
 
1338
{
 
1339
    GRegex *r;
 
1340
    GMatchInfo *match_info;
 
1341
    gboolean success = FALSE;
 
1342
 
 
1343
    g_return_val_if_fail (reply != NULL, FALSE);
 
1344
    g_return_val_if_fail (enabled != NULL, FALSE);
 
1345
 
 
1346
    reply = mm_strip_tag (reply, "+CLCK:");
 
1347
 
 
1348
    r = g_regex_new ("\\s*([01])\\s*", 0, 0, NULL);
 
1349
    g_assert (r != NULL);
 
1350
 
 
1351
    if (g_regex_match (r, reply, 0, &match_info)) {
 
1352
        gchar *str;
 
1353
 
 
1354
        str = g_match_info_fetch (match_info, 1);
 
1355
        if (str) {
 
1356
            /* We're trying to match either '0' or '1',
 
1357
             * so we don't expect any other thing */
 
1358
            if (*str == '0')
 
1359
                *enabled = FALSE;
 
1360
            else if (*str == '1')
 
1361
                *enabled = TRUE;
 
1362
            else
 
1363
                g_assert_not_reached ();
 
1364
 
 
1365
            g_free (str);
 
1366
            success = TRUE;
 
1367
        }
 
1368
    }
 
1369
    g_match_info_free (match_info);
 
1370
    g_regex_unref (r);
 
1371
 
 
1372
    return success;
 
1373
}
 
1374
 
 
1375
/*************************************************************************/
 
1376
 
 
1377
GStrv
 
1378
mm_3gpp_parse_cnum_exec_response (const gchar *reply,
 
1379
                                  GError **error)
 
1380
{
 
1381
    GArray *array = NULL;
 
1382
    GRegex *r;
 
1383
    GMatchInfo *match_info;
 
1384
 
 
1385
    /* Empty strings also return NULL list */
 
1386
    if (!reply || !reply[0])
 
1387
        return NULL;
 
1388
 
 
1389
    r = g_regex_new ("\\+CNUM:\\s*((\"([^\"]|(\\\"))*\")|([^,]*)),\"(?<num>\\S+)\",\\d",
 
1390
                     G_REGEX_UNGREEDY, 0, NULL);
 
1391
    g_assert (r != NULL);
 
1392
 
 
1393
    g_regex_match (r, reply, 0, &match_info);
 
1394
    while (g_match_info_matches (match_info)) {
 
1395
        gchar *number;
 
1396
 
 
1397
        number = g_match_info_fetch_named (match_info, "num");
 
1398
 
 
1399
        if (number && number[0]) {
 
1400
            if (!array)
 
1401
                array = g_array_new (TRUE, TRUE, sizeof (gchar *));
 
1402
            g_array_append_val (array, number);
 
1403
        } else
 
1404
            g_free (number);
 
1405
 
 
1406
        g_match_info_next (match_info, NULL);
 
1407
    }
 
1408
 
 
1409
    g_match_info_free (match_info);
 
1410
    g_regex_unref (r);
 
1411
 
 
1412
    return (array ? (GStrv) g_array_free (array, FALSE) : NULL);
 
1413
}
 
1414
 
 
1415
/*************************************************************************/
 
1416
 
 
1417
struct MM3gppCindResponse {
 
1418
    gchar *desc;
 
1419
    guint idx;
 
1420
    gint min;
 
1421
    gint max;
 
1422
};
 
1423
 
 
1424
static MM3gppCindResponse *
 
1425
cind_response_new (const gchar *desc, guint idx, gint min, gint max)
 
1426
{
 
1427
    MM3gppCindResponse *r;
 
1428
    gchar *p;
 
1429
 
 
1430
    g_return_val_if_fail (desc != NULL, NULL);
 
1431
    g_return_val_if_fail (idx >= 0, NULL);
 
1432
 
 
1433
    r = g_malloc0 (sizeof (MM3gppCindResponse));
 
1434
 
 
1435
    /* Strip quotes */
 
1436
    r->desc = p = g_malloc0 (strlen (desc) + 1);
 
1437
    while (*desc) {
 
1438
        if (*desc != '"' && !isspace (*desc))
 
1439
            *p++ = tolower (*desc);
 
1440
        desc++;
 
1441
    }
 
1442
 
 
1443
    r->idx = idx;
 
1444
    r->max = max;
 
1445
    r->min = min;
 
1446
    return r;
 
1447
}
 
1448
 
 
1449
static void
 
1450
cind_response_free (MM3gppCindResponse *r)
 
1451
{
 
1452
    g_return_if_fail (r != NULL);
 
1453
 
 
1454
    g_free (r->desc);
 
1455
    memset (r, 0, sizeof (MM3gppCindResponse));
 
1456
    g_free (r);
 
1457
}
 
1458
 
 
1459
const gchar *
 
1460
mm_3gpp_cind_response_get_desc (MM3gppCindResponse *r)
 
1461
{
 
1462
    g_return_val_if_fail (r != NULL, NULL);
 
1463
 
 
1464
    return r->desc;
 
1465
}
 
1466
 
 
1467
guint
 
1468
mm_3gpp_cind_response_get_index (MM3gppCindResponse *r)
 
1469
{
 
1470
    g_return_val_if_fail (r != NULL, 0);
 
1471
 
 
1472
    return r->idx;
 
1473
}
 
1474
 
 
1475
gint
 
1476
mm_3gpp_cind_response_get_min (MM3gppCindResponse *r)
 
1477
{
 
1478
    g_return_val_if_fail (r != NULL, -1);
 
1479
 
 
1480
    return r->min;
 
1481
}
 
1482
 
 
1483
gint
 
1484
mm_3gpp_cind_response_get_max (MM3gppCindResponse *r)
 
1485
{
 
1486
    g_return_val_if_fail (r != NULL, -1);
 
1487
 
 
1488
    return r->max;
 
1489
}
 
1490
 
 
1491
#define CIND_TAG "+CIND:"
 
1492
 
 
1493
GHashTable *
 
1494
mm_3gpp_parse_cind_test_response (const gchar *reply,
 
1495
                                  GError **error)
 
1496
{
 
1497
    GHashTable *hash;
 
1498
    GRegex *r;
 
1499
    GMatchInfo *match_info;
 
1500
    guint idx = 1;
 
1501
 
 
1502
    g_return_val_if_fail (reply != NULL, NULL);
 
1503
 
 
1504
    /* Strip whitespace and response tag */
 
1505
    if (g_str_has_prefix (reply, CIND_TAG))
 
1506
        reply += strlen (CIND_TAG);
 
1507
    while (isspace (*reply))
 
1508
        reply++;
 
1509
 
 
1510
    r = g_regex_new ("\\(([^,]*),\\((\\d+)[-,](\\d+).*\\)", G_REGEX_UNGREEDY, 0, NULL);
 
1511
    if (!r) {
 
1512
        g_set_error_literal (error,
 
1513
                             MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
 
1514
                             "Could not parse scan results.");
 
1515
        return NULL;
 
1516
    }
 
1517
 
 
1518
    hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) cind_response_free);
 
1519
 
 
1520
    if (g_regex_match_full (r, reply, strlen (reply), 0, 0, &match_info, NULL)) {
 
1521
        while (g_match_info_matches (match_info)) {
 
1522
            MM3gppCindResponse *resp;
 
1523
            gchar *desc, *tmp;
 
1524
            gint min = 0, max = 0;
 
1525
 
 
1526
            desc = g_match_info_fetch (match_info, 1);
 
1527
 
 
1528
            tmp = g_match_info_fetch (match_info, 2);
 
1529
            min = atoi (tmp);
 
1530
            g_free (tmp);
 
1531
 
 
1532
            tmp = g_match_info_fetch (match_info, 3);
 
1533
            max = atoi (tmp);
 
1534
            g_free (tmp);
 
1535
 
 
1536
            resp = cind_response_new (desc, idx++, min, max);
 
1537
            if (resp)
 
1538
                g_hash_table_insert (hash, g_strdup (resp->desc), resp);
 
1539
 
 
1540
            g_free (desc);
 
1541
 
 
1542
            g_match_info_next (match_info, NULL);
 
1543
        }
 
1544
    }
 
1545
    g_match_info_free (match_info);
 
1546
    g_regex_unref (r);
 
1547
 
 
1548
    return hash;
 
1549
}
 
1550
 
 
1551
/*************************************************************************/
 
1552
 
 
1553
GByteArray *
 
1554
mm_3gpp_parse_cind_read_response (const gchar *reply,
 
1555
                                  GError **error)
 
1556
{
 
1557
    GByteArray *array = NULL;
 
1558
    GRegex *r = NULL;
 
1559
    GMatchInfo *match_info;
 
1560
    GError *inner_error = NULL;
 
1561
    guint8 t;
 
1562
 
 
1563
    g_return_val_if_fail (reply != NULL, NULL);
 
1564
 
 
1565
    if (!g_str_has_prefix (reply, CIND_TAG)) {
 
1566
        g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
 
1567
                     "Could not parse the +CIND response '%s': no CIND tag found",
 
1568
                     reply);
 
1569
        return NULL;
 
1570
    }
 
1571
 
 
1572
    reply = mm_strip_tag (reply, CIND_TAG);
 
1573
 
 
1574
    r = g_regex_new ("(\\d+)[^0-9]+", G_REGEX_UNGREEDY, 0, NULL);
 
1575
    g_assert (r != NULL);
 
1576
 
 
1577
    if (!g_regex_match_full (r, reply, strlen (reply), 0, 0, &match_info, NULL)) {
 
1578
        g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
 
1579
                     "Could not parse the +CIND response '%s': didn't match",
 
1580
                     reply);
 
1581
        goto done;
 
1582
    }
 
1583
 
 
1584
    array = g_byte_array_sized_new (g_match_info_get_match_count (match_info));
 
1585
 
 
1586
    /* Add a zero element so callers can use 1-based indexes returned by
 
1587
     * mm_3gpp_cind_response_get_index().
 
1588
     */
 
1589
    t = 0;
 
1590
    g_byte_array_append (array, &t, 1);
 
1591
 
 
1592
    while (!inner_error &&
 
1593
           g_match_info_matches (match_info)) {
 
1594
        gchar *str;
 
1595
        guint val = 0;
 
1596
 
 
1597
        str = g_match_info_fetch (match_info, 1);
 
1598
        if (mm_get_uint_from_str (str, &val) && val < 255) {
 
1599
            t = (guint8) val;
 
1600
            g_byte_array_append (array, &t, 1);
 
1601
        } else {
 
1602
            inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
 
1603
                                       "Could not parse the +CIND response: invalid index '%s'",
 
1604
                                       str);
 
1605
        }
 
1606
 
 
1607
        g_free (str);
 
1608
        g_match_info_next (match_info, NULL);
 
1609
    }
 
1610
 
 
1611
    if (inner_error) {
 
1612
        g_propagate_error (error, inner_error);
 
1613
        g_byte_array_unref (array);
 
1614
        array = NULL;
 
1615
    }
 
1616
 
 
1617
done:
 
1618
    g_match_info_free (match_info);
 
1619
    g_regex_unref (r);
 
1620
 
 
1621
    return array;
 
1622
}
 
1623
 
 
1624
/*************************************************************************/
 
1625
 
 
1626
static void
 
1627
mm_3gpp_pdu_info_free (MM3gppPduInfo *info)
 
1628
{
 
1629
    g_free (info->pdu);
 
1630
    g_free (info);
 
1631
}
 
1632
 
 
1633
void
 
1634
mm_3gpp_pdu_info_list_free (GList *info_list)
 
1635
{
 
1636
    g_list_free_full (info_list, (GDestroyNotify)mm_3gpp_pdu_info_free);
 
1637
}
 
1638
 
 
1639
GList *
 
1640
mm_3gpp_parse_pdu_cmgl_response (const gchar *str,
 
1641
                                 GError **error)
 
1642
{
 
1643
    GError *inner_error = NULL;
 
1644
    GList *list = NULL;
 
1645
    GMatchInfo *match_info;
 
1646
    GRegex *r;
 
1647
 
 
1648
    /*
 
1649
     * +CMGL: <index>, <status>, [<alpha>], <length>
 
1650
     *   or
 
1651
     * +CMGL: <index>, <status>, <length>
 
1652
     *
 
1653
     * We just read <index>, <stat> and the PDU itself.
 
1654
     */
 
1655
    r = g_regex_new ("\\+CMGL:\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,(.*)\\r\\n([^\\r\\n]*)(\\r\\n)?",
 
1656
                     G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
 
1657
    g_assert (r != NULL);
 
1658
 
 
1659
    g_regex_match_full (r, str, strlen (str), 0, 0, &match_info, &inner_error);
 
1660
    while (!inner_error && g_match_info_matches (match_info)) {
 
1661
        MM3gppPduInfo *info;
 
1662
 
 
1663
        info = g_new0 (MM3gppPduInfo, 1);
 
1664
        if (mm_get_int_from_match_info (match_info, 1, &info->index) &&
 
1665
            mm_get_int_from_match_info (match_info, 2, &info->status) &&
 
1666
            (info->pdu = mm_get_string_unquoted_from_match_info (match_info, 4)) != NULL) {
 
1667
            /* Append to our list of results and keep on */
 
1668
            list = g_list_append (list, info);
 
1669
            g_match_info_next (match_info, &inner_error);
 
1670
        } else {
 
1671
            inner_error = g_error_new (MM_CORE_ERROR,
 
1672
                                       MM_CORE_ERROR_FAILED,
 
1673
                                       "Error parsing +CMGL response: '%s'",
 
1674
                                       str);
 
1675
        }
 
1676
    }
 
1677
 
 
1678
    g_match_info_free (match_info);
 
1679
    g_regex_unref (r);
 
1680
 
 
1681
    if (inner_error) {
 
1682
        g_propagate_error (error, inner_error);
 
1683
        mm_3gpp_pdu_info_list_free (list);
 
1684
        return NULL;
 
1685
    }
 
1686
 
 
1687
    return list;
 
1688
}
 
1689
 
 
1690
/*************************************************************************/
 
1691
 
 
1692
/* Map two letter facility codes into flag values. There are
 
1693
 * many more facilities defined (for various flavors of call
 
1694
 * barring); we only map the ones we care about. */
 
1695
typedef struct {
 
1696
    MMModem3gppFacility facility;
 
1697
    gchar *acronym;
 
1698
} FacilityAcronym;
 
1699
 
 
1700
static const FacilityAcronym facility_acronyms[] = {
 
1701
    { MM_MODEM_3GPP_FACILITY_SIM,           "SC" },
 
1702
    { MM_MODEM_3GPP_FACILITY_PH_SIM,        "PS" },
 
1703
    { MM_MODEM_3GPP_FACILITY_PH_FSIM,       "PF" },
 
1704
    { MM_MODEM_3GPP_FACILITY_FIXED_DIALING, "FD" },
 
1705
    { MM_MODEM_3GPP_FACILITY_NET_PERS,      "PN" },
 
1706
    { MM_MODEM_3GPP_FACILITY_NET_SUB_PERS,  "PU" },
 
1707
    { MM_MODEM_3GPP_FACILITY_PROVIDER_PERS, "PP" },
 
1708
    { MM_MODEM_3GPP_FACILITY_CORP_PERS,     "PC" }
 
1709
};
 
1710
 
 
1711
MMModem3gppFacility
 
1712
mm_3gpp_acronym_to_facility (const gchar *str)
 
1713
{
 
1714
    guint i;
 
1715
 
 
1716
    for (i = 0; i < G_N_ELEMENTS (facility_acronyms); i++) {
 
1717
        if (g_str_equal (facility_acronyms[i].acronym, str))
 
1718
            return facility_acronyms[i].facility;
 
1719
    }
 
1720
 
 
1721
    return MM_MODEM_3GPP_FACILITY_NONE;
 
1722
}
 
1723
 
 
1724
gchar *
 
1725
mm_3gpp_facility_to_acronym (MMModem3gppFacility facility)
 
1726
{
 
1727
    guint i;
 
1728
 
 
1729
    for (i = 0; i < G_N_ELEMENTS (facility_acronyms); i++) {
 
1730
        if (facility_acronyms[i].facility == facility)
 
1731
            return facility_acronyms[i].acronym;
 
1732
    }
 
1733
 
 
1734
    return NULL;
 
1735
}
 
1736
 
 
1737
/*************************************************************************/
 
1738
 
 
1739
MMModemAccessTechnology
 
1740
mm_string_to_access_tech (const gchar *string)
 
1741
{
 
1742
    MMModemAccessTechnology act = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN;
 
1743
 
 
1744
    g_return_val_if_fail (string != NULL, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN);
 
1745
 
 
1746
    /* We're returning a MASK of technologies found; so we can include more
 
1747
     * than one technology in the result */
 
1748
    if (strcasestr (string, "LTE") || strcasestr (string, "4G"))
 
1749
        act |= MM_MODEM_ACCESS_TECHNOLOGY_LTE;
 
1750
 
 
1751
    if (strcasestr (string, "HSPA+"))
 
1752
        act |= MM_MODEM_ACCESS_TECHNOLOGY_HSPA_PLUS;
 
1753
    else if (strcasestr (string, "HSPA"))
 
1754
        act |= MM_MODEM_ACCESS_TECHNOLOGY_HSPA;
 
1755
 
 
1756
 
 
1757
    if (strcasestr (string, "HSUPA"))
 
1758
        act |= MM_MODEM_ACCESS_TECHNOLOGY_HSUPA;
 
1759
 
 
1760
    if (strcasestr (string, "HSDPA"))
 
1761
        act |= MM_MODEM_ACCESS_TECHNOLOGY_HSDPA;
 
1762
 
 
1763
    if (strcasestr (string, "UMTS"))
 
1764
        act |= MM_MODEM_ACCESS_TECHNOLOGY_UMTS;
 
1765
 
 
1766
    if (strcasestr (string, "EDGE"))
 
1767
        act |= MM_MODEM_ACCESS_TECHNOLOGY_EDGE;
 
1768
 
 
1769
    if (strcasestr (string, "GPRS"))
 
1770
        act |= MM_MODEM_ACCESS_TECHNOLOGY_GPRS;
 
1771
 
 
1772
    if (strcasestr (string, "GSM"))
 
1773
        act |= MM_MODEM_ACCESS_TECHNOLOGY_GSM;
 
1774
 
 
1775
    if (strcasestr (string, "EvDO Rel0"))
 
1776
        act |= MM_MODEM_ACCESS_TECHNOLOGY_EVDO0;
 
1777
 
 
1778
    if (strcasestr (string, "EvDO RelA"))
 
1779
        act |= MM_MODEM_ACCESS_TECHNOLOGY_EVDOA;
 
1780
 
 
1781
    if (strcasestr (string, "EvDO RelB"))
 
1782
        act |= MM_MODEM_ACCESS_TECHNOLOGY_EVDOB;
 
1783
 
 
1784
    if (strcasestr (string, "1xRTT") || strcasestr (string, "CDMA2000 1X"))
 
1785
        act |= MM_MODEM_ACCESS_TECHNOLOGY_1XRTT;
 
1786
 
 
1787
    return act;
 
1788
}
 
1789
 
 
1790
/*************************************************************************/
 
1791
 
 
1792
gchar *
 
1793
mm_3gpp_parse_operator (const gchar *reply,
 
1794
                        MMModemCharset cur_charset)
 
1795
{
 
1796
    gchar *operator = NULL;
 
1797
 
 
1798
    if (reply && !strncmp (reply, "+COPS: ", 7)) {
 
1799
        /* Got valid reply */
 
1800
                GRegex *r;
 
1801
                GMatchInfo *match_info;
 
1802
 
 
1803
                reply += 7;
 
1804
                r = g_regex_new ("(\\d),(\\d),\"(.+)\"", G_REGEX_UNGREEDY, 0, NULL);
 
1805
                if (!r)
 
1806
            return NULL;
 
1807
 
 
1808
                g_regex_match (r, reply, 0, &match_info);
 
1809
                if (g_match_info_matches (match_info))
 
1810
            operator = g_match_info_fetch (match_info, 3);
 
1811
 
 
1812
                g_match_info_free (match_info);
 
1813
                g_regex_unref (r);
 
1814
    }
 
1815
 
 
1816
    if (operator) {
 
1817
        /* Some modems (Option & HSO) return the operator name as a hexadecimal
 
1818
         * string of the bytes of the operator name as encoded by the current
 
1819
         * character set.
 
1820
         */
 
1821
        if (cur_charset == MM_MODEM_CHARSET_UCS2) {
 
1822
            /* In this case we're already checking UTF-8 validity */
 
1823
            operator = mm_charset_take_and_convert_to_utf8 (operator, MM_MODEM_CHARSET_UCS2);
 
1824
        }
 
1825
        /* Ensure the operator name is valid UTF-8 so that we can send it
 
1826
         * through D-Bus and such.
 
1827
         */
 
1828
        else if (!g_utf8_validate (operator, -1, NULL)) {
 
1829
            g_free (operator);
 
1830
            return NULL;
 
1831
        }
 
1832
 
 
1833
        /* Some modems (Novatel LTE) return the operator name as "Unknown" when
 
1834
         * it fails to obtain the operator name. Return NULL in such case.
 
1835
         */
 
1836
        if (operator && g_ascii_strcasecmp (operator, "unknown") == 0) {
 
1837
            g_free (operator);
 
1838
            return NULL;
 
1839
        }
 
1840
    }
 
1841
 
 
1842
    return operator;
 
1843
}
 
1844
 
 
1845
/*************************************************************************/
 
1846
 
 
1847
const gchar *
 
1848
mm_3gpp_get_pdp_type_from_ip_family (MMBearerIpFamily family)
 
1849
{
 
1850
    switch (family) {
 
1851
    case MM_BEARER_IP_FAMILY_IPV4:
 
1852
        return "IP";
 
1853
    case MM_BEARER_IP_FAMILY_IPV6:
 
1854
        return "IPV6";
 
1855
    case MM_BEARER_IP_FAMILY_IPV4V6:
 
1856
        return "IPV4V6";
 
1857
    default:
 
1858
        return NULL;
 
1859
    }
 
1860
}
 
1861
 
 
1862
MMBearerIpFamily
 
1863
mm_3gpp_get_ip_family_from_pdp_type (const gchar *pdp_type)
 
1864
{
 
1865
    if (g_str_equal (pdp_type, "IP"))
 
1866
        return MM_BEARER_IP_FAMILY_IPV4;
 
1867
    if (g_str_equal (pdp_type, "IPV6"))
 
1868
        return MM_BEARER_IP_FAMILY_IPV6;
 
1869
    if (g_str_equal (pdp_type, "IPV4V6"))
 
1870
        return MM_BEARER_IP_FAMILY_IPV4V6;
 
1871
    return MM_BEARER_IP_FAMILY_NONE;
 
1872
}
 
1873
 
 
1874
/*************************************************************************/
 
1875
 
 
1876
gboolean
 
1877
mm_3gpp_parse_operator_id (const gchar *operator_id,
 
1878
                           guint16 *mcc,
 
1879
                           guint16 *mnc,
 
1880
                           GError **error)
 
1881
{
 
1882
    guint len;
 
1883
    guint i;
 
1884
    gchar aux[4];
 
1885
    guint16 tmp;
 
1886
 
 
1887
    g_assert (operator_id != NULL);
 
1888
 
 
1889
    len = strlen (operator_id);
 
1890
    if (len != 5 && len != 6) {
 
1891
        g_set_error (error,
 
1892
                     MM_CORE_ERROR,
 
1893
                     MM_CORE_ERROR_FAILED,
 
1894
                     "Operator ID must have 5 or 6 digits");
 
1895
        return FALSE;
 
1896
    }
 
1897
 
 
1898
    for (i = 0; i < len; i++) {
 
1899
        if (!g_ascii_isdigit (operator_id[i])) {
 
1900
            g_set_error (error,
 
1901
                         MM_CORE_ERROR,
 
1902
                         MM_CORE_ERROR_FAILED,
 
1903
                         "Operator ID must only contain digits");
 
1904
            return FALSE;
 
1905
        }
 
1906
    }
 
1907
 
 
1908
    memcpy (&aux[0], operator_id, 3);
 
1909
    aux[3] = '\0';
 
1910
    tmp = atoi (aux);
 
1911
    if (tmp == 0) {
 
1912
        g_set_error (error,
 
1913
                     MM_CORE_ERROR,
 
1914
                     MM_CORE_ERROR_FAILED,
 
1915
                     "MCC must not be zero");
 
1916
        return FALSE;
 
1917
    }
 
1918
 
 
1919
    if (mcc)
 
1920
        *mcc = tmp;
 
1921
 
 
1922
    if (mnc) {
 
1923
        if (len == 5) {
 
1924
            memcpy (&aux[0], &operator_id[3], 2);
 
1925
            aux[2] = '\0';
 
1926
        } else
 
1927
            memcpy (&aux[0], &operator_id[3], 3);
 
1928
        *mnc = atoi (aux);
 
1929
    }
 
1930
 
 
1931
    return TRUE;
 
1932
}
 
1933
 
 
1934
/*************************************************************************/
 
1935
 
 
1936
gboolean
 
1937
mm_cdma_parse_spservice_read_response (const gchar *reply,
 
1938
                                       MMModemCdmaRegistrationState *out_cdma_1x_state,
 
1939
                                       MMModemCdmaRegistrationState *out_evdo_state)
 
1940
{
 
1941
    const gchar *p;
504
1942
 
505
1943
    g_return_val_if_fail (reply != NULL, FALSE);
506
1944
    g_return_val_if_fail (out_cdma_1x_state != NULL, FALSE);
533
1971
/*************************************************************************/
534
1972
 
535
1973
typedef struct {
536
 
    int num;
 
1974
    gint num;
537
1975
    gboolean roam_ind;
538
 
    const char *banner;
 
1976
    const gchar *banner;
539
1977
} EriItem;
540
1978
 
541
1979
/* NOTE: these may be Sprint-specific for now... */
731
2169
};
732
2170
 
733
2171
gboolean
734
 
mm_cdma_parse_eri (const char *reply,
 
2172
mm_cdma_parse_eri (const gchar *reply,
735
2173
                   gboolean *out_roaming,
736
 
                   guint32 *out_ind,
737
 
                   const char **out_desc)
 
2174
                   guint *out_ind,
 
2175
                   const gchar **out_desc)
738
2176
{
739
 
    long int ind;
 
2177
    guint ind;
740
2178
    const EriItem *iter = &eris[0];
741
2179
    gboolean found = FALSE;
742
2180
 
743
2181
    g_return_val_if_fail (reply != NULL, FALSE);
744
2182
    g_return_val_if_fail (out_roaming != NULL, FALSE);
745
2183
 
746
 
    errno = 0;
747
 
    ind = strtol (reply, NULL, 10);
748
 
    if (errno == 0) {
 
2184
    if (mm_get_uint_from_str (reply, &ind)) {
749
2185
        if (out_ind)
750
2186
            *out_ind = ind;
751
2187
 
767
2203
/*************************************************************************/
768
2204
 
769
2205
gboolean
770
 
mm_gsm_parse_cscs_support_response (const char *reply,
771
 
                                    MMModemCharset *out_charsets)
 
2206
mm_cdma_parse_crm_test_response (const gchar *reply,
 
2207
                                 MMModemCdmaRmProtocol *min,
 
2208
                                 MMModemCdmaRmProtocol *max,
 
2209
                                 GError **error)
772
2210
{
773
 
    MMModemCharset charsets = MM_MODEM_CHARSET_UNKNOWN;
 
2211
    gboolean result = FALSE;
774
2212
    GRegex *r;
775
 
    GMatchInfo *match_info;
776
 
    char *p, *str;
 
2213
    GMatchInfo *match_info = NULL;
 
2214
    GError *match_error = NULL;
 
2215
 
 
2216
    /* Expected reply format is:
 
2217
     *   ---> AT+CRM=?
 
2218
     *   <--- +CRM: (0-2)
 
2219
     */
 
2220
 
 
2221
    r = g_regex_new ("\\+CRM:\\s*\\((\\d+)-(\\d+)\\)",
 
2222
                     G_REGEX_DOLLAR_ENDONLY | G_REGEX_RAW,
 
2223
                     0, error);
 
2224
    g_assert (r != NULL);
 
2225
 
 
2226
    if (g_regex_match_full (r, reply, strlen (reply), 0, 0, &match_info, &match_error)) {
 
2227
        gchar *aux;
 
2228
        guint min_val = 0;
 
2229
        guint max_val = 0;
 
2230
 
 
2231
        aux = g_match_info_fetch (match_info, 1);
 
2232
        min_val = (guint) atoi (aux);
 
2233
        g_free (aux);
 
2234
 
 
2235
        aux = g_match_info_fetch (match_info, 2);
 
2236
        max_val = (guint) atoi (aux);
 
2237
        g_free (aux);
 
2238
 
 
2239
        if (min_val == 0 ||
 
2240
            max_val == 0 ||
 
2241
            min_val >= max_val) {
 
2242
            g_set_error (error,
 
2243
                         MM_CORE_ERROR,
 
2244
                         MM_CORE_ERROR_FAILED,
 
2245
                         "Couldn't parse CRM range: "
 
2246
                         "Unexpected range of RM protocols (%u,%u)",
 
2247
                         min_val,
 
2248
                         max_val);
 
2249
        } else {
 
2250
            *min = mm_cdma_get_rm_protocol_from_index (min_val, error);
 
2251
            if (*min != MM_MODEM_CDMA_RM_PROTOCOL_UNKNOWN) {
 
2252
                *max = mm_cdma_get_rm_protocol_from_index (max_val, error);
 
2253
                if (*max != MM_MODEM_CDMA_RM_PROTOCOL_UNKNOWN)
 
2254
                    result = TRUE;
 
2255
            }
 
2256
        }
 
2257
    } else if (match_error) {
 
2258
        g_propagate_error (error, match_error);
 
2259
    } else {
 
2260
        g_set_error (error,
 
2261
                     MM_CORE_ERROR,
 
2262
                     MM_CORE_ERROR_FAILED,
 
2263
                     "Couldn't parse CRM range: response didn't match (%s)",
 
2264
                     reply);
 
2265
    }
 
2266
 
 
2267
    g_match_info_free (match_info);
 
2268
    g_regex_unref (r);
 
2269
 
 
2270
    return result;
 
2271
}
 
2272
 
 
2273
/*************************************************************************/
 
2274
 
 
2275
MMModemCdmaRmProtocol
 
2276
mm_cdma_get_rm_protocol_from_index (guint index,
 
2277
                                    GError **error)
 
2278
{
 
2279
    guint protocol;
 
2280
 
 
2281
    /* just adding 1 from the index value should give us the enum */
 
2282
    protocol = index + 1 ;
 
2283
    if (protocol > MM_MODEM_CDMA_RM_PROTOCOL_STU_III) {
 
2284
        g_set_error (error,
 
2285
                     MM_CORE_ERROR,
 
2286
                     MM_CORE_ERROR_FAILED,
 
2287
                     "Unexpected RM protocol index (%u)",
 
2288
                     index);
 
2289
        protocol = MM_MODEM_CDMA_RM_PROTOCOL_UNKNOWN;
 
2290
    }
 
2291
 
 
2292
    return (MMModemCdmaRmProtocol)protocol;
 
2293
}
 
2294
 
 
2295
guint
 
2296
mm_cdma_get_index_from_rm_protocol (MMModemCdmaRmProtocol protocol,
 
2297
                                    GError **error)
 
2298
{
 
2299
    if (protocol == MM_MODEM_CDMA_RM_PROTOCOL_UNKNOWN) {
 
2300
        g_set_error (error,
 
2301
                     MM_CORE_ERROR,
 
2302
                     MM_CORE_ERROR_FAILED,
 
2303
                     "Unexpected RM protocol (%s)",
 
2304
                     mm_modem_cdma_rm_protocol_get_string (protocol));
 
2305
        return 0;
 
2306
    }
 
2307
 
 
2308
    /* just substracting 1 from the enum value should give us the index */
 
2309
    return (protocol - 1);
 
2310
}
 
2311
 
 
2312
/*************************************************************************/
 
2313
 
 
2314
gint
 
2315
mm_cdma_normalize_class (const gchar *orig_class)
 
2316
{
 
2317
    gchar class;
 
2318
 
 
2319
    g_return_val_if_fail (orig_class != NULL, '0');
 
2320
 
 
2321
    class = toupper (orig_class[0]);
 
2322
 
 
2323
    /* Cellular (850MHz) */
 
2324
    if (class == '1' || class == 'C')
 
2325
        return 1;
 
2326
    /* PCS (1900MHz) */
 
2327
    if (class == '2' || class == 'P')
 
2328
        return 2;
 
2329
 
 
2330
    /* Unknown/not registered */
 
2331
    return 0;
 
2332
}
 
2333
 
 
2334
/*************************************************************************/
 
2335
 
 
2336
gchar
 
2337
mm_cdma_normalize_band (const gchar *long_band,
 
2338
                        gint *out_class)
 
2339
{
 
2340
    gchar band;
 
2341
 
 
2342
    g_return_val_if_fail (long_band != NULL, 'Z');
 
2343
 
 
2344
    /* There are two response formats for the band; one includes the band
 
2345
     * class and the other doesn't.  For modems that include the band class
 
2346
     * (ex Novatel S720) you'll see "Px" or "Cx" depending on whether the modem
 
2347
     * is registered on a PCS/1900 (P) or Cellular/850 (C) system.
 
2348
     */
 
2349
    band = toupper (long_band[0]);
 
2350
 
 
2351
    /* Possible band class in first position; return it */
 
2352
    if (band == 'C' || band == 'P') {
 
2353
        gchar tmp[2] = { band, '\0' };
 
2354
 
 
2355
        *out_class = mm_cdma_normalize_class (tmp);
 
2356
        band = toupper (long_band[1]);
 
2357
    }
 
2358
 
 
2359
    /* normalize to A - F, and Z */
 
2360
    if (band >= 'A' && band <= 'F')
 
2361
        return band;
 
2362
 
 
2363
    /* Unknown/not registered */
 
2364
    return 'Z';
 
2365
}
 
2366
 
 
2367
/*************************************************************************/
 
2368
/* Caller must strip any "+GSN:" or "+CGSN" from @gsn */
 
2369
 
 
2370
gboolean
 
2371
mm_parse_gsn (const char *gsn,
 
2372
              gchar **out_imei,
 
2373
              gchar **out_meid,
 
2374
              gchar **out_esn)
 
2375
{
 
2376
    gchar **items, **iter;
 
2377
    gchar *meid = NULL, *esn = NULL, *imei = NULL, *p;
777
2378
    gboolean success = FALSE;
778
2379
 
779
 
    g_return_val_if_fail (reply != NULL, FALSE);
780
 
    g_return_val_if_fail (out_charsets != NULL, FALSE);
781
 
 
782
 
    /* Find the first '(' or '"'; the general format is:
783
 
     *
784
 
     * +CSCS: ("IRA","GSM","UCS2")
785
 
     *
786
 
     * but some devices (some Blackberries) don't include the ().
787
 
     */
788
 
    p = strchr (reply, '(');
789
 
    if (p)
790
 
        p++;
791
 
    else {
792
 
        p = strchr (reply, '"');
793
 
        if (!p)
794
 
            return FALSE;
795
 
    }
796
 
 
797
 
    /* Now parse each charset */
798
 
    r = g_regex_new ("\\s*([^,\\)]+)\\s*", 0, 0, NULL);
799
 
    if (!r)
 
2380
    if (!gsn || !gsn[0])
800
2381
        return FALSE;
801
2382
 
802
 
    if (g_regex_match_full (r, p, strlen (p), 0, 0, &match_info, NULL)) {
803
 
        while (g_match_info_matches (match_info)) {
804
 
            str = g_match_info_fetch (match_info, 1);
805
 
            charsets |= mm_modem_charset_from_string (str);
806
 
            g_free (str);
807
 
 
808
 
            g_match_info_next (match_info, NULL);
809
 
            success = TRUE;
 
2383
    /* IMEI is 15 numeric digits */
 
2384
 
 
2385
    /* ESNs take one of two formats:
 
2386
     *  (1) 7 or 8 hexadecimal digits
 
2387
     *  (2) 10 or 11 decimal digits
 
2388
     *
 
2389
     * In addition, leading zeros may be present or absent, and hexadecimal
 
2390
     * ESNs may or may not be prefixed with "0x".
 
2391
     */
 
2392
 
 
2393
    /* MEIDs take one of two formats:
 
2394
     *  (1) 14 hexadecimal digits, sometimes padded to 16 digits with leading zeros
 
2395
     *  (2) 18 decimal digits
 
2396
     *
 
2397
     * As with ESNs, leading zeros may be present or absent, and hexadecimal
 
2398
     * MEIDs may or may not be prefixed with "0x".
 
2399
     */
 
2400
 
 
2401
    items = g_strsplit_set (gsn, "\r\n\t: ,", 0);
 
2402
    for (iter = items; iter && *iter && (!esn || !meid); iter++) {
 
2403
        gboolean expect_hex = FALSE, is_hex, is_digit;
 
2404
        gchar *s = *iter;
 
2405
        guint len = 0;
 
2406
 
 
2407
        if (!s[0])
 
2408
            continue;
 
2409
 
 
2410
        if (g_str_has_prefix (s, "0x") || g_str_has_prefix (s, "0X")) {
 
2411
            expect_hex = TRUE;
 
2412
            s += 2;
 
2413
 
 
2414
            /* Skip any leading zeros */
 
2415
            while (*s == '0')
 
2416
                s++;
 
2417
        }
 
2418
 
 
2419
        /* Check whether all digits are hex or decimal */
 
2420
        is_hex = is_digit = TRUE;
 
2421
        p = s;
 
2422
        while (*p && (is_hex || is_digit)) {
 
2423
            if (!g_ascii_isxdigit (*p))
 
2424
                is_hex = FALSE;
 
2425
            if (!g_ascii_isdigit (*p))
 
2426
                is_digit = FALSE;
 
2427
            p++, len++;
 
2428
        }
 
2429
 
 
2430
        /* Note that some hex strings are also valid digit strings */
 
2431
 
 
2432
        if (is_hex) {
 
2433
            if (len == 7 || len == 8) {
 
2434
                /* ESN */
 
2435
                if (!esn) {
 
2436
                    if (len == 7)
 
2437
                        esn = g_strdup_printf ("0%s", s);
 
2438
                    else
 
2439
                        esn = g_strdup (s);
 
2440
                }
 
2441
            } else if (len == 14) {
 
2442
                /* MEID */
 
2443
                if (!meid)
 
2444
                    meid = g_strdup (s);
 
2445
            }
 
2446
        }
 
2447
 
 
2448
        if (is_digit) {
 
2449
            if (!is_hex)
 
2450
                g_warn_if_fail (expect_hex == FALSE);
 
2451
 
 
2452
            if (len == 15) {
 
2453
                if (!imei)
 
2454
                    imei = g_strdup (s);
 
2455
            }
 
2456
 
 
2457
            /* Decimal ESN/MEID unhandled for now; conversion from decimal to
 
2458
             * hex isn't a straight dec->hex conversion, as the first 2 digits
 
2459
             * of the ESN and first 3 digits of the MEID are the manufacturer
 
2460
             * identifier and must be converted separately from serial number
 
2461
             * and then concatenated with it.
 
2462
             */
810
2463
        }
811
2464
    }
812
 
    g_match_info_free (match_info);
813
 
    g_regex_unref (r);
814
 
 
815
 
    if (success)
816
 
        *out_charsets = charsets;
 
2465
    g_strfreev (items);
 
2466
 
 
2467
    success = meid || esn || imei;
 
2468
 
 
2469
    if (out_imei)
 
2470
        *out_imei = imei;
 
2471
    else
 
2472
        g_free (imei);
 
2473
 
 
2474
    if (out_meid)
 
2475
        *out_meid = meid;
 
2476
    else
 
2477
        g_free (meid);
 
2478
 
 
2479
    if (out_esn)
 
2480
        *out_esn = esn;
 
2481
    else
 
2482
        g_free (esn);
817
2483
 
818
2484
    return success;
819
2485
}
820
 
 
821
 
/*************************************************************************/
822
 
 
823
 
MMModemGsmAccessTech
824
 
mm_gsm_string_to_access_tech (const char *string)
825
 
{
826
 
    g_return_val_if_fail (string != NULL, MM_MODEM_GSM_ACCESS_TECH_UNKNOWN);
827
 
 
828
 
    /* Better technologies are listed first since modems sometimes say
829
 
     * stuff like "GPRS/EDGE" and that should be handled as EDGE.
830
 
     */
831
 
    if (strcasestr (string, "HSPA+"))
832
 
        return MM_MODEM_GSM_ACCESS_TECH_HSPA_PLUS;
833
 
    else if (strcasestr (string, "HSPA"))
834
 
        return MM_MODEM_GSM_ACCESS_TECH_HSPA;
835
 
    else if (strcasestr (string, "HSDPA/HSUPA"))
836
 
        return MM_MODEM_GSM_ACCESS_TECH_HSPA;
837
 
    else if (strcasestr (string, "HSUPA"))
838
 
        return MM_MODEM_GSM_ACCESS_TECH_HSUPA;
839
 
    else if (strcasestr (string, "HSDPA"))
840
 
        return MM_MODEM_GSM_ACCESS_TECH_HSDPA;
841
 
    else if (strcasestr (string, "UMTS"))
842
 
        return MM_MODEM_GSM_ACCESS_TECH_UMTS;
843
 
    else if (strcasestr (string, "EDGE"))
844
 
        return MM_MODEM_GSM_ACCESS_TECH_EDGE;
845
 
    else if (strcasestr (string, "GPRS"))
846
 
        return MM_MODEM_GSM_ACCESS_TECH_GPRS;
847
 
    else if (strcasestr (string, "GSM"))
848
 
        return MM_MODEM_GSM_ACCESS_TECH_GSM;
849
 
 
850
 
    return MM_MODEM_GSM_ACCESS_TECH_UNKNOWN;
851
 
}
852
 
 
853
 
/*************************************************************************/
854
 
 
855
 
char *
856
 
mm_create_device_identifier (guint vid,
857
 
                             guint pid,
858
 
                             const char *ati,
859
 
                             const char *ati1,
860
 
                             const char *gsn,
861
 
                             const char *revision,
862
 
                             const char *model,
863
 
                             const char *manf)
864
 
{
865
 
    GString *devid, *msg = NULL;
866
 
    GChecksum *sum;
867
 
    char *p, *ret = NULL;
868
 
    char str_vid[10], str_pid[10];
869
 
 
870
 
    /* Build up the device identifier */
871
 
    devid = g_string_sized_new (50);
872
 
    if (ati)
873
 
        g_string_append (devid, ati);
874
 
    if (ati1) {
875
 
        /* Only append "ATI1" if it's differnet than "ATI" */
876
 
        if (!ati || (strcmp (ati, ati1) != 0))
877
 
            g_string_append (devid, ati1);
878
 
    }
879
 
    if (gsn)
880
 
        g_string_append (devid, gsn);
881
 
    if (revision)
882
 
        g_string_append (devid, revision);
883
 
    if (model)
884
 
        g_string_append (devid, model);
885
 
    if (manf)
886
 
        g_string_append (devid, manf);
887
 
 
888
 
    if (!strlen (devid->str)) {
889
 
        g_string_free (devid, TRUE);
890
 
        return NULL;
891
 
    }
892
 
 
893
 
    p = devid->str;
894
 
    msg = g_string_sized_new (strlen (devid->str) + 17);
895
 
 
896
 
    sum = g_checksum_new (G_CHECKSUM_SHA1);
897
 
 
898
 
    if (vid) {
899
 
        snprintf (str_vid, sizeof (str_vid) - 1, "%08x", vid);
900
 
        g_checksum_update (sum, (const guchar *) &str_vid[0], strlen (str_vid));
901
 
        g_string_append_printf (msg, "%08x", vid);
902
 
    }
903
 
    if (vid) {
904
 
        snprintf (str_pid, sizeof (str_pid) - 1, "%08x", pid);
905
 
        g_checksum_update (sum, (const guchar *) &str_pid[0], strlen (str_pid));
906
 
        g_string_append_printf (msg, "%08x", pid);
907
 
    }
908
 
 
909
 
    while (*p) {
910
 
        /* Strip spaces and linebreaks */
911
 
        if (!isblank (*p) && !isspace (*p) && isascii (*p)) {
912
 
            g_checksum_update (sum, (const guchar *) p, 1);
913
 
            g_string_append_c (msg, *p);
914
 
        }
915
 
        p++;
916
 
    }
917
 
    ret = g_strdup (g_checksum_get_string (sum));
918
 
    g_checksum_free (sum);
919
 
 
920
 
    mm_dbg ("Device ID source '%s'", msg->str);
921
 
    mm_dbg ("Device ID '%s'", ret);
922
 
    g_string_free (msg, TRUE);
923
 
    g_string_free (devid, TRUE);
924
 
 
925
 
    return ret;
926
 
}
927
 
 
928
 
/*************************************************************************/
929
 
 
930
 
struct CindResponse {
931
 
    char *desc;
932
 
    guint idx;
933
 
    gint min;
934
 
    gint max;
935
 
};
936
 
 
937
 
static CindResponse *
938
 
cind_response_new (const char *desc, guint idx, gint min, gint max)
939
 
{
940
 
    CindResponse *r;
941
 
    char *p;
942
 
 
943
 
    g_return_val_if_fail (desc != NULL, NULL);
944
 
    g_return_val_if_fail (idx >= 0, NULL);
945
 
 
946
 
    r = g_malloc0 (sizeof (CindResponse));
947
 
 
948
 
    /* Strip quotes */
949
 
    r->desc = p = g_malloc0 (strlen (desc) + 1);
950
 
    while (*desc) {
951
 
        if (*desc != '"' && !isspace (*desc))
952
 
            *p++ = tolower (*desc);
953
 
        desc++;
954
 
    }
955
 
 
956
 
    r->idx = idx;
957
 
    r->max = max;
958
 
    r->min = min;
959
 
    return r;
960
 
}
961
 
 
962
 
static void
963
 
cind_response_free (CindResponse *r)
964
 
{
965
 
    g_return_if_fail (r != NULL);
966
 
 
967
 
    g_free (r->desc);
968
 
    memset (r, 0, sizeof (CindResponse));
969
 
    g_free (r);
970
 
}
971
 
 
972
 
const char *
973
 
cind_response_get_desc (CindResponse *r)
974
 
{
975
 
    g_return_val_if_fail (r != NULL, NULL);
976
 
 
977
 
    return r->desc;
978
 
}
979
 
 
980
 
guint
981
 
cind_response_get_index (CindResponse *r)
982
 
{
983
 
    g_return_val_if_fail (r != NULL, 0);
984
 
 
985
 
    return r->idx;
986
 
}
987
 
 
988
 
gint
989
 
cind_response_get_min (CindResponse *r)
990
 
{
991
 
    g_return_val_if_fail (r != NULL, -1);
992
 
 
993
 
    return r->min;
994
 
}
995
 
 
996
 
gint
997
 
cind_response_get_max (CindResponse *r)
998
 
{
999
 
    g_return_val_if_fail (r != NULL, -1);
1000
 
 
1001
 
    return r->max;
1002
 
}
1003
 
 
1004
 
#define CIND_TAG "+CIND:"
1005
 
 
1006
 
GHashTable *
1007
 
mm_parse_cind_test_response (const char *reply, GError **error)
1008
 
{
1009
 
    GHashTable *hash;
1010
 
    GRegex *r;
1011
 
    GMatchInfo *match_info;
1012
 
    guint idx = 1;
1013
 
 
1014
 
    g_return_val_if_fail (reply != NULL, NULL);
1015
 
 
1016
 
    /* Strip whitespace and response tag */
1017
 
    if (g_str_has_prefix (reply, CIND_TAG))
1018
 
        reply += strlen (CIND_TAG);
1019
 
    while (isspace (*reply))
1020
 
        reply++;
1021
 
 
1022
 
    r = g_regex_new ("\\(([^,]*),\\((\\d+)[-,](\\d+)\\)", G_REGEX_UNGREEDY, 0, NULL);
1023
 
    if (!r) {
1024
 
        g_set_error_literal (error,
1025
 
                             MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL,
1026
 
                             "Could not parse scan results.");
1027
 
        return NULL;
1028
 
    }
1029
 
 
1030
 
    hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) cind_response_free);
1031
 
 
1032
 
    if (g_regex_match_full (r, reply, strlen (reply), 0, 0, &match_info, NULL)) {
1033
 
        while (g_match_info_matches (match_info)) {
1034
 
            CindResponse *resp;
1035
 
            char *desc, *tmp;
1036
 
            gint min = 0, max = 0;
1037
 
 
1038
 
            desc = g_match_info_fetch (match_info, 1);
1039
 
 
1040
 
            tmp = g_match_info_fetch (match_info, 2);
1041
 
            min = atoi (tmp);
1042
 
            g_free (tmp);
1043
 
 
1044
 
            tmp = g_match_info_fetch (match_info, 3);
1045
 
            max = atoi (tmp);
1046
 
            g_free (tmp);
1047
 
 
1048
 
            resp = cind_response_new (desc, idx++, min, max);
1049
 
            if (resp)
1050
 
                g_hash_table_insert (hash, g_strdup (resp->desc), resp);
1051
 
 
1052
 
            g_free (desc);
1053
 
 
1054
 
            g_match_info_next (match_info, NULL);
1055
 
        }
1056
 
    }
1057
 
    g_match_info_free (match_info);
1058
 
    g_regex_unref (r);
1059
 
 
1060
 
    return hash;
1061
 
}
1062
 
 
1063
 
GByteArray *
1064
 
mm_parse_cind_query_response(const char *reply, GError **error)
1065
 
{
1066
 
    GByteArray *array = NULL;
1067
 
    const char *p = reply;
1068
 
    GRegex *r = NULL;
1069
 
    GMatchInfo *match_info;
1070
 
    guint8 t = 0;
1071
 
 
1072
 
    g_return_val_if_fail (reply != NULL, NULL);
1073
 
 
1074
 
    if (!g_str_has_prefix (p, CIND_TAG)) {
1075
 
        g_set_error_literal (error, MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL,
1076
 
                             "Could not parse the +CIND response");
1077
 
        return NULL;
1078
 
    }
1079
 
 
1080
 
    p += strlen (CIND_TAG);
1081
 
    while (isspace (*p))
1082
 
        p++;
1083
 
 
1084
 
    r = g_regex_new ("(\\d+)[^0-9]+", G_REGEX_UNGREEDY, 0, NULL);
1085
 
    if (!r) {
1086
 
        g_set_error_literal (error, MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL,
1087
 
                             "Internal failure attempting to parse +CIND response");
1088
 
        return NULL;
1089
 
    }
1090
 
 
1091
 
    if (!g_regex_match_full (r, p, strlen (p), 0, 0, &match_info, NULL)) {
1092
 
        g_set_error_literal (error, MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL,
1093
 
                             "Failure parsing the +CIND response");
1094
 
        goto done;
1095
 
    }
1096
 
 
1097
 
    array = g_byte_array_sized_new (g_match_info_get_match_count (match_info));
1098
 
 
1099
 
    /* Add a zero element so callers can use 1-based indexes returned by
1100
 
     * cind_response_get_index().
1101
 
     */
1102
 
    g_byte_array_append (array, &t, 1);
1103
 
 
1104
 
    while (g_match_info_matches (match_info)) {
1105
 
        char *str;
1106
 
        gulong val;
1107
 
 
1108
 
        str = g_match_info_fetch (match_info, 1);
1109
 
 
1110
 
        errno = 0;
1111
 
        val = strtoul (str, NULL, 10);
1112
 
 
1113
 
        t = 0;
1114
 
        if ((errno == 0) && (val < 255))
1115
 
            t = (guint8) val;
1116
 
        /* FIXME: indicate errors somehow? */
1117
 
        g_byte_array_append (array, &t, 1);
1118
 
 
1119
 
        g_free (str);
1120
 
        g_match_info_next (match_info, NULL);
1121
 
    }
1122
 
 
1123
 
done:
1124
 
    g_match_info_free (match_info);
1125
 
    g_regex_unref (r);
1126
 
 
1127
 
    return array;
1128
 
}
1129