blob: d88c73a8c7906fa64d363471a0b3eca6aac44b6c [file] [log] [blame]
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* qmicli -- Command line interface to control QMI devices
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Copyright (C) 2012-2017 Aleksander Morgado <aleksander@aleksander.es>
*/
#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <locale.h>
#include <string.h>
#include <glib.h>
#include <gio/gio.h>
#include <libqmi-glib.h>
#include "qmicli.h"
#include "qmicli-helpers.h"
#if defined HAVE_QMI_SERVICE_UIM
#undef VALIDATE_MASK_NONE
#define VALIDATE_MASK_NONE(str) (str ? str : "none")
/* Context */
typedef struct {
QmiDevice *device;
QmiClientUim *client;
GCancellable *cancellable;
/* For Slot Status indication */
guint slot_status_indication_id;
guint refresh_indication_id;
} Context;
static Context *ctx;
/* Options */
static gchar *read_transparent_str;
static gchar *read_record_str;
static gchar *set_pin_protection_str;
static gchar *verify_pin_str;
static gchar *unblock_pin_str;
static gchar *change_pin_str;
static gchar *get_file_attributes_str;
static gchar *sim_power_on_str;
static gchar *sim_power_off_str;
static gchar *change_provisioning_session_str;
static gchar *switch_slot_str;
static gchar *depersonalization_str;
static gchar **monitor_refresh_file_array;
static gboolean get_card_status_flag;
static gboolean get_supported_messages_flag;
static gboolean get_slot_status_flag;
static gboolean monitor_slot_status_flag;
static gboolean reset_flag;
static gboolean monitor_refresh_all_flag;
static gboolean noop_flag;
static gboolean get_configuration_flag;
#undef VALIDATE_UNKNOWN
#define VALIDATE_UNKNOWN(str) (str ? str : "unknown")
static GOptionEntry entries[] = {
#if defined HAVE_QMI_MESSAGE_UIM_SET_PIN_PROTECTION
{ "uim-set-pin-protection", 0, 0, G_OPTION_ARG_STRING, &set_pin_protection_str,
"Set PIN protection",
"[(PIN1|PIN2|UPIN),(disable|enable),(current PIN)]"
},
#endif
#if defined HAVE_QMI_MESSAGE_UIM_VERIFY_PIN
{ "uim-verify-pin", 0, 0, G_OPTION_ARG_STRING, &verify_pin_str,
"Verify PIN",
"[(PIN1|PIN2|UPIN),(current PIN)]",
},
#endif
#if defined HAVE_QMI_MESSAGE_UIM_UNBLOCK_PIN
{ "uim-unblock-pin", 0, 0, G_OPTION_ARG_STRING, &unblock_pin_str,
"Unblock PIN",
"[(PIN1|PIN2|UPIN),(PUK),(new PIN)]",
},
#endif
#if defined HAVE_QMI_MESSAGE_UIM_CHANGE_PIN
{ "uim-change-pin", 0, 0, G_OPTION_ARG_STRING, &change_pin_str,
"Change PIN",
"[(PIN1|PIN2|UPIN),(old PIN),(new PIN)]",
},
#endif
#if defined HAVE_QMI_MESSAGE_UIM_READ_TRANSPARENT
{ "uim-read-transparent", 0, 0, G_OPTION_ARG_STRING, &read_transparent_str,
"Read a transparent file given the file path",
"[0xNNNN,0xNNNN,...]"
},
#endif
#if defined HAVE_QMI_MESSAGE_UIM_GET_FILE_ATTRIBUTES
{ "uim-get-file-attributes", 0, 0, G_OPTION_ARG_STRING, &get_file_attributes_str,
"Get the attributes of a given file",
"[0xNNNN,0xNNNN,...]"
},
#endif
#if defined HAVE_QMI_MESSAGE_UIM_READ_RECORD
{ "uim-read-record", 0, 0, G_OPTION_ARG_STRING, &read_record_str,
"Read a record from given file (allowed keys: record-number, record-length, file ([0xNNNN-0xNNNN,...])",
"[\"key=value,...\"]"
},
#endif
#if defined HAVE_QMI_MESSAGE_UIM_GET_CARD_STATUS
{ "uim-get-card-status", 0, 0, G_OPTION_ARG_NONE, &get_card_status_flag,
"Get card status",
NULL
},
#endif
#if defined HAVE_QMI_MESSAGE_UIM_GET_SUPPORTED_MESSAGES
{ "uim-get-supported-messages", 0, 0, G_OPTION_ARG_NONE, &get_supported_messages_flag,
"Get supported messages",
NULL
},
#endif
#if defined HAVE_QMI_MESSAGE_UIM_POWER_ON_SIM
{ "uim-sim-power-on", 0, 0, G_OPTION_ARG_STRING, &sim_power_on_str,
"Power on SIM card",
"[(slot number)]"
},
#endif
#if defined HAVE_QMI_MESSAGE_UIM_POWER_OFF_SIM
{ "uim-sim-power-off", 0, 0, G_OPTION_ARG_STRING, &sim_power_off_str,
"Power off SIM card",
"[(slot number)]"
},
#endif
#if defined HAVE_QMI_MESSAGE_UIM_CHANGE_PROVISIONING_SESSION
{ "uim-change-provisioning-session", 0, 0, G_OPTION_ARG_STRING, &change_provisioning_session_str,
"Change provisioning session (allowed keys: session-type, activate, slot, aid)",
"[\"key=value,...\"]"
},
#endif
#if defined HAVE_QMI_MESSAGE_UIM_GET_SLOT_STATUS
{ "uim-get-slot-status", 0, 0, G_OPTION_ARG_NONE, &get_slot_status_flag,
"Get slot status",
NULL
},
#endif
#if defined HAVE_QMI_MESSAGE_UIM_SWITCH_SLOT && defined HAVE_QMI_MESSAGE_UIM_GET_SLOT_STATUS
{ "uim-switch-slot", 0, 0, G_OPTION_ARG_STRING, &switch_slot_str,
"Switch active physical slot",
"[(slot number)]"
},
#endif
#if defined HAVE_QMI_INDICATION_UIM_SLOT_STATUS
{ "uim-monitor-slot-status", 0, 0, G_OPTION_ARG_NONE, &monitor_slot_status_flag,
"Watch for slot status indications",
NULL
},
#endif
#if defined HAVE_QMI_MESSAGE_UIM_RESET
{ "uim-reset", 0, 0, G_OPTION_ARG_NONE, &reset_flag,
"Reset the service state",
NULL
},
#endif
#if defined HAVE_QMI_MESSAGE_UIM_REFRESH_REGISTER
{ "uim-monitor-refresh-file", 0, 0, G_OPTION_ARG_STRING_ARRAY, &monitor_refresh_file_array,
"Watch for REFRESH events for given file paths",
"[0xNNNN,0xNNNN,...]"
},
#endif
#if defined HAVE_QMI_MESSAGE_UIM_REFRESH_REGISTER_ALL
{ "uim-monitor-refresh-all", 0, 0, G_OPTION_ARG_NONE, &monitor_refresh_all_flag,
"Watch for REFRESH events for any file",
NULL
},
#endif
#if defined HAVE_QMI_MESSAGE_UIM_GET_CONFIGURATION
{ "uim-get-configuration", 0, 0, G_OPTION_ARG_NONE, &get_configuration_flag,
"Get personalization status of the modem",
NULL
},
#endif
#if defined HAVE_QMI_MESSAGE_UIM_DEPERSONALIZATION
{ "uim-depersonalization", 0, 0, G_OPTION_ARG_STRING, &depersonalization_str,
"Deactivates or unblocks personalization feature",
"[(feature),(operation),(control key)[,(slot number)]]"
},
#endif
{ "uim-noop", 0, 0, G_OPTION_ARG_NONE, &noop_flag,
"Just allocate or release a UIM client. Use with `--client-no-release-cid' and/or `--client-cid'",
NULL
},
{ NULL }
};
GOptionGroup *
qmicli_uim_get_option_group (void)
{
GOptionGroup *group;
group = g_option_group_new ("uim",
"UIM options:",
"Show User Identity Module options",
NULL,
NULL);
g_option_group_add_entries (group, entries);
return group;
}
gboolean
qmicli_uim_options_enabled (void)
{
static guint n_actions = 0;
static gboolean checked = FALSE;
if (checked)
return !!n_actions;
n_actions = (!!set_pin_protection_str +
!!verify_pin_str +
!!unblock_pin_str +
!!change_pin_str +
!!read_transparent_str +
!!read_record_str +
!!get_file_attributes_str +
!!sim_power_on_str +
!!sim_power_off_str +
!!change_provisioning_session_str +
!!switch_slot_str +
!!monitor_refresh_file_array +
!!depersonalization_str +
get_card_status_flag +
get_supported_messages_flag +
get_slot_status_flag +
monitor_slot_status_flag +
reset_flag +
monitor_refresh_all_flag +
get_configuration_flag +
noop_flag);
if (n_actions > 1) {
g_printerr ("error: too many UIM actions requested\n");
exit (EXIT_FAILURE);
}
if (monitor_slot_status_flag || monitor_refresh_file_array || monitor_refresh_all_flag)
qmicli_expect_indications ();
checked = TRUE;
return !!n_actions;
}
static void
context_free (Context *context)
{
if (!context)
return;
if (context->slot_status_indication_id)
g_signal_handler_disconnect (context->client,
context->slot_status_indication_id);
if (context->refresh_indication_id)
g_signal_handler_disconnect (context->client,
context->refresh_indication_id);
if (context->client)
g_object_unref (context->client);
g_object_unref (context->cancellable);
g_object_unref (context->device);
g_slice_free (Context, context);
}
static void
operation_shutdown (gboolean operation_status)
{
/* Cleanup context and finish async operation */
context_free (ctx);
qmicli_async_operation_done (operation_status, FALSE);
}
#if defined HAVE_QMI_MESSAGE_UIM_SET_PIN_PROTECTION
static QmiMessageUimSetPinProtectionInput *
set_pin_protection_input_create (const gchar *str)
{
QmiMessageUimSetPinProtectionInput *input = NULL;
gchar **split;
QmiUimPinId pin_id;
gboolean enable_disable;
gchar *current_pin;
/* Prepare inputs.
* Format of the string is:
* "[(PIN1|PIN2|UPIN),(disable|enable),(current PIN)]"
*/
split = g_strsplit (str, ",", -1);
if (qmicli_read_uim_pin_id_from_string (split[0], &pin_id) &&
qmicli_read_enable_disable_from_string (split[1], &enable_disable) &&
qmicli_read_non_empty_string (split[2], "current PIN", &current_pin)) {
GError *error = NULL;
GArray *placeholder_aid;
placeholder_aid = g_array_new (FALSE, FALSE, sizeof (guint8));
input = qmi_message_uim_set_pin_protection_input_new ();
if (!qmi_message_uim_set_pin_protection_input_set_info (
input,
pin_id,
enable_disable,
current_pin,
&error) ||
!qmi_message_uim_set_pin_protection_input_set_session (
input,
QMI_UIM_SESSION_TYPE_CARD_SLOT_1,
placeholder_aid, /* ignored */
&error)) {
g_printerr ("error: couldn't create input data bundle: '%s'\n",
error->message);
g_error_free (error);
qmi_message_uim_set_pin_protection_input_unref (input);
input = NULL;
}
g_array_unref (placeholder_aid);
}
g_strfreev (split);
return input;
}
static void
set_pin_protection_ready (QmiClientUim *client,
GAsyncResult *res)
{
QmiMessageUimSetPinProtectionOutput *output;
GError *error = NULL;
output = qmi_client_uim_set_pin_protection_finish (client, res, &error);
if (!output) {
g_printerr ("error: operation failed: %s\n", error->message);
g_error_free (error);
operation_shutdown (FALSE);
return;
}
if (!qmi_message_uim_set_pin_protection_output_get_result (output, &error)) {
guint8 verify_retries_left;
guint8 unblock_retries_left;
g_printerr ("error: couldn't set PIN protection: %s\n", error->message);
g_error_free (error);
if (qmi_message_uim_set_pin_protection_output_get_retries_remaining (
output,
&verify_retries_left,
&unblock_retries_left,
NULL)) {
g_printerr ("[%s] Retries left:\n"
"\tVerify: %u\n"
"\tUnblock: %u\n",
qmi_device_get_path_display (ctx->device),
verify_retries_left,
unblock_retries_left);
}
qmi_message_uim_set_pin_protection_output_unref (output);
operation_shutdown (FALSE);
return;
}
g_print ("[%s] PIN protection updated\n",
qmi_device_get_path_display (ctx->device));
qmi_message_uim_set_pin_protection_output_unref (output);
operation_shutdown (TRUE);
}
#endif /* HAVE_QMI_MESSAGE_UIM_SET_PIN_PROTECTION */
#if defined HAVE_QMI_MESSAGE_UIM_VERIFY_PIN
static QmiMessageUimVerifyPinInput *
verify_pin_input_create (const gchar *str)
{
QmiMessageUimVerifyPinInput *input = NULL;
gchar **split;
QmiUimPinId pin_id;
gchar *current_pin;
/* Prepare inputs.
* Format of the string is:
* "[(PIN1|PIN2),(current PIN)]"
*/
split = g_strsplit (str, ",", -1);
if (qmicli_read_uim_pin_id_from_string (split[0], &pin_id) &&
qmicli_read_non_empty_string (split[1], "current PIN", &current_pin)) {
GError *error = NULL;
GArray *placeholder_aid;
placeholder_aid = g_array_new (FALSE, FALSE, sizeof (guint8));
input = qmi_message_uim_verify_pin_input_new ();
if (!qmi_message_uim_verify_pin_input_set_info (
input,
pin_id,
current_pin,
&error) ||
!qmi_message_uim_verify_pin_input_set_session (
input,
QMI_UIM_SESSION_TYPE_CARD_SLOT_1,
placeholder_aid, /* ignored */
&error)) {
g_printerr ("error: couldn't create input data bundle: '%s'\n",
error->message);
g_error_free (error);
qmi_message_uim_verify_pin_input_unref (input);
input = NULL;
}
g_array_unref (placeholder_aid);
}
g_strfreev (split);
return input;
}
static void
verify_pin_ready (QmiClientUim *client,
GAsyncResult *res)
{
QmiMessageUimVerifyPinOutput *output;
GError *error = NULL;
output = qmi_client_uim_verify_pin_finish (client, res, &error);
if (!output) {
g_printerr ("error: operation failed: %s\n", error->message);
g_error_free (error);
operation_shutdown (FALSE);
return;
}
if (!qmi_message_uim_verify_pin_output_get_result (output, &error)) {
guint8 verify_retries_left;
guint8 unblock_retries_left;
g_printerr ("error: couldn't verify PIN: %s\n", error->message);
g_error_free (error);
if (qmi_message_uim_verify_pin_output_get_retries_remaining (
output,
&verify_retries_left,
&unblock_retries_left,
NULL)) {
g_printerr ("[%s] Retries left:\n"
"\tVerify: %u\n"
"\tUnblock: %u\n",
qmi_device_get_path_display (ctx->device),
verify_retries_left,
unblock_retries_left);
}
qmi_message_uim_verify_pin_output_unref (output);
operation_shutdown (FALSE);
return;
}
g_print ("[%s] PIN verified successfully\n",
qmi_device_get_path_display (ctx->device));
qmi_message_uim_verify_pin_output_unref (output);
operation_shutdown (TRUE);
}
#endif /* HAVE_QMI_MESSAGE_UIM_VERIFY_PIN */
#if defined HAVE_QMI_MESSAGE_UIM_UNBLOCK_PIN
static QmiMessageUimUnblockPinInput *
unblock_pin_input_create (const gchar *str)
{
QmiMessageUimUnblockPinInput *input = NULL;
gchar **split;
QmiUimPinId pin_id;
gchar *puk;
gchar *new_pin;
/* Prepare inputs.
* Format of the string is:
* "[(PIN|PIN2),(PUK),(new PIN)]"
*/
split = g_strsplit (str, ",", -1);
if (qmicli_read_uim_pin_id_from_string (split[0], &pin_id) &&
qmicli_read_non_empty_string (split[1], "PUK", &puk) &&
qmicli_read_non_empty_string (split[2], "new PIN", &new_pin)) {
GError *error = NULL;
GArray *placeholder_aid;
placeholder_aid = g_array_new (FALSE, FALSE, sizeof (guint8));
input = qmi_message_uim_unblock_pin_input_new ();
if (!qmi_message_uim_unblock_pin_input_set_info (
input,
pin_id,
puk,
new_pin,
&error) ||
!qmi_message_uim_unblock_pin_input_set_session (
input,
QMI_UIM_SESSION_TYPE_CARD_SLOT_1,
placeholder_aid, /* ignored */
&error)) {
g_printerr ("error: couldn't create input data bundle: '%s'\n",
error->message);
g_error_free (error);
qmi_message_uim_unblock_pin_input_unref (input);
input = NULL;
}
g_array_unref (placeholder_aid);
}
g_strfreev (split);
return input;
}
static void
unblock_pin_ready (QmiClientUim *client,
GAsyncResult *res)
{
QmiMessageUimUnblockPinOutput *output;
GError *error = NULL;
output = qmi_client_uim_unblock_pin_finish (client, res, &error);
if (!output) {
g_printerr ("error: operation failed: %s\n", error->message);
g_error_free (error);
operation_shutdown (FALSE);
return;
}
if (!qmi_message_uim_unblock_pin_output_get_result (output, &error)) {
guint8 verify_retries_left;
guint8 unblock_retries_left;
g_printerr ("error: couldn't unblock PIN: %s\n", error->message);
g_error_free (error);
if (qmi_message_uim_unblock_pin_output_get_retries_remaining (
output,
&verify_retries_left,
&unblock_retries_left,
NULL)) {
g_printerr ("[%s] Retries left:\n"
"\tVerify: %u\n"
"\tUnblock: %u\n",
qmi_device_get_path_display (ctx->device),
verify_retries_left,
unblock_retries_left);
}
qmi_message_uim_unblock_pin_output_unref (output);
operation_shutdown (FALSE);
return;
}
g_print ("[%s] PIN unblocked successfully\n",
qmi_device_get_path_display (ctx->device));
qmi_message_uim_unblock_pin_output_unref (output);
operation_shutdown (TRUE);
}
#endif /* HAVE_QMI_MESSAGE_UIM_UNBLOCK_PIN */
#if defined HAVE_QMI_MESSAGE_UIM_CHANGE_PIN
static QmiMessageUimChangePinInput *
change_pin_input_create (const gchar *str)
{
QmiMessageUimChangePinInput *input = NULL;
gchar **split;
QmiUimPinId pin_id;
gchar *old_pin;
gchar *new_pin;
/* Prepare inputs.
* Format of the string is:
* "[(PIN1|PIN2),(old PIN),(new PIN)]"
*/
split = g_strsplit (str, ",", -1);
if (qmicli_read_uim_pin_id_from_string (split[0], &pin_id) &&
qmicli_read_non_empty_string (split[1], "old PIN", &old_pin) &&
qmicli_read_non_empty_string (split[2], "new PIN", &new_pin)) {
GError *error = NULL;
GArray *placeholder_aid;
placeholder_aid = g_array_new (FALSE, FALSE, sizeof (guint8));
input = qmi_message_uim_change_pin_input_new ();
if (!qmi_message_uim_change_pin_input_set_info (
input,
pin_id,
old_pin,
new_pin,
&error) ||
!qmi_message_uim_change_pin_input_set_session (
input,
QMI_UIM_SESSION_TYPE_CARD_SLOT_1,
placeholder_aid, /* ignored */
&error)) {
g_printerr ("error: couldn't create input data bundle: '%s'\n",
error->message);
g_error_free (error);
qmi_message_uim_change_pin_input_unref (input);
input = NULL;
}
g_array_unref (placeholder_aid);
}
g_strfreev (split);
return input;
}
static void
change_pin_ready (QmiClientUim *client,
GAsyncResult *res)
{
QmiMessageUimChangePinOutput *output;
GError *error = NULL;
output = qmi_client_uim_change_pin_finish (client, res, &error);
if (!output) {
g_printerr ("error: operation failed: %s\n", error->message);
g_error_free (error);
operation_shutdown (FALSE);
return;
}
if (!qmi_message_uim_change_pin_output_get_result (output, &error)) {
guint8 verify_retries_left;
guint8 unblock_retries_left;
g_printerr ("error: couldn't change PIN: %s\n", error->message);
g_error_free (error);
if (qmi_message_uim_change_pin_output_get_retries_remaining (
output,
&verify_retries_left,
&unblock_retries_left,
NULL)) {
g_printerr ("[%s] Retries left:\n"
"\tVerify: %u\n"
"\tUnblock: %u\n",
qmi_device_get_path_display (ctx->device),
verify_retries_left,
unblock_retries_left);
}
qmi_message_uim_change_pin_output_unref (output);
operation_shutdown (FALSE);
return;
}
g_print ("[%s] PIN changed successfully\n",
qmi_device_get_path_display (ctx->device));
qmi_message_uim_change_pin_output_unref (output);
operation_shutdown (TRUE);
}
#endif /* HAVE_QMI_MESSAGE_UIM_CHANGE_PIN */
#if defined HAVE_QMI_MESSAGE_UIM_GET_SUPPORTED_MESSAGES
static void
get_supported_messages_ready (QmiClientUim *client,
GAsyncResult *res)
{
QmiMessageUimGetSupportedMessagesOutput *output;
GError *error = NULL;
GArray *bytearray = NULL;
gchar *str;
output = qmi_client_uim_get_supported_messages_finish (client, res, &error);
if (!output) {
g_printerr ("error: operation failed: %s\n", error->message);
g_error_free (error);
operation_shutdown (FALSE);
return;
}
if (!qmi_message_uim_get_supported_messages_output_get_result (output, &error)) {
g_printerr ("error: couldn't get supported UIM messages: %s\n", error->message);
g_error_free (error);
qmi_message_uim_get_supported_messages_output_unref (output);
operation_shutdown (FALSE);
return;
}
g_print ("[%s] Successfully got supported UIM messages:\n",
qmi_device_get_path_display (ctx->device));
qmi_message_uim_get_supported_messages_output_get_list (output, &bytearray, NULL);
str = qmicli_get_supported_messages_list (bytearray ? (const guint8 *)bytearray->data : NULL,
bytearray ? bytearray->len : 0);
g_print ("%s", str);
g_free (str);
qmi_message_uim_get_supported_messages_output_unref (output);
operation_shutdown (TRUE);
}
#endif /* HAVE_QMI_MESSAGE_UIM_GET_SUPPORTED_MESSAGES */
#if defined HAVE_QMI_MESSAGE_UIM_POWER_ON_SIM
static QmiMessageUimPowerOnSimInput *
power_on_sim_input_create (const gchar *slot_str)
{
QmiMessageUimPowerOnSimInput *input;
guint slot;
GError *error = NULL;
input = qmi_message_uim_power_on_sim_input_new ();
if (!qmicli_read_uint_from_string (slot_str, &slot) || (slot > G_MAXUINT8)) {
g_printerr ("error: invalid slot number\n");
return NULL;
}
if (!qmi_message_uim_power_on_sim_input_set_slot (input, slot, &error)) {
g_printerr ("error: could not create SIM power on input: %s\n", error->message);
g_error_free (error);
qmi_message_uim_power_on_sim_input_unref (input);
input = NULL;
}
return input;
}
static void
power_on_sim_ready (QmiClientUim *client,
GAsyncResult *res)
{
QmiMessageUimPowerOnSimOutput *output;
GError *error = NULL;
output = qmi_client_uim_power_on_sim_finish (client, res, &error);
if (!output) {
g_printerr ("error: operation failed: %s\n", error->message);
g_error_free (error);
operation_shutdown (FALSE);
return;
}
if (!qmi_message_uim_power_on_sim_output_get_result (output, &error)) {
g_printerr ("error: could not power on SIM: %s\n", error->message);
g_error_free (error);
qmi_message_uim_power_on_sim_output_unref (output);
operation_shutdown (FALSE);
return;
}
g_print ("[%s] Successfully performed SIM power on\n",
qmi_device_get_path_display (ctx->device));
qmi_message_uim_power_on_sim_output_unref (output);
operation_shutdown (TRUE);
}
#endif /* HAVE_QMI_MESSAGE_UIM_POWER_ON_SIM */
#if defined HAVE_QMI_MESSAGE_UIM_POWER_OFF_SIM
static QmiMessageUimPowerOffSimInput *
power_off_sim_input_create (const gchar *slot_str)
{
QmiMessageUimPowerOffSimInput *input;
guint slot;
GError *error = NULL;
input = qmi_message_uim_power_off_sim_input_new ();
if (!qmicli_read_uint_from_string (slot_str, &slot) || (slot > G_MAXUINT8)) {
g_printerr ("error: invalid slot number\n");
return NULL;
}
if (!qmi_message_uim_power_off_sim_input_set_slot (input, slot, &error)) {
g_printerr ("error: could not create SIM power off input: %s\n", error->message);
g_error_free (error);
qmi_message_uim_power_off_sim_input_unref (input);
input = NULL;
}
return input;
}
static void
power_off_sim_ready (QmiClientUim *client,
GAsyncResult *res)
{
QmiMessageUimPowerOffSimOutput *output;
GError *error = NULL;
output = qmi_client_uim_power_off_sim_finish (client, res, &error);
if (!output) {
g_printerr ("error: operation failed: %s\n", error->message);
g_error_free (error);
operation_shutdown (FALSE);
return;
}
if (!qmi_message_uim_power_off_sim_output_get_result (output, &error)) {
g_printerr ("error: could not power off SIM: %s\n", error->message);
g_error_free (error);
qmi_message_uim_power_off_sim_output_unref (output);
operation_shutdown (FALSE);
return;
}
g_print ("[%s] Successfully performed SIM power off\n",
qmi_device_get_path_display (ctx->device));
qmi_message_uim_power_off_sim_output_unref (output);
operation_shutdown (TRUE);
}
#endif /* HAVE_QMI_MESSAGE_UIM_POWER_OFF_SIM */
#if defined HAVE_QMI_MESSAGE_UIM_CHANGE_PROVISIONING_SESSION
typedef struct {
QmiUimSessionType session_type;
gboolean session_type_set;
gboolean activate;
gboolean activate_set;
guint slot;
GArray *aid;
} SetChangeProvisioningSessionProperties;
static gboolean
set_change_provisioning_session_properties_handle (const gchar *key,
const gchar *value,
GError **error,
gpointer user_data)
{
SetChangeProvisioningSessionProperties *props = (SetChangeProvisioningSessionProperties *) user_data;
if (!value || !value[0]) {
g_set_error (error, QMI_CORE_ERROR, QMI_CORE_ERROR_FAILED,
"key '%s' requires a value", key);
return FALSE;
}
if (g_ascii_strcasecmp (key, "session-type") == 0) {
if (!qmicli_read_uim_session_type_from_string (value, &props->session_type)) {
g_set_error (error, QMI_CORE_ERROR, QMI_CORE_ERROR_FAILED,
"invalid session type value: %s (not a valid enum)", value);
return FALSE;
}
props->session_type_set = TRUE;
return TRUE;
}
if (g_ascii_strcasecmp (key, "activate") == 0) {
if (!qmicli_read_yes_no_from_string (value, &props->activate)) {
g_set_error (error, QMI_CORE_ERROR, QMI_CORE_ERROR_FAILED,
"invalid activate value: %s (not a boolean)", value);
return FALSE;
}
props->activate_set = TRUE;
return TRUE;
}
if (g_ascii_strcasecmp (key, "slot") == 0) {
if (!qmicli_read_uint_from_string (value, &props->slot)) {
g_set_error (error, QMI_CORE_ERROR, QMI_CORE_ERROR_FAILED,
"invalid slot value: %s (not a number)", value);
return FALSE;
}
if (props->slot > G_MAXUINT8) {
g_set_error (error, QMI_CORE_ERROR, QMI_CORE_ERROR_FAILED,
"invalid slot value: %s (out of range)", value);
return FALSE;
}
return TRUE;
}
if (g_ascii_strcasecmp (key, "aid") == 0) {
if (!qmicli_read_raw_data_from_string (value, &props->aid)) {
g_set_error (error, QMI_CORE_ERROR, QMI_CORE_ERROR_FAILED,
"invalid aid value: %s (not an hex string)", value);
return FALSE;
}
return TRUE;
}
g_set_error (error, QMI_CORE_ERROR, QMI_CORE_ERROR_FAILED,
"Unrecognized option '%s'", key);
return FALSE;
}
static QmiMessageUimChangeProvisioningSessionInput *
change_provisioning_session_input_create (const gchar *str)
{
QmiMessageUimChangeProvisioningSessionInput *input;
GError *error = NULL;
SetChangeProvisioningSessionProperties props = { 0 };
input = qmi_message_uim_change_provisioning_session_input_new ();
if (!qmicli_parse_key_value_string (str,
&error,
set_change_provisioning_session_properties_handle,
&props)) {
g_printerr ("error: could not parse input string '%s': %s\n", str, error->message);
g_error_free (error);
g_clear_pointer (&input, qmi_message_uim_change_provisioning_session_input_unref);
goto out;
}
if (!props.session_type_set || !props.activate_set) {
g_printerr ("error: mandatory fields 'session-type' and 'activate' not given\n");
g_clear_pointer (&input, qmi_message_uim_change_provisioning_session_input_unref);
goto out;
}
qmi_message_uim_change_provisioning_session_input_set_session_change (
input,
props.session_type,
props.activate,
NULL);
if (props.slot || props.aid) {
GArray *aid = NULL;
aid = props.aid ? g_array_ref (props.aid) : g_array_new (FALSE, FALSE, sizeof (guint8));
qmi_message_uim_change_provisioning_session_input_set_application_information (
input,
props.slot,
aid,
NULL);
g_array_unref (aid);
}
out:
return input;
}
static void
change_provisioning_session_ready (QmiClientUim *client,
GAsyncResult *res)
{
QmiMessageUimChangeProvisioningSessionOutput *output;
GError *error = NULL;
output = qmi_client_uim_change_provisioning_session_finish (client, res, &error);
if (!output) {
g_printerr ("error: operation failed: %s\n", error->message);
g_error_free (error);
operation_shutdown (FALSE);
return;
}
if (!qmi_message_uim_change_provisioning_session_output_get_result (output, &error)) {
g_printerr ("error: could not power off SIM: %s\n", error->message);
g_error_free (error);
qmi_message_uim_change_provisioning_session_output_unref (output);
operation_shutdown (FALSE);
return;
}
g_print ("[%s] Successfully changed provisioning session\n",
qmi_device_get_path_display (ctx->device));
qmi_message_uim_change_provisioning_session_output_unref (output);
operation_shutdown (TRUE);
}
#endif /* HAVE_QMI_MESSAGE_UIM_CHANGE_PROVISIONING_SESSION */
#if (defined HAVE_QMI_MESSAGE_UIM_GET_SLOT_STATUS || \
defined HAVE_QMI_INDICATION_UIM_SLOT_STATUS)
static const gchar bcd_chars[] = "0123456789\0\0\0\0\0\0";
static gchar *
decode_iccid (const gchar *bcd, gsize bcd_len)
{
GString *str;
gsize i;
if (!bcd)
return NULL;
str = g_string_sized_new (bcd_len * 2 + 1);
for (i = 0; i < bcd_len; i++) {
str = g_string_append_c (str, bcd_chars[bcd[i] & 0xF]);
str = g_string_append_c (str, bcd_chars[(bcd[i] >> 4) & 0xF]);
}
return g_string_free (str, FALSE);
}
#define EID_LENGTH 16
static gchar *
decode_eid (const gchar *eid, gsize eid_len)
{
GString *str;
gsize i;
if (!eid)
return NULL;
if (eid_len != EID_LENGTH)
return NULL;
str = g_string_sized_new (eid_len * 2 + 1);
for (i = 0; i < eid_len; i++) {
str = g_string_append_c (str, bcd_chars[(eid[i] >> 4) & 0xF]);
str = g_string_append_c (str, bcd_chars[eid[i] & 0xF]);
}
return g_string_free (str, FALSE);
}
static void
print_slot_status (GArray *physical_slots,
GArray *ext_information,
GArray *slot_eids)
{
guint i;
if (ext_information && physical_slots->len != ext_information->len) {
g_print ("Malformed extended information data");
ext_information = NULL;
}
if (slot_eids && physical_slots->len != slot_eids->len) {
g_print ("Malformed slot EID data");
slot_eids = NULL;
}
for (i = 0; i < physical_slots->len; i++) {
QmiPhysicalSlotStatusSlot *slot_status;
QmiPhysicalSlotInformationSlot *slot_info = NULL;
GArray *slot_eid = NULL;
g_autofree gchar *iccid = NULL;
g_autofree gchar *eid = NULL;
slot_status = &g_array_index (physical_slots, QmiPhysicalSlotStatusSlot, i);
g_print (" Physical slot %u:\n", i + 1);
g_print (" Card status: %s\n",
qmi_uim_physical_card_state_get_string (slot_status->physical_card_status));
g_print (" Slot status: %s\n",
qmi_uim_slot_state_get_string (slot_status->physical_slot_status));
if (slot_status->physical_slot_status == QMI_UIM_SLOT_STATE_ACTIVE)
g_print (" Logical slot: %u\n", slot_status->logical_slot);
if (slot_status->physical_card_status != QMI_UIM_PHYSICAL_CARD_STATE_PRESENT)
continue;
if (slot_status->iccid->len)
iccid = decode_iccid (slot_status->iccid->data, slot_status->iccid->len);
g_print (" ICCID: %s\n", VALIDATE_UNKNOWN (iccid));
/* Extended information, if available */
if (!ext_information)
continue;
slot_info = &g_array_index (ext_information, QmiPhysicalSlotInformationSlot, i);
g_print (" Protocol: %s\n",
qmi_uim_card_protocol_get_string (slot_info->card_protocol));
g_print (" Num apps: %u\n", slot_info->valid_applications);
g_print (" Is eUICC: %s\n", slot_info->is_euicc ? "yes" : "no");
/* EID info, if available and this is an eUICC */
if (!slot_info->is_euicc || !slot_eids)
continue;
slot_eid = g_array_index (slot_eids, GArray *, i);
if (slot_eid->len)
eid = decode_eid (slot_eid->data, slot_eid->len);
g_print (" EID: %s\n", VALIDATE_UNKNOWN (eid));
}
}
#endif
#if defined HAVE_QMI_MESSAGE_UIM_GET_SLOT_STATUS
static void
get_slot_status_ready (QmiClientUim *client,
GAsyncResult *res)
{
QmiMessageUimGetSlotStatusOutput *output;
GArray *physical_slots;
GArray *ext_information = NULL;
GArray *slot_eids = NULL;
GError *error = NULL;
output = qmi_client_uim_get_slot_status_finish (client, res, &error);
if (!output) {
g_printerr ("error: operation failed: %s\n", error->message);
g_error_free (error);
operation_shutdown (FALSE);
return;
}
if (!qmi_message_uim_get_slot_status_output_get_result (output, &error)) {
g_printerr ("error: could not get slots status: %s\n", error->message);
g_error_free (error);
qmi_message_uim_get_slot_status_output_unref (output);
operation_shutdown (FALSE);
return;
}
g_print ("[%s] Successfully got slots status\n",
qmi_device_get_path_display (ctx->device));
if (!qmi_message_uim_get_slot_status_output_get_physical_slot_status (
output, &physical_slots, &error)) {
g_printerr ("error: could not parse slots status response: %s\n", error->message);
g_error_free (error);
qmi_message_uim_get_slot_status_output_unref (output);
operation_shutdown (FALSE);
return;
}
/* Both of these are recoverable, just print less information per slot */
qmi_message_uim_get_slot_status_output_get_physical_slot_information (output, &ext_information, NULL);
qmi_message_uim_get_slot_status_output_get_slot_eid_information (output, &slot_eids, NULL);
g_print ("[%s] %u physical slots found:\n",
qmi_device_get_path_display (ctx->device), physical_slots->len);
print_slot_status (physical_slots, ext_information, slot_eids);
qmi_message_uim_get_slot_status_output_unref (output);
operation_shutdown (TRUE);
}
#endif /* HAVE_QMI_MESSAGE_UIM_GET_SLOT_STATUS */
#if defined HAVE_QMI_MESSAGE_UIM_SWITCH_SLOT && defined HAVE_QMI_MESSAGE_UIM_GET_SLOT_STATUS
static void
switch_slot_ready (QmiClientUim *client,
GAsyncResult *res)
{
QmiMessageUimSwitchSlotOutput *output;
GError *error = NULL;
output = qmi_client_uim_switch_slot_finish (client, res, &error);
if (!output) {
g_printerr ("error: operation failed: %s\n", error->message);
g_error_free (error);
operation_shutdown (FALSE);
return;
}
if (!qmi_message_uim_switch_slot_output_get_result (output, &error)) {
g_printerr ("error: couldn't switch slots: %s\n", error->message);
g_error_free (error);
qmi_message_uim_switch_slot_output_unref (output);
operation_shutdown (FALSE);
return;
}
g_print ("[%s] Successfully switched slots\n",
qmi_device_get_path_display (ctx->device));
qmi_message_uim_switch_slot_output_unref (output);
operation_shutdown (TRUE);
}
static QmiMessageUimSwitchSlotInput *
switch_slot_input_create (guint logical_slot,
guint physical_slot)
{
QmiMessageUimSwitchSlotInput *input;
GError *error = NULL;
input = qmi_message_uim_switch_slot_input_new ();
if (!qmi_message_uim_switch_slot_input_set_logical_slot (input, logical_slot, &error) ||
!qmi_message_uim_switch_slot_input_set_physical_slot (input, physical_slot, &error)) {
g_printerr ("error: could not create switch slot input: %s\n", error->message);
g_error_free (error);
g_clear_pointer (&input, qmi_message_uim_switch_slot_input_unref);
}
return input;
}
static void
get_active_logical_slot_ready (QmiClientUim *client,
GAsyncResult *res,
gpointer user_data)
{
QmiMessageUimGetSlotStatusOutput *output;
QmiMessageUimSwitchSlotInput *input;
GArray *physical_slots;
guint physical_slot_id;
guint active_logical_slot_id = 0;
guint i;
GError *error = NULL;
physical_slot_id = GPOINTER_TO_UINT (user_data);
output = qmi_client_uim_get_slot_status_finish (client, res, &error);
if (!output) {
g_printerr ("error: operation failed: %s\n", error->message);
g_error_free (error);
operation_shutdown (FALSE);
return;
}
if (!qmi_message_uim_get_slot_status_output_get_result (output, &error)) {
g_printerr ("error: could not get slots status: %s\n", error->message);
g_error_free (error);
qmi_message_uim_get_slot_status_output_unref (output);
operation_shutdown (FALSE);
return;
}
if (!qmi_message_uim_get_slot_status_output_get_physical_slot_status (
output, &physical_slots, &error)) {
g_printerr ("error: could not parse slots status response: %s\n", error->message);
g_error_free (error);
qmi_message_uim_get_slot_status_output_unref (output);
operation_shutdown (FALSE);
return;
}
/* Ensure the physical slot is available. */
if (physical_slot_id > physical_slots->len) {
g_printerr ("error: physical slot %u is unavailable\n", physical_slot_id);
qmi_message_uim_get_slot_status_output_unref (output);
operation_shutdown (FALSE);
return;
}
/* Find active logical slot */
for (i = 0; i < physical_slots->len; i++) {
QmiPhysicalSlotStatusSlot *slot_status;
slot_status = &g_array_index (physical_slots, QmiPhysicalSlotStatusSlot, i);
if (slot_status->physical_slot_status == QMI_UIM_SLOT_STATE_ACTIVE) {
active_logical_slot_id = slot_status->logical_slot;
break;
}
}
qmi_message_uim_get_slot_status_output_unref (output);
if (active_logical_slot_id == 0) {
g_printerr ("error: no active logical slot\n");
operation_shutdown (FALSE);
return;
}
input = switch_slot_input_create (active_logical_slot_id, physical_slot_id);
qmi_client_uim_switch_slot (ctx->client,
input,
10,
ctx->cancellable,
(GAsyncReadyCallback)switch_slot_ready,
ctx);
qmi_message_uim_switch_slot_input_unref (input);
}
#endif /* HAVE_QMI_MESSAGE_UIM_SWITCH_SLOT && HAVE_QMI_MESSAGE_UIM_GET_SLOT_STATUS */
#if defined HAVE_QMI_INDICATION_UIM_SLOT_STATUS
static void
monitoring_cancelled (GCancellable *cancellable)
{
operation_shutdown (TRUE);
}
static void
slot_status_received (QmiClientUim *client,
QmiIndicationUimSlotStatusOutput *output)
{
GArray *physical_slots;
GArray *ext_information = NULL;
GArray *slot_eids = NULL;
GError *error = NULL;
g_print ("[%s] Received slot status indication:\n",
qmi_device_get_path_display (ctx->device));
if (!qmi_indication_uim_slot_status_output_get_physical_slot_status (
output, &physical_slots, &error)) {
g_printerr ("error: could not parse slots status: %s\n", error->message);
g_error_free (error);
operation_shutdown (FALSE);
return;
}
/* Both of these are recoverable, just print less information per slot */
qmi_indication_uim_slot_status_output_get_physical_slot_information (output, &ext_information, NULL);
qmi_indication_uim_slot_status_output_get_slot_eid_information (output, &slot_eids, NULL);
print_slot_status (physical_slots, ext_information, slot_eids);
}
static void
register_physical_slot_status_events_ready (QmiClientUim *client,
GAsyncResult *res)
{
QmiMessageUimRegisterEventsOutput *output;
GError *error = NULL;
output = qmi_client_uim_register_events_finish (client, res, &error);
if (!output) {
g_printerr ("error: operation failed: %s\n", error->message);
g_error_free (error);
operation_shutdown (FALSE);
return;
}
if (!qmi_message_uim_register_events_output_get_result (output, &error)) {
g_printerr ("error: could not register slot status change events: %s\n", error->message);
qmi_message_uim_register_events_output_unref (output);
g_error_free (error);
operation_shutdown (FALSE);
return;
}
g_debug ("Registered physical slot status change events...");
ctx->slot_status_indication_id =
g_signal_connect (ctx->client,
"slot-status",
G_CALLBACK (slot_status_received),
NULL);
/* User can use Ctrl+C to cancel the monitoring at any time */
g_cancellable_connect (ctx->cancellable,
G_CALLBACK (monitoring_cancelled),
NULL,
NULL);
}
static void
register_physical_slot_status_events (void)
{
QmiMessageUimRegisterEventsInput *re_input;
re_input = qmi_message_uim_register_events_input_new ();
qmi_message_uim_register_events_input_set_event_registration_mask (
re_input, QMI_UIM_EVENT_REGISTRATION_FLAG_PHYSICAL_SLOT_STATUS, NULL);
qmi_client_uim_register_events (
ctx->client,
re_input,
10,
ctx->cancellable,
(GAsyncReadyCallback) register_physical_slot_status_events_ready,
NULL);
qmi_message_uim_register_events_input_unref (re_input);
}
#endif /* HAVE_QMI_INDICATION_UIM_SLOT_STATUS */
#if defined HAVE_QMI_MESSAGE_UIM_RESET
static void
reset_ready (QmiClientUim *client,
GAsyncResult *res)
{
QmiMessageUimResetOutput *output;
GError *error = NULL;
output = qmi_client_uim_reset_finish (client, res, &error);
if (!output) {
g_printerr ("error: operation failed: %s\n", error->message);
g_error_free (error);
operation_shutdown (FALSE);
return;
}
if (!qmi_message_uim_reset_output_get_result (output, &error)) {
g_printerr ("error: couldn't reset the UIM service: %s\n", error->message);
g_error_free (error);
qmi_message_uim_reset_output_unref (output);
operation_shutdown (FALSE);
return;
}
g_print ("[%s] Successfully performed UIM service reset\n",
qmi_device_get_path_display (ctx->device));
qmi_message_uim_reset_output_unref (output);
operation_shutdown (TRUE);
}
#endif
static gboolean
noop_cb (gpointer unused)
{
operation_shutdown (TRUE);
return FALSE;
}
#if defined HAVE_QMI_MESSAGE_UIM_GET_CARD_STATUS
static void
get_card_status_ready (QmiClientUim *client,
GAsyncResult *res)
{
QmiMessageUimGetCardStatusOutput *output;
GError *error = NULL;
guint16 index_gw_primary;
guint16 index_1x_primary;
guint16 index_gw_secondary;
guint16 index_1x_secondary;
GArray *cards;
guint i;
output = qmi_client_uim_get_card_status_finish (client, res, &error);
if (!output) {
g_printerr ("error: operation failed: %s\n", error->message);
g_error_free (error);
operation_shutdown (FALSE);
return;
}
if (!qmi_message_uim_get_card_status_output_get_result (output, &error)) {
g_printerr ("error: couldn't get card status: %s\n", error->message);
g_error_free (error);
qmi_message_uim_get_card_status_output_unref (output);
operation_shutdown (FALSE);
return;
}
g_print ("[%s] Successfully got card status\n",
qmi_device_get_path_display (ctx->device));
qmi_message_uim_get_card_status_output_get_card_status (
output,
&index_gw_primary,
&index_1x_primary,
&index_gw_secondary,
&index_1x_secondary,
&cards,
NULL);
g_print ("Provisioning applications:\n");
if (index_gw_primary == 0xFFFF)
g_print ("\tPrimary GW: session doesn't exist\n");
else
g_print ("\tPrimary GW: slot '%u', application '%u'\n",
((index_gw_primary & 0xFF00) >> 8) + 1,
((index_gw_primary & 0x00FF)) + 1);
if (index_1x_primary == 0xFFFF)
g_print ("\tPrimary 1X: session doesn't exist\n");
else
g_print ("\tPrimary 1X: slot '%u', application '%u'\n",
((index_1x_primary & 0xFF00) >> 8) + 1,
((index_1x_primary & 0x00FF)) + 1);
if (index_gw_secondary == 0xFFFF)
g_print ("\tSecondary GW: session doesn't exist\n");
else
g_print ("\tSecondary GW: slot '%u', application '%u'\n",
((index_gw_secondary & 0xFF00) >> 8) + 1,
((index_gw_secondary & 0x00FF)) + 1);
if (index_1x_secondary == 0xFFFF)
g_print ("\tSecondary 1X: session doesn't exist\n");
else
g_print ("\tSecondary 1X: slot '%u', application '%u'\n",
((index_1x_secondary & 0xFF00) >> 8) + 1,
((index_1x_secondary & 0x00FF)) + 1);
for (i = 0; i < cards->len; i++) {
QmiMessageUimGetCardStatusOutputCardStatusCardsElement *card;
guint j;
card = &g_array_index (cards, QmiMessageUimGetCardStatusOutputCardStatusCardsElement, i);
g_print ("Slot [%u]:\n", i + 1);
if (card->card_state != QMI_UIM_CARD_STATE_ERROR)
g_print ("\tCard state: '%s'\n",
qmi_uim_card_state_get_string (card->card_state));
else
g_print ("\tCard state: '%s: %s (%u)'\n",
qmi_uim_card_state_get_string (card->card_state),
VALIDATE_UNKNOWN (qmi_uim_card_error_get_string (card->error_code)),
card->error_code);
g_print ("\tUPIN state: '%s'\n"
"\t\tUPIN retries: '%u'\n"
"\t\tUPUK retries: '%u'\n",
qmi_uim_pin_state_get_string (card->upin_state),
card->upin_retries,
card->upuk_retries);
for (j = 0; j < card->applications->len; j++) {
QmiMessageUimGetCardStatusOutputCardStatusCardsElementApplicationsElement *app;
gchar *str;
app = &g_array_index (card->applications, QmiMessageUimGetCardStatusOutputCardStatusCardsElementApplicationsElement, j);
str = qmicli_get_raw_data_printable (app->application_identifier_value, 80, "");
g_print ("\tApplication [%u]:\n"
"\t\tApplication type: '%s (%u)'\n"
"\t\tApplication state: '%s'\n"
"\t\tApplication ID:\n"
"\t\t\t%s",
j + 1,
VALIDATE_UNKNOWN (qmi_uim_card_application_type_get_string (app->type)), app->type,
qmi_uim_card_application_state_get_string (app->state),
str);
if (app->personalization_state == QMI_UIM_CARD_APPLICATION_PERSONALIZATION_STATE_CODE_REQUIRED ||
app->personalization_state == QMI_UIM_CARD_APPLICATION_PERSONALIZATION_STATE_PUK_CODE_REQUIRED)
g_print ("\t\tPersonalization state: '%s' (feature: %s)\n"
"\t\t\tDisable retries: '%u'\n"
"\t\t\tUnblock retries: '%u'\n",
qmi_uim_card_application_personalization_state_get_string (app->personalization_state),
qmi_uim_card_application_personalization_feature_get_string (app->personalization_feature),
app->personalization_retries,
app->personalization_unblock_retries);
else
g_print ("\t\tPersonalization state: '%s'\n",
qmi_uim_card_application_personalization_state_get_string (app->personalization_state));
g_print ("\t\tUPIN replaces PIN1: '%s'\n",
app->upin_replaces_pin1 ? "yes" : "no");
g_print ("\t\tPIN1 state: '%s'\n"
"\t\t\tPIN1 retries: '%u'\n"
"\t\t\tPUK1 retries: '%u'\n"
"\t\tPIN2 state: '%s'\n"
"\t\t\tPIN2 retries: '%u'\n"
"\t\t\tPUK2 retries: '%u'\n",
qmi_uim_pin_state_get_string (app->pin1_state),
app->pin1_retries,
app->puk1_retries,
qmi_uim_pin_state_get_string (app->pin2_state),
app->pin2_retries,
app->puk2_retries);
g_free (str);
}
}
qmi_message_uim_get_card_status_output_unref (output);
operation_shutdown (TRUE);
}
#endif /* defined HAVE_QMI_MESSAGE_UIM_GET_CARD_STATUS */
#if defined HAVE_QMI_MESSAGE_UIM_READ_TRANSPARENT || \
defined HAVE_QMI_MESSAGE_UIM_READ_RECORD || \
defined HAVE_QMI_MESSAGE_UIM_GET_FILE_ATTRIBUTES
static gboolean
get_sim_file_id_and_path_with_separator (const gchar *file_path_str,
guint16 *file_id,
GArray **file_path,
const gchar *separator)
{
guint i;
gchar **split;
split = g_strsplit (file_path_str, separator, -1);
if (!split) {
g_printerr ("error: invalid file path given: '%s'\n", file_path_str);
return FALSE;
}
*file_path = g_array_sized_new (FALSE,
FALSE,
sizeof (guint8),
g_strv_length (split) - 1);
*file_id = 0;
for (i = 0; split[i]; i++) {
gulong path_item;
path_item = (strtoul (split[i], NULL, 16)) & 0xFFFF;
/* If there are more fields, this is part of the path; otherwise it's
* the file id */
if (split[i + 1]) {
guint8 val;
val = path_item & 0xFF;
g_array_append_val (*file_path, val);
val = (path_item >> 8) & 0xFF;
g_array_append_val (*file_path, val);
} else {
*file_id = path_item;
}
}
g_strfreev (split);
if (*file_id == 0) {
g_array_unref (*file_path);
g_printerr ("error: invalid file path given: '%s'\n", file_path_str);
return FALSE;
}
return TRUE;
}
#endif /* HAVE_QMI_MESSAGE_UIM_READ_TRANSPARENT
* HAVE_QMI_MESSAGE_UIM_READ_RECORD
* HAVE_QMI_MESSAGE_UIM_GET_FILE_ATTRIBUTES */
#if defined HAVE_QMI_MESSAGE_UIM_READ_TRANSPARENT || \
defined HAVE_QMI_MESSAGE_UIM_GET_FILE_ATTRIBUTES || \
defined HAVE_QMI_MESSAGE_UIM_REFRESH_REGISTER
static gboolean
get_sim_file_id_and_path (const gchar *file_path_str,
guint16 *file_id,
GArray **file_path)
{
return get_sim_file_id_and_path_with_separator (file_path_str, file_id, file_path, ",");
}
#endif /* HAVE_QMI_MESSAGE_UIM_READ_TRANSPARENT
* HAVE_QMI_MESSAGE_UIM_GET_FILE_ATTRIBUTES
* HAVE_QMI_MESSAGE_UIM_REFRESH_REGISTER */
#if defined HAVE_QMI_MESSAGE_UIM_READ_TRANSPARENT
static void
read_transparent_ready (QmiClientUim *client,
GAsyncResult *res)
{
QmiMessageUimReadTransparentOutput *output;
GError *error = NULL;
guint8 sw1 = 0;
guint8 sw2 = 0;
GArray *read_result = NULL;
output = qmi_client_uim_read_transparent_finish (client, res, &error);
if (!output) {
g_printerr ("error: operation failed: %s\n", error->message);
g_error_free (error);
operation_shutdown (FALSE);
return;
}
if (!qmi_message_uim_read_transparent_output_get_result (output, &error)) {
g_printerr ("error: couldn't read transparent file from the UIM: %s\n", error->message);
g_error_free (error);
/* Card result */
if (qmi_message_uim_read_transparent_output_get_card_result (
output,
&sw1,
&sw2,
NULL)) {
g_print ("Card result:\n"
"\tSW1: '0x%02x'\n"
"\tSW2: '0x%02x'\n",
sw1, sw2);
}
qmi_message_uim_read_transparent_output_unref (output);
operation_shutdown (FALSE);
return;
}
g_print ("[%s] Successfully read information from the UIM:\n",
qmi_device_get_path_display (ctx->device));
/* Card result */
if (qmi_message_uim_read_transparent_output_get_card_result (
output,
&sw1,
&sw2,
NULL)) {
g_print ("Card result:\n"
"\tSW1: '0x%02x'\n"
"\tSW2: '0x%02x'\n",
sw1, sw2);
}
/* Read result */
if (qmi_message_uim_read_transparent_output_get_read_result (
output,
&read_result,
NULL)) {
gchar *str;
str = qmicli_get_raw_data_printable (read_result, 80, "\t");
g_print ("Read result:\n"
"%s\n",
str);
g_free (str);
}
qmi_message_uim_read_transparent_output_unref (output);
operation_shutdown (TRUE);
}
static QmiMessageUimReadTransparentInput *
read_transparent_build_input (const gchar *file_path_str)
{
QmiMessageUimReadTransparentInput *input;
guint16 file_id = 0;
GArray *file_path = NULL;
GArray *placeholder_aid;
if (!get_sim_file_id_and_path (file_path_str, &file_id, &file_path))
return NULL;
placeholder_aid = g_array_new (FALSE, FALSE, sizeof (guint8));
input = qmi_message_uim_read_transparent_input_new ();
qmi_message_uim_read_transparent_input_set_session (
input,
QMI_UIM_SESSION_TYPE_PRIMARY_GW_PROVISIONING,
placeholder_aid, /* ignored */
NULL);
qmi_message_uim_read_transparent_input_set_file (
input,
file_id,
file_path,
NULL);
qmi_message_uim_read_transparent_input_set_read_information (input, 0, 0, NULL);
g_array_unref (file_path);
g_array_unref (placeholder_aid);
return input;
}
#endif /* HAVE_QMI_MESSAGE_UIM_READ_TRANSPARENT */
#if defined HAVE_QMI_MESSAGE_UIM_READ_RECORD
static void
read_record_ready (QmiClientUim *client,
GAsyncResult *res)
{
QmiMessageUimReadRecordOutput *output;
GError *error = NULL;
guint8 sw1 = 0;
guint8 sw2 = 0;
GArray *read_result = NULL;
output = qmi_client_uim_read_record_finish (client, res, &error);
if (!output) {
g_printerr ("error: operation failed: %s\n", error->message);
g_error_free (error);
operation_shutdown (FALSE);
return;
}
if (!qmi_message_uim_read_record_output_get_result (output, &error)) {
g_printerr ("error: couldn't read record file from the UIM: %s\n", error->message);
g_error_free (error);
/* Card result */
if (qmi_message_uim_read_record_output_get_card_result (
output,
&sw1,
&sw2,
NULL)) {
g_print ("Card result:\n"
"\tSW1: '0x%02x'\n"
"\tSW2: '0x%02x'\n",
sw1, sw2);
}
qmi_message_uim_read_record_output_unref (output);
operation_shutdown (FALSE);
return;
}
g_print ("[%s] Successfully read information from the UIM:\n",
qmi_device_get_path_display (ctx->device));
/* Card result */
if (qmi_message_uim_read_record_output_get_card_result (
output,
&sw1,
&sw2,
NULL)) {
g_print ("Card result:\n"
"\tSW1: '0x%02x'\n"
"\tSW2: '0x%02x'\n",
sw1, sw2);
}
/* Read result */
if (qmi_message_uim_read_record_output_get_read_result (
output,
&read_result,
NULL)) {
gchar *str;
str = qmicli_get_raw_data_printable (read_result, 80, "\t");
g_print ("Read result:\n"
"%s\n",
str);
g_free (str);
}
qmi_message_uim_read_record_output_unref (output);
operation_shutdown (TRUE);
}
typedef struct {
char *file;
guint16 record_number;
guint16 record_length;
} SetReadRecordProperties;
static gboolean
set_read_record_properties_handle (const gchar *key,
const gchar *value,
GError **error,
gpointer user_data)
{
SetReadRecordProperties *props = (SetReadRecordProperties *) user_data;
if (!value || !value[0]) {
g_set_error (error,
QMI_CORE_ERROR,
QMI_CORE_ERROR_FAILED,
"key '%s' requires a value",
key);
return FALSE;
}
if (g_ascii_strcasecmp (key, "file") == 0) {
props->file = g_strdup (value);
return TRUE;
}
if (g_ascii_strcasecmp (key, "record-number") == 0) {
guint aux;
if (!qmicli_read_uint_from_string (value, &aux) || (aux > G_MAXUINT16)) {
g_set_error (error, QMI_CORE_ERROR, QMI_CORE_ERROR_FAILED,
"failed reading key 'record-number' as 16bit value");
return FALSE;
}
props->record_number = (guint16) aux;
return TRUE;
}
if (g_ascii_strcasecmp (key, "record-length") == 0) {
guint aux;
if (!qmicli_read_uint_from_string (value, &aux) || (aux > G_MAXUINT16)) {
g_set_error (error, QMI_CORE_ERROR, QMI_CORE_ERROR_FAILED,
"failed reading key 'record-length' as 16bit value");
return FALSE;
}
props->record_length = (guint16) aux;
return TRUE;
}
g_set_error (error,
QMI_CORE_ERROR,
QMI_CORE_ERROR_FAILED,
"Unrecognized option '%s'",
key);
return FALSE;
}
static QmiMessageUimReadRecordInput *
read_record_input_create (const gchar *str)
{
GError *error = NULL;
QmiMessageUimReadRecordInput *input = NULL;
SetReadRecordProperties props = {
.file = NULL,
.record_number = 0,
.record_length = 0,
};
guint16 file_id = 0;
GArray *file_path = NULL;
GArray *placeholder_aid;
if (!qmicli_parse_key_value_string (str,
&error,
set_read_record_properties_handle,
&props)) {
g_printerr ("error: could not parse input string '%s': %s\n",
str,
error->message);
g_error_free (error);
goto out;
}
if (!get_sim_file_id_and_path_with_separator (props.file, &file_id, &file_path, "-"))
goto out;
placeholder_aid = g_array_new (FALSE, FALSE, sizeof (guint8));
input = qmi_message_uim_read_record_input_new ();
qmi_message_uim_read_record_input_set_session (
input,
QMI_UIM_SESSION_TYPE_PRIMARY_GW_PROVISIONING,
placeholder_aid, /* ignored */
NULL);
qmi_message_uim_read_record_input_set_file (
input,
file_id,
file_path,
NULL);
qmi_message_uim_read_record_input_set_record (
input,
props.record_number,
props.record_length,
NULL);
g_array_unref (placeholder_aid);
out:
free (props.file);
g_array_unref (file_path);
return input;
}
#endif /* HAVE_QMI_MESSAGE_UIM_READ_RECORD */
#if defined HAVE_QMI_MESSAGE_UIM_GET_FILE_ATTRIBUTES
static void
get_file_attributes_ready (QmiClientUim *client,
GAsyncResult *res,
gchar *file_name)
{
QmiMessageUimGetFileAttributesOutput *output;
GError *error = NULL;
guint8 sw1 = 0;
guint8 sw2 = 0;
guint16 file_size;
guint16 file_id;
QmiUimFileType file_type;
guint16 record_size;
guint16 record_count;
QmiUimSecurityAttributeLogic read_security_attributes_logic;
QmiUimSecurityAttribute read_security_attributes;
QmiUimSecurityAttributeLogic write_security_attributes_logic;
QmiUimSecurityAttribute write_security_attributes;
QmiUimSecurityAttributeLogic increase_security_attributes_logic;
QmiUimSecurityAttribute increase_security_attributes;
QmiUimSecurityAttributeLogic deactivate_security_attributes_logic;
QmiUimSecurityAttribute deactivate_security_attributes;
QmiUimSecurityAttributeLogic activate_security_attributes_logic;
QmiUimSecurityAttribute activate_security_attributes;
GArray *raw = NULL;
output = qmi_client_uim_get_file_attributes_finish (client, res, &error);
if (!output) {
g_printerr ("error: operation failed: %s\n", error->message);
g_error_free (error);
operation_shutdown (FALSE);
g_free (file_name);
return;
}
if (!qmi_message_uim_get_file_attributes_output_get_result (output, &error)) {
g_printerr ("error: couldn't get '%s' file attributes from the UIM: %s\n",
file_name,
error->message);
g_error_free (error);
/* Card result */
if (qmi_message_uim_get_file_attributes_output_get_card_result (
output,
&sw1,
&sw2,
NULL)) {
g_print ("Card result:\n"
"\tSW1: '0x%02x'\n"
"\tSW2: '0x%02x'\n",
sw1, sw2);
}
qmi_message_uim_get_file_attributes_output_unref (output);
operation_shutdown (FALSE);
g_free (file_name);
return;
}
g_print ("[%s] Successfully got file '%s' attributes from the UIM:\n",
file_name,
qmi_device_get_path_display (ctx->device));
/* Card result */
if (qmi_message_uim_get_file_attributes_output_get_card_result (
output,
&sw1,
&sw2,
NULL)) {
g_print ("Card result:\n"
"\tSW1: '0x%02x'\n"
"\tSW2: '0x%02x'\n",
sw1, sw2);
}
/* File attributes */
if (qmi_message_uim_get_file_attributes_output_get_file_attributes (
output,
&file_size,
&file_id,
&file_type,
&record_size,
&record_count,
&read_security_attributes_logic,
&read_security_attributes,
&write_security_attributes_logic,
&write_security_attributes,
&increase_security_attributes_logic,
&increase_security_attributes,
&deactivate_security_attributes_logic,
&deactivate_security_attributes,
&activate_security_attributes_logic,
&activate_security_attributes,
&raw,
NULL)) {
g_autofree gchar *read_security_attributes_str = NULL;
g_autofree gchar *write_security_attributes_str = NULL;
g_autofree gchar *increase_security_attributes_str = NULL;
g_autofree gchar *deactivate_security_attributes_str = NULL;
g_autofree gchar *activate_security_attributes_str = NULL;
g_autofree gchar *raw_str = NULL;
g_print ("File attributes:\n");
g_print ("\tFile size: %u\n", (guint)file_size);
g_print ("\tFile ID: %u\n", (guint)file_id);
g_print ("\tFile type: %s\n", qmi_uim_file_type_get_string (file_type));
g_print ("\tRecord size: %u\n", (guint)record_size);
g_print ("\tRecord count: %u\n", (guint)record_count);
read_security_attributes_str = qmi_uim_security_attribute_build_string_from_mask (read_security_attributes);
g_print ("\tRead security attributes: (%s) %s\n",
qmi_uim_security_attribute_logic_get_string (read_security_attributes_logic),
VALIDATE_MASK_NONE (read_security_attributes_str));
write_security_attributes_str = qmi_uim_security_attribute_build_string_from_mask (write_security_attributes);
g_print ("\tWrite security attributes: (%s) %s\n",
qmi_uim_security_attribute_logic_get_string (write_security_attributes_logic),
VALIDATE_MASK_NONE (write_security_attributes_str));
increase_security_attributes_str = qmi_uim_security_attribute_build_string_from_mask (increase_security_attributes);
g_print ("\tIncrease security attributes: (%s) %s\n",
qmi_uim_security_attribute_logic_get_string (increase_security_attributes_logic),
VALIDATE_MASK_NONE (increase_security_attributes_str));
deactivate_security_attributes_str = qmi_uim_security_attribute_build_string_from_mask (deactivate_security_attributes);
g_print ("\tDeactivate security attributes: (%s) %s\n",
qmi_uim_security_attribute_logic_get_string (deactivate_security_attributes_logic),
VALIDATE_MASK_NONE (deactivate_security_attributes_str));
activate_security_attributes_str = qmi_uim_security_attribute_build_string_from_mask (activate_security_attributes);
g_print ("\tActivate security attributes: (%s) %s\n",
qmi_uim_security_attribute_logic_get_string (activate_security_attributes_logic),
VALIDATE_MASK_NONE (activate_security_attributes_str));
raw_str = qmicli_get_raw_data_printable (raw, 80, "\t");
g_print ("\tRaw: %s\n", raw_str);
}
qmi_message_uim_get_file_attributes_output_unref (output);
operation_shutdown (TRUE);
}
static QmiMessageUimGetFileAttributesInput *
get_file_attributes_build_input (const gchar *file_path_str)
{
QmiMessageUimGetFileAttributesInput *input;
guint16 file_id = 0;
GArray *file_path = NULL;
GArray *placeholder_aid;
if (!get_sim_file_id_and_path (file_path_str, &file_id, &file_path))
return NULL;
placeholder_aid = g_array_new (FALSE, FALSE, sizeof (guint8));
input = qmi_message_uim_get_file_attributes_input_new ();
qmi_message_uim_get_file_attributes_input_set_session (
input,
QMI_UIM_SESSION_TYPE_PRIMARY_GW_PROVISIONING,
placeholder_aid, /* ignored */
NULL);
qmi_message_uim_get_file_attributes_input_set_file (
input,
file_id,
file_path,
NULL);
g_array_unref (placeholder_aid);
g_array_unref (file_path);
return input;
}
#endif /* HAVE_QMI_MESSAGE_UIM_GET_FILE_ATTRIBUTES */
#if defined HAVE_QMI_MESSAGE_UIM_REFRESH_REGISTER || \
defined HAVE_QMI_MESSAGE_UIM_REFRESH_REGISTER_ALL
static void
refresh_complete_ready (QmiClientUim *client,
GAsyncResult *res)
{
QmiMessageUimRefreshCompleteOutput *output;
GError *error = NULL;
output = qmi_client_uim_refresh_complete_finish (client, res, &error);
if (!output) {
g_printerr ("error: refresh complete failed: %s\n", error->message);
g_error_free (error);
return;
}
/* Ignore error, just log it as warning. In case we send complete message when
* the modem does not expect it, we could get an error that is harmless.
*/
if (!qmi_message_uim_refresh_complete_output_get_result (output, &error)) {
g_warning ("refresh complete failed: %s\n", error->message);
g_error_free (error);
} else
g_debug ("Refresh complete OK.");
qmi_message_uim_refresh_complete_output_unref (output);
}
static void
refresh_complete (QmiClientUim *client,
gboolean success)
{
QmiMessageUimRefreshCompleteInput *refresh_complete_input;
GArray *placeholder_aid;
placeholder_aid = g_array_new (FALSE, FALSE, sizeof (guint8));
refresh_complete_input = qmi_message_uim_refresh_complete_input_new ();
qmi_message_uim_refresh_complete_input_set_session (
refresh_complete_input,
QMI_UIM_SESSION_TYPE_CARD_SLOT_1,
placeholder_aid, /* ignored */
NULL);
qmi_message_uim_refresh_complete_input_set_info (
refresh_complete_input,
success,
NULL);
qmi_client_uim_refresh_complete (
ctx->client,
refresh_complete_input,
10,
ctx->cancellable,
(GAsyncReadyCallback) refresh_complete_ready,
NULL);
qmi_message_uim_refresh_complete_input_unref (refresh_complete_input);
g_array_unref (placeholder_aid);
}
static void
refresh_received (QmiClientUim *client,
QmiIndicationUimRefreshOutput *output)
{
QmiUimRefreshStage stage;
QmiUimRefreshMode mode;
GArray *files = NULL;
GError *error = NULL;
guint i, j;
g_print ("[%s] Received refresh indication:\n",
qmi_device_get_path_display (ctx->device));
if (!qmi_indication_uim_refresh_output_get_event (
output, &stage, &mode, NULL, NULL, &files, &error)) {
g_printerr ("error: could not parse refresh ind: %s\n", error->message);
g_error_free (error);
operation_shutdown (FALSE);
return;
}
g_print (" Refresh stage: %s\n",
qmi_uim_refresh_stage_get_string (stage));
g_print (" Refresh mode: %s\n",
qmi_uim_refresh_mode_get_string (mode));
g_print (" Files:\n");
if (files && files->len > 0)
for (i = 0; i < files->len; i++) {
QmiIndicationUimRefreshOutputEventFilesElement *file;
GArray *path;
file = &g_array_index (files, QmiIndicationUimRefreshOutputEventFilesElement, i);
g_print (" 0x%x; path:",
file->file_id);
path = file->path;
if (path && path->len >= 2)
for (j = 0; j < path->len / 2; j++) {
guint16 path_component;
path_component = g_array_index (path, guint8, j * 2) |
((guint16)g_array_index (path, guint8, j * 2 + 1) << 8);
g_print (" 0x%x", path_component);
}
else
g_print (" <none>");
g_print ("\n");
}
else
g_print (" <none>\n");
/* Send refresh complete message only in start stage and only if the
* mode is something other than reset.
*/
if (stage == QMI_UIM_REFRESH_STAGE_START && mode != QMI_UIM_REFRESH_MODE_RESET)
refresh_complete (client, TRUE);
}
static void
refresh_cancelled (GCancellable *cancellable)
{
operation_shutdown (TRUE);
}
#endif /* HAVE_QMI_MESSAGE_UIM_REFRESH_REGISTER
* HAVE_QMI_MESSAGE_UIM_REFRESH_REGISTER_ALL */
#if defined HAVE_QMI_MESSAGE_UIM_REFRESH_REGISTER
static void
register_refresh_events_ready (QmiClientUim *client,
GAsyncResult *res)
{
QmiMessageUimRefreshRegisterOutput *output;
GError *error = NULL;
output = qmi_client_uim_refresh_register_finish (client, res, &error);
if (!output) {
g_printerr ("error: operation failed: %s\n", error->message);
g_error_free (error);
operation_shutdown (FALSE);
return;
}
if (!qmi_message_uim_refresh_register_output_get_result (output, &error)) {
g_printerr ("error: could not register refresh file events: %s\n", error->message);
qmi_message_uim_refresh_register_output_unref (output);
g_error_free (error);
operation_shutdown (FALSE);
return;
}
g_debug ("Registered refresh file events...");
ctx->refresh_indication_id =
g_signal_connect (ctx->client,
"refresh",
G_CALLBACK (refresh_received),
NULL);
/* User can use Ctrl+C to cancel the monitoring at any time */
g_cancellable_connect (ctx->cancellable,
G_CALLBACK (refresh_cancelled),
NULL,
NULL);
qmi_message_uim_refresh_register_output_unref (output);
}
static void
register_refresh_events (gchar **file_path_array)
{
QmiMessageUimRefreshRegisterInput *refresh_input;
GArray *placeholder_aid;
GArray *file_list;
QmiMessageUimRefreshRegisterInputInfoFilesElement file_element;
guint i;
file_list = g_array_new (FALSE, FALSE, sizeof (QmiMessageUimRefreshRegisterInputInfoFilesElement));
while (*file_path_array) {
memset (&file_element, 0, sizeof (file_element));
if (!get_sim_file_id_and_path (*file_path_array, &file_element.file_id, &file_element.path))
goto out;
g_array_append_val (file_list, file_element);
file_path_array++;
}
placeholder_aid = g_array_new (FALSE, FALSE, sizeof (guint8));
refresh_input = qmi_message_uim_refresh_register_input_new ();
qmi_message_uim_refresh_register_input_set_session (
refresh_input,
QMI_UIM_SESSION_TYPE_CARD_SLOT_1,
placeholder_aid, /* ignored */
NULL);
qmi_message_uim_refresh_register_input_set_info (
refresh_input,
TRUE,
FALSE,
file_list,
NULL);
qmi_client_uim_refresh_register (
ctx->client,
refresh_input,
10,
ctx->cancellable,
(GAsyncReadyCallback) register_refresh_events_ready,
NULL);
qmi_message_uim_refresh_register_input_unref (refresh_input);
g_array_unref (placeholder_aid);
out:
for (i = 0; i < file_list->len; i++) {
QmiMessageUimRefreshRegisterInputInfoFilesElement *file;
file = &g_array_index (file_list, QmiMessageUimRefreshRegisterInputInfoFilesElement, i);
g_array_unref (file->path);
}
g_array_unref (file_list);
}
#endif /* HAVE_QMI_MESSAGE_UIM_REFRESH_REGISTER */
#if defined HAVE_QMI_MESSAGE_UIM_REFRESH_REGISTER_ALL
static void
register_refresh_all_events_ready (QmiClientUim *client,
GAsyncResult *res)
{
QmiMessageUimRefreshRegisterAllOutput *output;
GError *error = NULL;
output = qmi_client_uim_refresh_register_all_finish (client, res, &error);
if (!output) {
g_printerr ("error: operation failed: %s\n", error->message);
g_error_free (error);
operation_shutdown (FALSE);
return;
}
if (!qmi_message_uim_refresh_register_all_output_get_result (output, &error)) {
g_printerr ("error: could not register refresh file events: %s\n", error->message);
qmi_message_uim_refresh_register_all_output_unref (output);
g_error_free (error);
operation_shutdown (FALSE);
return;
}
g_debug ("Registered refresh all file events...");
ctx->refresh_indication_id =
g_signal_connect (ctx->client,
"refresh",
G_CALLBACK (refresh_received),
NULL);
/* User can use Ctrl+C to cancel the monitoring at any time */
g_cancellable_connect (ctx->cancellable,
G_CALLBACK (refresh_cancelled),
NULL,
NULL);
qmi_message_uim_refresh_register_all_output_unref (output);
}
static void
register_refresh_all_events (void)
{
QmiMessageUimRefreshRegisterAllInput *refresh_all_input;
GArray *placeholder_aid;
refresh_all_input = qmi_message_uim_refresh_register_all_input_new ();
placeholder_aid = g_array_new (FALSE, FALSE, sizeof (guint8));
qmi_message_uim_refresh_register_all_input_set_session (
refresh_all_input,
QMI_UIM_SESSION_TYPE_CARD_SLOT_1,
placeholder_aid, /* ignored */
NULL);
qmi_message_uim_refresh_register_all_input_set_info (
refresh_all_input,
TRUE,
NULL);
qmi_client_uim_refresh_register_all (
ctx->client,
refresh_all_input,
10,
ctx->cancellable,
(GAsyncReadyCallback) register_refresh_all_events_ready,
NULL);
qmi_message_uim_refresh_register_all_input_unref (refresh_all_input);
g_array_unref (placeholder_aid);
}
#endif /* HAVE_QMI_MESSAGE_UIM_REFRESH_REGISTER_ALL */
#if defined HAVE_QMI_MESSAGE_UIM_GET_CONFIGURATION
static QmiMessageUimGetConfigurationInput *
get_configuration_input_create (void)
{
QmiMessageUimGetConfigurationInput *input;
input = qmi_message_uim_get_configuration_input_new ();
qmi_message_uim_get_configuration_input_set_configuration_mask (
input,
QMI_UIM_CONFIGURATION_PERSONALIZATION_STATUS,
NULL);
return input;
}
static void
get_configuration_ready (QmiClientUim *client,
GAsyncResult *res)
{
g_autoptr(QmiMessageUimGetConfigurationOutput) output = NULL;
g_autoptr(GError) error = NULL;
GArray *elements = NULL;
GArray *other_slots = NULL;
output = qmi_client_uim_get_configuration_finish (client, res, &error);
if (!output) {
g_printerr ("error: operation failed: %s\n", error->message);
operation_shutdown (FALSE);
return;
}
if (!qmi_message_uim_get_configuration_output_get_result (output, &error)) {
g_printerr ("error: get configuration failed: %s\n", error->message);
operation_shutdown (FALSE);
return;
}
g_print ("Configuration successfully retrieved\n");
/* Other slots TLV contains info for slots > 1 */
qmi_message_uim_get_configuration_output_get_personalization_status_other_slots (output, &other_slots, NULL);
if (qmi_message_uim_get_configuration_output_get_personalization_status (output, &elements, NULL)) {
if (elements->len == 0)
g_print ("Personalization features%s: all disabled\n",
other_slots ? " in slot 1" : "");
else {
QmiMessageUimGetConfigurationOutputPersonalizationStatusElement *element;
guint i;
g_print ("Personalization features%s:\n",
other_slots ? " in slot 1" : "");
for (i = 0; i < elements->len; i++) {
element = &g_array_index (elements,
QmiMessageUimGetConfigurationOutputPersonalizationStatusElement,
i);
g_print ("\tPersonalization: %s\n"
"\t\tVerify left: %u\n"
"\t\tUnblock left: %u\n",
qmi_uim_card_application_personalization_feature_get_string (element->feature),
element->verify_left,
element->unblock_left);
}
}
}
if (other_slots) {
if (other_slots->len == 0)
g_print ("Personalization features in other slots: all disabled\n");
else {
guint slot;
for (slot = 0; slot < other_slots->len; slot++) {
QmiMessageUimGetConfigurationOutputPersonalizationStatusElement *element;
guint i;
elements = g_array_index (other_slots, GArray *, slot);
if (!elements)
continue;
g_print ("Personalization features in slot %u:\n", slot + 2);
for (i = 0; i < elements->len; i++) {
element = &g_array_index (elements,
QmiMessageUimGetConfigurationOutputPersonalizationStatusElement,
i);
g_print ("\tPersonalization: %s\n"
"\t\tVerify left: %u\n"
"\t\tUnblock left: %u\n",
qmi_uim_card_application_personalization_feature_get_string (element->feature),
element->verify_left,
element->unblock_left);
}
}
}
}
operation_shutdown (TRUE);
}
#endif /* HAVE_QMI_MESSAGE_UIM_GET_CONFIGURATION */
#if defined HAVE_QMI_MESSAGE_UIM_DEPERSONALIZATION
static QmiMessageUimDepersonalizationInput *
depersonalization_input_create (const gchar *str)
{
g_auto(GStrv) split = NULL;
QmiMessageUimDepersonalizationInput *input = NULL;
QmiUimCardApplicationPersonalizationFeature feature;
QmiUimDepersonalizationOperation operation;
const gchar *control_key;
guint slot = 0;
/* Prepare inputs.
* Format of the string is:
* "[(feature),(operation),(control key)[,(slot number)]]"
*/
split = g_strsplit (str, ",", -1);
if (!split[0] || !qmicli_read_uim_card_application_personalization_feature_from_string (split[0], &feature)) {
g_printerr ("error: invalid personalization feature\n");
return NULL;
}
if (!split[1] || !qmicli_read_uim_depersonalization_operation_from_string (split[1], &operation)) {
g_printerr ("error: invalid depersonalization operation\n");
return NULL;
}
if (!split[2]) {
g_printerr ("error: missing control key\n");
return NULL;
}
control_key = split[2];
if (g_strv_length (split) > 3) {
if (!qmicli_read_uint_from_string (split[3], &slot) || (slot < 1) || (slot > 5)) {
g_printerr ("error: invalid slot number\n");
return NULL;
}
}
input = qmi_message_uim_depersonalization_input_new ();
qmi_message_uim_depersonalization_input_set_info (input, feature, operation, control_key, NULL);
/* skip setting slot if not given by the user */
if (slot > 0)
qmi_message_uim_depersonalization_input_set_slot (input, slot, NULL);
return input;
}
static void
depersonalization_ready (QmiClientUim *client,
GAsyncResult *res)
{
g_autoptr(QmiMessageUimDepersonalizationOutput) output = NULL;
g_autoptr(GError) error = NULL;
guint8 unblock_left;
guint8 verify_left;
output = qmi_client_uim_depersonalization_finish (client, res, &error);
if (!output) {
g_printerr ("error: operation failed: %s\n", error->message);
operation_shutdown (FALSE);
return;
}
if (qmi_message_uim_depersonalization_output_get_result (output, &error)) {
g_print ("Modem was unlocked successfully\n");
operation_shutdown (TRUE);
return;
}
g_printerr ("error: depersonalization failed: %s\n", error->message);
if (qmi_message_uim_depersonalization_output_get_retries_remaining (
output,
&verify_left,
&unblock_left,
NULL)) {
g_printerr ("Retries left:\n"
"\tVerify: %u\n"
"\tUnblock: %u\n",
verify_left,
unblock_left);
}
operation_shutdown (FALSE);
}
#endif /* HAVE_QMI_MESSAGE_UIM_DEPERSONALIZATION */
void
qmicli_uim_run (QmiDevice *device,
QmiClientUim *client,
GCancellable *cancellable)
{
/* Initialize context */
ctx = g_slice_new0 (Context);
ctx->device = g_object_ref (device);
ctx->client = g_object_ref (client);
ctx->cancellable = g_object_ref (cancellable);
#if defined HAVE_QMI_MESSAGE_UIM_SET_PIN_PROTECTION
if (set_pin_protection_str) {
QmiMessageUimSetPinProtectionInput *input;
g_debug ("Asynchronously setting PIN protection...");
input = set_pin_protection_input_create (set_pin_protection_str);
if (!input) {
operation_shutdown (FALSE);
return;
}
qmi_client_uim_set_pin_protection (ctx->client,
input,
10,
ctx->cancellable,
(GAsyncReadyCallback)set_pin_protection_ready,
NULL);
qmi_message_uim_set_pin_protection_input_unref (input);
return;
}
#endif
#if defined HAVE_QMI_MESSAGE_UIM_VERIFY_PIN
if (verify_pin_str) {
QmiMessageUimVerifyPinInput *input;
g_debug ("Asynchronously verifying PIN...");
input = verify_pin_input_create (verify_pin_str);
if (!input) {
operation_shutdown (FALSE);
return;
}
qmi_client_uim_verify_pin (ctx->client,
input,
10,
ctx->cancellable,
(GAsyncReadyCallback)verify_pin_ready,
NULL);
qmi_message_uim_verify_pin_input_unref (input);
return;
}
#endif
#if defined HAVE_QMI_MESSAGE_UIM_UNBLOCK_PIN
if (unblock_pin_str) {
QmiMessageUimUnblockPinInput *input;
g_debug ("Asynchronously unblocking PIN...");
input = unblock_pin_input_create (unblock_pin_str);
if (!input) {
operation_shutdown (FALSE);
return;
}
qmi_client_uim_unblock_pin (ctx->client,
input,
10,
ctx->cancellable,
(GAsyncReadyCallback)unblock_pin_ready,
NULL);
qmi_message_uim_unblock_pin_input_unref (input);
return;
}
#endif
#if defined HAVE_QMI_MESSAGE_UIM_CHANGE_PIN
if (change_pin_str) {
QmiMessageUimChangePinInput *input;
g_debug ("Asynchronously changing PIN...");
input = change_pin_input_create (change_pin_str);
if (!input) {
operation_shutdown (FALSE);
return;
}
qmi_client_uim_change_pin (ctx->client,
input,
10,
ctx->cancellable,
(GAsyncReadyCallback)change_pin_ready,
NULL);
qmi_message_uim_change_pin_input_unref (input);
return;
}
#endif
#if defined HAVE_QMI_MESSAGE_UIM_READ_TRANSPARENT
if (read_transparent_str) {
QmiMessageUimReadTransparentInput *input;
input = read_transparent_build_input (read_transparent_str);
if (!input) {
operation_shutdown (FALSE);
return;
}
g_debug ("Asynchronously reading transparent file at '%s'...",
read_transparent_str);
qmi_client_uim_read_transparent (ctx->client,
input,
10,
ctx->cancellable,
(GAsyncReadyCallback)read_transparent_ready,
NULL);
qmi_message_uim_read_transparent_input_unref (input);
return;
}
#endif
#if defined HAVE_QMI_MESSAGE_UIM_READ_RECORD
if (read_record_str) {
QmiMessageUimReadRecordInput *input;
input = read_record_input_create (read_record_str);
if (!input) {
operation_shutdown (FALSE);
return;
}
g_debug ("Asynchronously reading record file at '%s'...",
read_record_str);
qmi_client_uim_read_record (ctx->client,
input,
10,
ctx->cancellable,
(GAsyncReadyCallback)read_record_ready,
NULL);
qmi_message_uim_read_record_input_unref (input);
return;
}
#endif
#if defined HAVE_QMI_MESSAGE_UIM_GET_FILE_ATTRIBUTES
if (get_file_attributes_str) {
QmiMessageUimGetFileAttributesInput *input;
input = get_file_attributes_build_input (get_file_attributes_str);
if (!input) {
operation_shutdown (FALSE);
return;
}
g_debug ("Asynchronously reading attributes of file '%s'...",
get_file_attributes_str);
qmi_client_uim_get_file_attributes (ctx->client,
input,
10,
ctx->cancellable,
(GAsyncReadyCallback)get_file_attributes_ready,
NULL);
qmi_message_uim_get_file_attributes_input_unref (input);
return;
}
#endif
#if defined HAVE_QMI_MESSAGE_UIM_GET_CARD_STATUS
if (get_card_status_flag) {
g_debug ("Asynchronously getting card status...");
qmi_client_uim_get_card_status (ctx->client,
NULL,
10,
ctx->cancellable,
(GAsyncReadyCallback)get_card_status_ready,
NULL);
return;
}
#endif
#if defined HAVE_QMI_MESSAGE_UIM_GET_SUPPORTED_MESSAGES
if (get_supported_messages_flag) {
g_debug ("Asynchronously getting supported UIM messages...");
qmi_client_uim_get_supported_messages (ctx->client,
NULL,
10,
ctx->cancellable,
(GAsyncReadyCallback)get_supported_messages_ready,
NULL);
return;
}
#endif
#if defined HAVE_QMI_MESSAGE_UIM_POWER_ON_SIM
if (sim_power_on_str) {
QmiMessageUimPowerOnSimInput *input;
g_debug ("Asynchronously power on SIM card");
input = power_on_sim_input_create (sim_power_on_str);
if (!input) {
operation_shutdown (FALSE);
return;
}
qmi_client_uim_power_on_sim (ctx->client,
input,
10,
ctx->cancellable,
(GAsyncReadyCallback)power_on_sim_ready,
NULL);
qmi_message_uim_power_on_sim_input_unref (input);
return;
}
#endif
#if defined HAVE_QMI_MESSAGE_UIM_POWER_OFF_SIM
if (sim_power_off_str) {
QmiMessageUimPowerOffSimInput *input;
g_debug ("Asynchronously power off SIM card");
input = power_off_sim_input_create (sim_power_off_str);
if (!input) {
operation_shutdown (FALSE);
return;
}
qmi_client_uim_power_off_sim (ctx->client,
input,
10,
ctx->cancellable,
(GAsyncReadyCallback)power_off_sim_ready,
NULL);
qmi_message_uim_power_off_sim_input_unref (input);
return;
}
#endif
#if defined HAVE_QMI_MESSAGE_UIM_CHANGE_PROVISIONING_SESSION
if (change_provisioning_session_str) {
QmiMessageUimChangeProvisioningSessionInput *input;
g_debug ("Asynchronously changing provisioning session");
input = change_provisioning_session_input_create (change_provisioning_session_str);
if (!input) {
operation_shutdown (FALSE);
return;
}
qmi_client_uim_change_provisioning_session (ctx->client,
input,
10,
ctx->cancellable,
(GAsyncReadyCallback)change_provisioning_session_ready,
NULL);
qmi_message_uim_change_provisioning_session_input_unref (input);
return;
}
#endif
#if defined HAVE_QMI_MESSAGE_UIM_GET_SLOT_STATUS
/* Request to get slot status? */
if (get_slot_status_flag) {
g_debug ("Asynchronously getting slot status...");
qmi_client_uim_get_slot_status (ctx->client,
NULL,
10,
ctx->cancellable,
(GAsyncReadyCallback)get_slot_status_ready,
NULL);
return;
}
#endif
#if defined HAVE_QMI_MESSAGE_UIM_SWITCH_SLOT && defined HAVE_QMI_MESSAGE_UIM_GET_SLOT_STATUS
/* Request to change active slot? */
if (switch_slot_str) {
guint physical_slot;
if (!qmicli_read_uint_from_string (switch_slot_str, &physical_slot) ||
(physical_slot < 1) || (physical_slot > G_MAXUINT8)) {
g_printerr ("error: invalid slot number\n");
return;
}
g_debug ("Asynchronously switching active slot");
qmi_client_uim_get_slot_status (ctx->client,
NULL,
10,
ctx->cancellable,
(GAsyncReadyCallback)get_active_logical_slot_ready,
GUINT_TO_POINTER (physical_slot));
return;
}
#endif
#if defined HAVE_QMI_INDICATION_UIM_SLOT_STATUS
/* Watch for slot status changes? */
if (monitor_slot_status_flag) {
g_debug ("Listening for slot status changes...");
register_physical_slot_status_events ();
return;
}
#endif
#if defined HAVE_QMI_MESSAGE_UIM_REFRESH_REGISTER
if (monitor_refresh_file_array) {
g_debug ("Listening for refresh events...");
register_refresh_events (monitor_refresh_file_array);
return;
}
#endif
#if defined HAVE_QMI_MESSAGE_UIM_REFRESH_REGISTER_ALL
if (monitor_refresh_all_flag) {
g_debug ("Listening for all refresh events...");
register_refresh_all_events ();
return;
}
#endif
#if defined HAVE_QMI_MESSAGE_UIM_RESET
/* Request to reset UIM service? */
if (reset_flag) {
g_debug ("Asynchronously resetting UIM service...");
qmi_client_uim_reset (ctx->client,
NULL,
10,
ctx->cancellable,
(GAsyncReadyCallback)reset_ready,
NULL);
return;
}
#endif
#if defined HAVE_QMI_MESSAGE_UIM_GET_CONFIGURATION
/* Request to get personalization status? */
if (get_configuration_flag) {
g_autoptr(QmiMessageUimGetConfigurationInput) input = NULL;
g_debug ("Asynchronously getting UIM configuration...");
input = get_configuration_input_create ();
if (!input) {
operation_shutdown (FALSE);
return;
}
qmi_client_uim_get_configuration (ctx->client,
input,
10,
ctx->cancellable,
(GAsyncReadyCallback)get_configuration_ready,
NULL);
return;
}
#endif
#if defined HAVE_QMI_MESSAGE_UIM_DEPERSONALIZATION
/* Request to depersonalize the modem? */
if (depersonalization_str) {
g_autoptr(QmiMessageUimDepersonalizationInput) input = NULL;
g_debug ("Asynchronously removing personalization...");
input = depersonalization_input_create (depersonalization_str);
if (!input) {
operation_shutdown (FALSE);
return;
}
qmi_client_uim_depersonalization (ctx->client,
input,
10,
ctx->cancellable,
(GAsyncReadyCallback)depersonalization_ready,
NULL);
return;
}
#endif
/* Just client allocate/release? */
if (noop_flag) {
g_idle_add (noop_cb, NULL);
return;
}
g_warn_if_reached ();
}
#endif /* HAVE_QMI_SERVICE_UIM */