const gchar *
mm_base_modem_at_command_finish (MMBaseModem *self,
                                 GAsyncResult *res,
                                 GError **error)
{
    return mm_base_modem_at_command_full_finish (self, res, error);
}
static void
authenticate_ready (MMBaseModem *modem,
                    GAsyncResult *res,
                    Dial3gppContext *ctx)
{
    gchar *command;

    /* If cancelled, complete */
    if (dial_3gpp_context_complete_and_free_if_cancelled (ctx))
        return;

    if (!mm_base_modem_at_command_full_finish (modem, res, NULL)) {
        /* Try the next auth command */
        ctx->auth_idx++;
        authenticate (ctx);
        return;
    }

    /* Store which auth command worked, for next attempts */
    ctx->self->priv->auth_idx = ctx->auth_idx;

    /* Success, activate the PDP context and start the data session */
    command = g_strdup_printf ("AT_OWANCALL=%d,1,1",
                               ctx->cid);
    mm_base_modem_at_command_full (ctx->modem,
                                   ctx->primary,
                                   command,
                                   3,
                                   FALSE,
                                   NULL, /* cancellable */
                                   (GAsyncReadyCallback)activate_ready,
                                   ctx);
    g_free (command);
}
static void
disconnect_ndisdup_ready (MMBaseModem *modem,
                          GAsyncResult *res,
                          MMBroadbandBearerHuawei *self)
{
    Disconnect3gppContext *ctx;
    GError *error = NULL;

    ctx = self->priv->disconnect_pending;
    g_assert (ctx != NULL);

    /* Balance refcount */
    g_object_unref (self);

    if (!mm_base_modem_at_command_full_finish (modem, res, &error)) {
        /* Clear context */
        self->priv->disconnect_pending = NULL;
        g_simple_async_result_take_error (ctx->result, error);
        disconnect_3gpp_context_complete_and_free (ctx);
        return;
    }

    /* Go to next step */
    ctx->step++;
    disconnect_3gpp_context_step (ctx);
}
static void
activate_ready (MMBaseModem *modem,
                GAsyncResult *res,
                Dial3gppContext *ctx)
{
    GError *error = NULL;

    /* From now on, if we get cancelled, we'll need to run the connection
     * reset ourselves just in case */

    if (!mm_base_modem_at_command_full_finish (modem, res, &error)) {
        g_simple_async_result_take_error (ctx->result, error);
        g_simple_async_result_complete (ctx->result);
        g_object_unref (ctx->result);
        return;
    }

    /* We will now setup a timeout and keep the context in the bearer's private.
     * Reports of modem being connected will arrive via unsolicited messages. */
    g_assert (ctx->self->priv->connect_pending == NULL);
    ctx->self->priv->connect_pending = ctx;
    ctx->self->priv->connect_pending_id = g_timeout_add_seconds (30,
                                                                 (GSourceFunc)connect_timed_out_cb,
                                                                 ctx->self);
    ctx->self->priv->connect_cancellable_id = g_cancellable_connect (ctx->cancellable,
                                                                     G_CALLBACK (connect_cancelled_cb),
                                                                     ctx->self,
                                                                     NULL);
}
static void
connect_reset_ready (MMBaseModem *modem,
                     GAsyncResult *res,
                     Dial3gppContext *ctx)
{
    mm_base_modem_at_command_full_finish (modem, res, NULL);

    /* error should have already been set in the simple async result */
    dial_3gpp_context_complete_and_free (ctx);
}
static void
selrat_set_ready (MMBaseModem *self,
                  GAsyncResult *res,
                  GSimpleAsyncResult *simple)
{
    GError *error = NULL;

    if (!mm_base_modem_at_command_full_finish (MM_BASE_MODEM (self), res, &error))
        /* Let the error be critical. */
        g_simple_async_result_take_error (simple, error);
    else
        g_simple_async_result_set_op_res_gboolean (simple, TRUE);
    g_simple_async_result_complete (simple);
    g_object_unref (simple);
}
static void
disconnect_ndisstatqry_check_ready (MMBaseModem *modem,
                                    GAsyncResult *res,
                                    MMBroadbandBearerHuawei *self)
{
    Disconnect3gppContext *ctx;
    const gchar *response;
    GError *error = NULL;
    gboolean ipv4_available = FALSE;
    gboolean ipv4_connected = FALSE;
    gboolean ipv6_available = FALSE;
    gboolean ipv6_connected = FALSE;

    ctx = self->priv->disconnect_pending;
    g_assert (ctx != NULL);

    /* Balance refcount */
    g_object_unref (self);

    response = mm_base_modem_at_command_full_finish (modem, res, &error);
    if (!response ||
        !mm_huawei_parse_ndisstatqry_response (response,
                                               &ipv4_available,
                                               &ipv4_connected,
                                               &ipv6_available,
                                               &ipv6_connected,
                                               &error)) {
        ctx->failed_ndisstatqry_count++;
        mm_dbg ("Unexpected response to ^NDISSTATQRY command: %s (Attempts so far: %u)",
                error->message, ctx->failed_ndisstatqry_count);
        g_error_free (error);
    }

    /* Disconnected IPv4? */
    if (ipv4_available && !ipv4_connected) {
        /* Success! */
        ctx->step++;
        disconnect_3gpp_context_step (ctx);
        return;
    }

    /* Setup timeout to retry the same step */
    g_timeout_add_seconds (1,
                           (GSourceFunc)disconnect_retry_ndisstatqry_check_cb,
                           g_object_ref (self));
}
static void
disconnect_owancall_ready (MMBaseModem *modem,
                           GAsyncResult *res,
                           DisconnectContext *ctx)
{
    GError *error = NULL;

    /* Ignore errors for now */
    mm_base_modem_at_command_full_finish (MM_BASE_MODEM (modem), res, &error);
    if (error) {
        mm_dbg ("Disconnection failed (not fatal): %s", error->message);
        g_error_free (error);
    }

    g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
    disconnect_context_complete_and_free (ctx);
}
static void
ip_config_ready (MMBaseModem *modem,
                 GAsyncResult *res,
                 GetIpConfig3gppContext *ctx)
{
    MMBearerIpConfig *ip_config = NULL;
    const gchar *response;
    GError *error = NULL;
    gchar **items;
    gchar *dns[3] = { 0 };
    guint i;
    guint dns_i;
    guint32 tmp;

    response = mm_base_modem_at_command_full_finish (MM_BASE_MODEM (modem), res, &error);
    if (error) {
        g_simple_async_result_take_error (ctx->result, error);
        get_ip_config_context_complete_and_free (ctx);
        return;
    }

    /* TODO: use a regex to parse this */

    /* Check result */
    if (!g_str_has_prefix (response, OWANDATA_TAG)) {
        g_simple_async_result_set_error (ctx->result,
                                         MM_CORE_ERROR,
                                         MM_CORE_ERROR_FAILED,
                                         "Couldn't get IP config: invalid response '%s'",
                                         response);
        get_ip_config_context_complete_and_free (ctx);
        return;
    }

    response = mm_strip_tag (response, OWANDATA_TAG);
    items = g_strsplit (response, ", ", 0);

    for (i = 0, dns_i = 0; items[i]; i++) {
        if (i == 0) { /* CID */
            glong num;

            errno = 0;
            num = strtol (items[i], NULL, 10);
            if (errno != 0 || num < 0 || (gint) num != ctx->cid) {
                error = g_error_new (MM_CORE_ERROR,
                                     MM_CORE_ERROR_FAILED,
                                     "Unknown CID in OWANDATA response ("
                                     "got %d, expected %d)", (guint) num, ctx->cid);
                break;
            }
        } else if (i == 1) { /* IP address */
            if (!inet_pton (AF_INET, items[i], &tmp))
                break;

            ip_config = mm_bearer_ip_config_new ();
            mm_bearer_ip_config_set_method (ip_config, MM_BEARER_IP_METHOD_STATIC);
            mm_bearer_ip_config_set_address (ip_config,  items[i]);
        } else if (i == 3 || i == 4) { /* DNS entries */
            if (!inet_pton (AF_INET, items[i], &tmp)) {
                g_clear_object (&ip_config);
                break;
            }

            dns[dns_i++] = items[i];
        }
    }

    if (!ip_config) {
        if (error)
            g_simple_async_result_take_error (ctx->result, error);
        else
            g_simple_async_result_set_error (ctx->result,
                                             MM_CORE_ERROR,
                                             MM_CORE_ERROR_FAILED,
                                             "Couldn't get IP config: couldn't parse response '%s'",
                                             response);
    } else {
        /* If we got DNS entries, set them in the IP config */
        if (dns[0])
            mm_bearer_ip_config_set_dns (ip_config, (const gchar **)dns);

        g_simple_async_result_set_op_res_gpointer (ctx->result,
                                                   ip_config,
                                                   (GDestroyNotify)g_object_unref);
    }

    get_ip_config_context_complete_and_free (ctx);
    g_strfreev (items);
}
static void
selrat_query_ready (MMBaseModem *self,
                    GAsyncResult *res,
                    GSimpleAsyncResult *simple)
{
    LoadAllowedModesResult result;
    const gchar *response;
    GError *error = NULL;
    GRegex *r = NULL;
    GMatchInfo *match_info = NULL;

    response = mm_base_modem_at_command_full_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;
    }

    /* Example response: !SELRAT: 03, UMTS 3G Preferred */
    r = g_regex_new ("!SELRAT:\\s*(\\d+).*$", 0, 0, NULL);
    g_assert (r != NULL);

    if (g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, &error)) {
        guint mode;

        if (mm_get_uint_from_match_info (match_info, 1, &mode) &&
            mode >= 0 &&
            mode <= 7) {
            switch (mode) {
            case 0:
                result.allowed = MM_MODEM_MODE_ANY;
                result.preferred = MM_MODEM_MODE_NONE;
                break;
            case 1:
                result.allowed = MM_MODEM_MODE_3G;
                result.preferred = MM_MODEM_MODE_NONE;
                break;
            case 2:
                result.allowed = MM_MODEM_MODE_2G;
                result.preferred = MM_MODEM_MODE_NONE;
                break;
            case 3:
                /* in Sierra LTE devices, mode 3 is automatic, including LTE, no preference */
                if (mm_iface_modem_is_3gpp_lte (MM_IFACE_MODEM (self))) {
                    result.allowed = MM_MODEM_MODE_ANY;
                    result.preferred = MM_MODEM_MODE_NONE;
                } else {
                    result.allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G);
                    result.preferred = MM_MODEM_MODE_3G;
                }
                break;
            case 4:
                /* in Sierra LTE devices, mode 4 is automatic, including LTE, no preference */
                if (mm_iface_modem_is_3gpp_lte (MM_IFACE_MODEM (self))) {
                    result.allowed = MM_MODEM_MODE_ANY;
                    result.preferred = MM_MODEM_MODE_NONE;
                } else {
                    result.allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G);
                    result.preferred = MM_MODEM_MODE_2G;
                }
                break;
            case 5:
                result.allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G);
                result.preferred = MM_MODEM_MODE_NONE;
                break;
            case 6:
                result.allowed = MM_MODEM_MODE_4G;
                result.preferred = MM_MODEM_MODE_NONE;
                break;
            case 7:
                result.allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G);
                result.preferred = MM_MODEM_MODE_NONE;
                break;
            default:
                g_assert_not_reached ();
                break;
            }
        } else
            error = g_error_new (MM_CORE_ERROR,
                                 MM_CORE_ERROR_FAILED,
                                 "Failed to parse the allowed mode response: '%s'",
                                 response);
    }

    if (error)
        g_simple_async_result_take_error (simple, error);
    else
        g_simple_async_result_set_op_res_gpointer (simple, &result, NULL);
    g_simple_async_result_complete (simple);
    g_object_unref (simple);
}