static void
nwrat_query_ready (MMBaseModem *self,
                   GAsyncResult *res,
                   GSimpleAsyncResult *simple)
{
    LoadCurrentModesResult result;
    GError *error = NULL;
    const gchar *response;
    GRegex *r;
    GMatchInfo *match_info = NULL;
    gint a = -1;
    gint b = -1;

    response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
    if (!response) {
        g_simple_async_result_take_error (simple, error);
        g_simple_async_result_complete (simple);
        g_object_unref (simple);
        return;
    }

    /* Parse response */
    r = g_regex_new ("\\$NWRAT:\\s*(\\d),(\\d),(\\d)", G_REGEX_UNGREEDY, 0, NULL);
    g_assert (r != NULL);

    if (!g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, &error)) {
        if (error)
            g_simple_async_result_take_error (simple, error);
        else
            g_simple_async_result_set_error (simple,
                                             MM_CORE_ERROR,
                                             MM_CORE_ERROR_FAILED,
                                             "Couldn't match NWRAT reply: %s",
                                             response);
        g_simple_async_result_complete (simple);
        g_object_unref (simple);
        g_match_info_free (match_info);
        g_regex_unref (r);
        return;
    }

    if (!mm_get_int_from_match_info (match_info, 1, &a) ||
        !mm_get_int_from_match_info (match_info, 2, &b) ||
        a < 0 || a > 2 ||
        b < 1 || b > 2) {
        g_simple_async_result_set_error (
            simple,
            MM_CORE_ERROR,
            MM_CORE_ERROR_FAILED,
            "Failed to parse mode/tech response '%s': invalid modes reported",
            response);
        g_match_info_free (match_info);
        g_regex_unref (r);
        g_simple_async_result_complete (simple);
        g_object_unref (simple);
        return;
    }

    switch (a) {
    case 0:
        result.allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G);
        result.preferred = MM_MODEM_MODE_NONE;
        break;
    case 1:
        if (b == 1) {
            result.allowed = MM_MODEM_MODE_2G;
            result.preferred = MM_MODEM_MODE_NONE;
        } else /* b == 2 */ {
            result.allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G);
            result.preferred = MM_MODEM_MODE_2G;
        }
        break;
    case 2:
        if (b == 1) {
            result.allowed = MM_MODEM_MODE_3G;
            result.preferred = MM_MODEM_MODE_NONE;
        } else /* b == 2 */ {
            result.allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G);
            result.preferred = MM_MODEM_MODE_3G;
        }
        break;
    default:
        /* We only allow mode 0|1|2 */
        g_assert_not_reached ();
        break;
    }

    g_match_info_free (match_info);
    g_regex_unref (r);

    /* When a valid result is given, we never complete in idle */
    g_simple_async_result_set_op_res_gpointer (simple, &result, NULL);
    g_simple_async_result_complete (simple);
    g_object_unref (simple);
}
static gboolean
load_allowed_modes_finish (MMIfaceModem *self,
                           GAsyncResult *res,
                           MMModemMode *allowed,
                           MMModemMode *preferred,
                           GError **error)
{
    const gchar *response;
    GMatchInfo *match_info = NULL;
    GRegex *r;
    gint cm_mode = -1;
    gint pref_acq = -1;
    gboolean result;

    response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error);
    if (!response)
        return FALSE;

    r = g_regex_new ("\\+ZSNT:\\s*(\\d),(\\d),(\\d)", G_REGEX_UNGREEDY, 0, error);
    g_assert (r != NULL);

    result = FALSE;
    if (!g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, error))
        goto done;

    if (!mm_get_int_from_match_info (match_info, 1, &cm_mode) ||
        cm_mode < 0 || (cm_mode > 2 && cm_mode != 6) ||
        !mm_get_int_from_match_info (match_info, 3, &pref_acq) ||
        pref_acq < 0 || pref_acq > 2) {
        g_set_error (error,
                     MM_CORE_ERROR,
                     MM_CORE_ERROR_FAILED,
                     "Failed to parse the allowed mode response: '%s'",
                     response);
        goto done;
    }

    /* Correctly parsed! */
    result = TRUE;
    if (cm_mode == 0) {
        /* Both 2G, 3G and LTE allowed. For LTE modems, no 2G/3G preference supported. */
        if (pref_acq == 0 || mm_iface_modem_is_3gpp_lte (self)) {
            /* Any allowed */
            *allowed = MM_MODEM_MODE_ANY;
            *preferred = MM_MODEM_MODE_NONE;
        } else if (pref_acq == 1) {
            *allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G);
            *preferred = MM_MODEM_MODE_2G;
        } else if (pref_acq == 2) {
            *allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G);
            *preferred = MM_MODEM_MODE_3G;
        } else
            g_assert_not_reached ();
    } else if (cm_mode == 1) {
        /* GSM only */
        *allowed = MM_MODEM_MODE_2G;
        *preferred = MM_MODEM_MODE_NONE;
    } else if (cm_mode == 2) {
        /* WCDMA only */
        *allowed = MM_MODEM_MODE_3G;
        *preferred = MM_MODEM_MODE_NONE;
    } else if (cm_mode == 6) {
        /* LTE only */
        *allowed = MM_MODEM_MODE_4G;
        *preferred = MM_MODEM_MODE_NONE;
    } else
        g_assert_not_reached ();

done:
    if (match_info)
        g_match_info_free (match_info);
    if (r)
        g_regex_unref (r);

    return result;
}
static gboolean
parse_nwltime_reply (const char *response,
                     gchar **out_iso_8601,
                     MMNetworkTimezone **out_tz,
                     GError **error)
{
    GRegex *r;
    GMatchInfo *match_info = NULL;
    GError *match_error = NULL;
    guint year, month, day, hour, minute, second;
    gchar *result = NULL;
    gint utc_offset = 0;
    gboolean success = FALSE;

    /* Sample reply: 2013.3.27.15.47.19.2.-5 */
    r = g_regex_new ("(\\d+)\\.(\\d+)\\.(\\d+)\\.(\\d+)\\.(\\d+)\\.(\\d+)\\.(\\d+)\\.([\\-\\+\\d]+)$", 0, 0, NULL);
    g_assert (r != NULL);

    if (!g_regex_match_full (r, response, -1, 0, 0, &match_info, &match_error)) {
        if (match_error) {
            g_propagate_error (error, match_error);
            g_prefix_error (error, "Could not parse $NWLTIME results: ");
        } else {
            g_set_error_literal (error,
                                 MM_CORE_ERROR,
                                 MM_CORE_ERROR_FAILED,
                                 "Couldn't match $NWLTIME reply");
        }
    } else {
        /* Remember that g_match_info_get_match_count() includes match #0 */
        g_assert (g_match_info_get_match_count (match_info) >= 9);

        if (mm_get_uint_from_match_info (match_info, 1, &year) &&
            mm_get_uint_from_match_info (match_info, 2, &month) &&
            mm_get_uint_from_match_info (match_info, 3, &day) &&
            mm_get_uint_from_match_info (match_info, 4, &hour) &&
            mm_get_uint_from_match_info (match_info, 5, &minute) &&
            mm_get_uint_from_match_info (match_info, 6, &second) &&
            mm_get_int_from_match_info (match_info, 8, &utc_offset)) {

            result = mm_new_iso8601_time (year, month, day, hour, minute, second,
                                          TRUE, utc_offset * 60);
            if (out_tz) {
                *out_tz = mm_network_timezone_new ();
                mm_network_timezone_set_offset (*out_tz, utc_offset * 60);
            }

            success = TRUE;
        } else {
            g_set_error_literal (error,
                                 MM_CORE_ERROR,
                                 MM_CORE_ERROR_FAILED,
                                 "Failed to parse $NWLTIME reply");
        }
    }

    if (out_iso_8601)
        *out_iso_8601 = result;
    else
        g_free (result);

    if (match_info)
        g_match_info_free (match_info);
    g_regex_unref (r);
    return success;
}
gboolean mm_huawei_parse_nwtime_response (const gchar *response,
                                          gchar **iso8601p,
                                          MMNetworkTimezone **tzp,
                                          GError **error)
{
    GRegex *r;
    GMatchInfo *match_info = NULL;
    GError *match_error = NULL;
    guint year = 0, month = 0, day = 0, hour = 0, minute = 0, second = 0, dt = 0;
    gint tz = 0;
    gboolean ret = FALSE;

    g_assert (iso8601p || tzp); /* at least one */

    r = g_regex_new ("\\^NWTIME:\\s*(\\d+)/(\\d+)/(\\d+),(\\d+):(\\d+):(\\d*)([\\-\\+\\d]+),(\\d+)$", 0, 0, NULL);
    g_assert (r != NULL);

    if (!g_regex_match_full (r, response, -1, 0, 0, &match_info, &match_error)) {
        if (match_error) {
            g_propagate_error (error, match_error);
            g_prefix_error (error, "Could not parse ^NWTIME results: ");
        } else {
            g_set_error_literal (error,
                                 MM_CORE_ERROR,
                                 MM_CORE_ERROR_FAILED,
                                 "Couldn't match ^NWTIME reply");
        }
    } else {
        /* Remember that g_match_info_get_match_count() includes match #0 */
        g_assert (g_match_info_get_match_count (match_info) >= 9);

        if (mm_get_uint_from_match_info (match_info, 1, &year) &&
            mm_get_uint_from_match_info (match_info, 2, &month) &&
            mm_get_uint_from_match_info (match_info, 3, &day) &&
            mm_get_uint_from_match_info (match_info, 4, &hour) &&
            mm_get_uint_from_match_info (match_info, 5, &minute) &&
            mm_get_uint_from_match_info (match_info, 6, &second) &&
            mm_get_int_from_match_info  (match_info, 7, &tz) &&
            mm_get_uint_from_match_info (match_info, 8, &dt)) {
            /* adjust year */
            if (year < 100)
                year += 2000;
            /*
             * tz = timezone offset in 15 minute intervals
             * dt = daylight adjustment, 0 = none, 1 = 1 hour, 2 = 2 hours
             *      other values are marked reserved.
             */
            if (iso8601p) {
                /* Return ISO-8601 format date/time string */
                *iso8601p = mm_new_iso8601_time (year, month, day, hour,
                                                 minute, second,
                                                 TRUE, (tz * 15) + (dt * 60));
            }
            if (tzp) {
                *tzp = mm_network_timezone_new ();
                mm_network_timezone_set_offset (*tzp, tz * 15);
                mm_network_timezone_set_dst_offset (*tzp, dt * 60);
            }

            ret = TRUE;
        } else {
            g_set_error_literal (error,
                                 MM_CORE_ERROR,
                                 MM_CORE_ERROR_FAILED,
                                 "Failed to parse ^NWTIME reply");
        }
    }

    if (match_info)
        g_match_info_free (match_info);
    g_regex_unref (r);

    return ret;
}