#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <string.h>
#include <stdlib.h>
#include "device_link_service.h"
#include "property_list_service.h"
#include "common/debug.h"
static device_link_service_error_t device_link_error(property_list_service_error_t err)
{
switch (err) {
case PROPERTY_LIST_SERVICE_E_SUCCESS:
return DEVICE_LINK_SERVICE_E_SUCCESS;
case PROPERTY_LIST_SERVICE_E_INVALID_ARG:
return DEVICE_LINK_SERVICE_E_INVALID_ARG;
case PROPERTY_LIST_SERVICE_E_PLIST_ERROR:
return DEVICE_LINK_SERVICE_E_PLIST_ERROR;
case PROPERTY_LIST_SERVICE_E_MUX_ERROR:
return DEVICE_LINK_SERVICE_E_MUX_ERROR;
case PROPERTY_LIST_SERVICE_E_SSL_ERROR:
return DEVICE_LINK_SERVICE_E_SSL_ERROR;
case PROPERTY_LIST_SERVICE_E_RECEIVE_TIMEOUT:
return DEVICE_LINK_SERVICE_E_RECEIVE_TIMEOUT;
default:
break;
}
return DEVICE_LINK_SERVICE_E_UNKNOWN_ERROR;
}
static int device_link_service_get_message(plist_t dl_msg, char **message)
{
plist_t cmd = NULL;
char *cmd_str = NULL;
if ((plist_get_node_type(dl_msg) != PLIST_ARRAY) || (plist_array_get_size(dl_msg) < 1)) {
return 0;
}
cmd = plist_array_get_item(dl_msg, 0);
if (!cmd || (plist_get_node_type(cmd) != PLIST_STRING)) {
return 0;
}
plist_get_string_val(cmd, &cmd_str);
if (!cmd_str) {
return 0;
}
if ((strlen(cmd_str) < 9) || (strncmp(cmd_str, "DL", 2) != 0)) {
free(cmd_str);
return 0;
}
if (message)
*message = cmd_str;
return 1;
}
device_link_service_error_t device_link_service_client_new(idevice_t device, lockdownd_service_descriptor_t service, device_link_service_client_t *client)
{
if (!device || !service || service->port == 0 || !client || *client) {
return DEVICE_LINK_SERVICE_E_INVALID_ARG;
}
property_list_service_client_t plistclient = NULL;
device_link_service_error_t err = device_link_error(property_list_service_client_new(device, service, &plistclient));
if (err != DEVICE_LINK_SERVICE_E_SUCCESS) {
return err;
}
device_link_service_client_t client_loc = (device_link_service_client_t) malloc(sizeof(struct device_link_service_client_private));
client_loc->parent = plistclient;
*client = client_loc;
return DEVICE_LINK_SERVICE_E_SUCCESS;
}
device_link_service_error_t device_link_service_client_free(device_link_service_client_t client)
{
if (!client)
return DEVICE_LINK_SERVICE_E_INVALID_ARG;
device_link_service_error_t err = device_link_error(property_list_service_client_free(client->parent));
free(client);
return err;
}
device_link_service_error_t device_link_service_version_exchange(device_link_service_client_t client, uint64_t version_major, uint64_t version_minor)
{
if (!client)
return DEVICE_LINK_SERVICE_E_INVALID_ARG;
device_link_service_error_t err = DEVICE_LINK_SERVICE_E_UNKNOWN_ERROR;
plist_t array = NULL;
char *msg = NULL;
err = device_link_error(property_list_service_receive_plist(client->parent, &array));
if (err != DEVICE_LINK_SERVICE_E_SUCCESS) {
debug_info("Did not receive initial message from device!");
goto leave;
}
device_link_service_get_message(array, &msg);
if (!msg || strcmp(msg, "DLMessageVersionExchange") != 0) {
debug_info("Did not receive DLMessageVersionExchange from device!");
err = DEVICE_LINK_SERVICE_E_PLIST_ERROR;
goto leave;
}
free(msg);
msg = NULL;
if (plist_array_get_size(array) < 3) {
debug_info("DLMessageVersionExchange has unexpected format!");
err = DEVICE_LINK_SERVICE_E_PLIST_ERROR;
goto leave;
}
plist_t maj = plist_array_get_item(array, 1);
plist_t min = plist_array_get_item(array, 2);
uint64_t vmajor = 0;
uint64_t vminor = 0;
if (maj) {
plist_get_uint_val(maj, &vmajor);
}
if (min) {
plist_get_uint_val(min, &vminor);
}
if (vmajor > version_major) {
debug_info("Version mismatch: device=(%lld,%lld) > expected=(%lld,%lld)", vmajor, vminor, version_major, version_minor);
err = DEVICE_LINK_SERVICE_E_BAD_VERSION;
goto leave;
} else if ((vmajor == version_major) && (vminor > version_minor)) {
debug_info("WARNING: Version mismatch: device=(%lld,%lld) > expected=(%lld,%lld)", vmajor, vminor, version_major, version_minor);
err = DEVICE_LINK_SERVICE_E_BAD_VERSION;
goto leave;
}
plist_free(array);
array = plist_new_array();
plist_array_append_item(array, plist_new_string("DLMessageVersionExchange"));
plist_array_append_item(array, plist_new_string("DLVersionsOk"));
plist_array_append_item(array, plist_new_uint(version_major));
err = device_link_error(property_list_service_send_binary_plist(client->parent, array));
if (err != DEVICE_LINK_SERVICE_E_SUCCESS) {
debug_info("Error when sending DLVersionsOk");
goto leave;
}
plist_free(array);
array = NULL;
err = device_link_error(property_list_service_receive_plist(client->parent, &array));
if (err != DEVICE_LINK_SERVICE_E_SUCCESS) {
debug_info("Error when receiving DLMessageDeviceReady!");
goto leave;
}
device_link_service_get_message(array, &msg);
if (!msg || strcmp(msg, "DLMessageDeviceReady") != 0) {
debug_info("Did not get DLMessageDeviceReady!");
err = DEVICE_LINK_SERVICE_E_PLIST_ERROR;
goto leave;
}
err = DEVICE_LINK_SERVICE_E_SUCCESS;
leave:
if (msg) {
free(msg);
}
if (array) {
plist_free(array);
}
return err;
}
device_link_service_error_t device_link_service_disconnect(device_link_service_client_t client, const char *message)
{
if (!client)
return DEVICE_LINK_SERVICE_E_INVALID_ARG;
plist_t array = plist_new_array();
plist_array_append_item(array, plist_new_string("DLMessageDisconnect"));
if (message)
plist_array_append_item(array, plist_new_string(message));
else
plist_array_append_item(array, plist_new_string("___EmptyParameterString___"));
device_link_service_error_t err = device_link_error(property_list_service_send_binary_plist(client->parent, array));
plist_free(array);
return err;
}
device_link_service_error_t device_link_service_send_ping(device_link_service_client_t client, const char *message)
{
if (!client || !client->parent || !message)
return DEVICE_LINK_SERVICE_E_INVALID_ARG;
plist_t array = plist_new_array();
plist_array_append_item(array, plist_new_string("DLMessagePing"));
plist_array_append_item(array, plist_new_string(message));
device_link_service_error_t err = device_link_error(property_list_service_send_binary_plist(client->parent, array));
plist_free(array);
return err;
}
device_link_service_error_t device_link_service_send_process_message(device_link_service_client_t client, plist_t message)
{
if (!client || !client->parent || !message)
return DEVICE_LINK_SERVICE_E_INVALID_ARG;
if (plist_get_node_type(message) != PLIST_DICT)
return DEVICE_LINK_SERVICE_E_INVALID_ARG;
plist_t array = plist_new_array();
plist_array_append_item(array, plist_new_string("DLMessageProcessMessage"));
plist_array_append_item(array, plist_copy(message));
device_link_service_error_t err = device_link_error(property_list_service_send_binary_plist(client->parent, array));
plist_free(array);
return err;
}
device_link_service_error_t device_link_service_receive_message(device_link_service_client_t client, plist_t *msg_plist, char **dlmessage)
{
if (!client || !client->parent || !msg_plist)
return DEVICE_LINK_SERVICE_E_INVALID_ARG;
*msg_plist = NULL;
device_link_service_error_t err = device_link_error(property_list_service_receive_plist(client->parent, msg_plist));
if (err != DEVICE_LINK_SERVICE_E_SUCCESS) {
return err;
}
if (!device_link_service_get_message(*msg_plist, dlmessage)) {
debug_info("Did not receive a DL* message as expected!");
return DEVICE_LINK_SERVICE_E_PLIST_ERROR;
}
return DEVICE_LINK_SERVICE_E_SUCCESS;
}
device_link_service_error_t device_link_service_receive_process_message(device_link_service_client_t client, plist_t *message)
{
if (!client || !client->parent || !message)
return DEVICE_LINK_SERVICE_E_INVALID_ARG;
plist_t pmsg = NULL;
device_link_service_error_t err = device_link_error(property_list_service_receive_plist(client->parent, &pmsg));
if (err != DEVICE_LINK_SERVICE_E_SUCCESS) {
return err;
}
err = DEVICE_LINK_SERVICE_E_UNKNOWN_ERROR;
char *msg = NULL;
device_link_service_get_message(pmsg, &msg);
if (!msg || strcmp(msg, "DLMessageProcessMessage") != 0) {
debug_info("Did not receive DLMessageProcessMessage as expected!");
err = DEVICE_LINK_SERVICE_E_PLIST_ERROR;
goto leave;
}
if (plist_array_get_size(pmsg) != 2) {
debug_info("Malformed plist received for DLMessageProcessMessage");
err = DEVICE_LINK_SERVICE_E_PLIST_ERROR;
goto leave;
}
plist_t msg_loc = plist_array_get_item(pmsg, 1);
if (msg_loc) {
*message = plist_copy(msg_loc);
err = DEVICE_LINK_SERVICE_E_SUCCESS;
} else {
*message = NULL;
err = DEVICE_LINK_SERVICE_E_PLIST_ERROR;
}
leave:
if (msg)
free(msg);
if (pmsg)
plist_free(pmsg);
return err;
}
device_link_service_error_t device_link_service_send(device_link_service_client_t client, plist_t plist)
{
if (!client || !plist) {
return DEVICE_LINK_SERVICE_E_INVALID_ARG;
}
return device_link_error(property_list_service_send_binary_plist(client->parent, plist));
}
device_link_service_error_t device_link_service_receive(device_link_service_client_t client, plist_t *plist)
{
if (!client || !plist || (plist && *plist)) {
return DEVICE_LINK_SERVICE_E_INVALID_ARG;
}
return device_link_error(property_list_service_receive_plist(client->parent, plist));
}