mobileactivation: Add new functions required for drmHandshake / session mode device activation
diff --git a/include/libimobiledevice/mobileactivation.h b/include/libimobiledevice/mobileactivation.h
index bb977fe..bb1f3f4 100644
--- a/include/libimobiledevice/mobileactivation.h
+++ b/include/libimobiledevice/mobileactivation.h
@@ -3,7 +3,7 @@
  * @brief Handle device activation and deactivation.
  * \internal
  *
- * Copyright (c) 2016 Nikias Bassen, All Rights Reserved.
+ * Copyright (c) 2016-2017 Nikias Bassen, All Rights Reserved.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -104,6 +104,20 @@
 mobileactivation_error_t mobileactivation_get_activation_state(mobileactivation_client_t client, plist_t *state);
 
 /**
+ * Retrieves a session blob required for 'drmHandshake' via albert.apple.com.
+ *
+ * @param client The mobileactivation client
+ * @param blob Pointer to a plist_t variable that will be set to the
+ *     session blob created by the mobielactivation service. The
+ *     consumer is responsible for freeing the returned object using
+ *     plist_free().
+ *
+ * @return MOBILEACTIVATION_E_SUCCESS on success, or an MOBILEACTIVATION_E_*
+ *     error code otherwise.
+ */
+mobileactivation_error_t mobileactivation_create_activation_session_info(mobileactivation_client_t client, plist_t *blob);
+
+/**
  * Retrieves the activation info required for device activation.
  *
  * @param client The mobileactivation client
@@ -118,6 +132,24 @@
 mobileactivation_error_t mobileactivation_create_activation_info(mobileactivation_client_t client, plist_t *info);
 
 /**
+ * Retrieves the activation info required for device activation in 'session'
+ * mode. This function expects a handshake result retrieved from
+ * https://albert.apple.com/deviceservies/drmHandshake  with a blob
+ * provided by mobileactivation_create_activation_session_info().
+ *
+ * @param client The mobileactivation client
+ * @aram handshake_result The handshake result returned from drmHandshake
+ * @param info Pointer to a plist_t variable that will be set to the
+ *     activation info created by the mobileactivation service. The
+ *     consumer is responsible for freeing the returned object using
+ *     plist_free().
+ *
+ * @return MOBILEACTIVATION_E_SUCCESS on success, or an MOBILEACTIVATION_E_*
+ *     error code otherwise.
+ */
+mobileactivation_error_t mobileactivation_create_activation_info_with_session(mobileactivation_client_t client, plist_t handshake_result, plist_t *info);
+
+/**
  * Activates the device with the given activation record.
  * The activation record plist dictionary must be obtained using the
  * activation protocol requesting from Apple's https webservice.
@@ -131,6 +163,19 @@
 mobileactivation_error_t mobileactivation_activate(mobileactivation_client_t client, plist_t activation_record);
 
 /**
+ * Activates the device with the given activation record in 'session' mode.
+ * The activation record plist dictionary must be obtained using the
+ * activation protocol requesting from Apple's https webservice.
+ *
+ * @param client The mobileactivation client
+ * @param activation_record The activation record plist dictionary
+ *
+ * @return MOBILEACTIVATION_E_SUCCESS on success, or an MOBILEACTIVATION_E_*
+ *     error code otherwise.
+ */
+mobileactivation_error_t mobileactivation_activate_with_session(mobileactivation_client_t client, plist_t activation_record);
+
+/**
  * Deactivates the device.
  *
  * @param client The mobileactivation client
diff --git a/src/mobileactivation.c b/src/mobileactivation.c
index f14eb73..7ae35bb 100644
--- a/src/mobileactivation.c
+++ b/src/mobileactivation.c
@@ -2,7 +2,7 @@
  * mobileactivation.c
  * com.apple.mobileactivationd service implementation.
  *
- * Copyright (c) 2016 Nikias Bassen, All Rights Reserved.
+ * Copyright (c) 2016-2017 Nikias Bassen, All Rights Reserved.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -89,6 +89,17 @@
 	return MOBILEACTIVATION_E_SUCCESS;
 }
 
+static plist_t plist_data_from_plist(plist_t plist)
+{
+	plist_t result = NULL;
+	char *xml = NULL;
+	uint32_t xml_len = 0;
+	plist_to_xml(plist, &xml, &xml_len);
+	result = plist_new_data(xml, xml_len);
+	free(xml);
+	return result;
+}
+
 static mobileactivation_error_t mobileactivation_check_result(plist_t dict, const char *command)
 {
 	mobileactivation_error_t ret = MOBILEACTIVATION_E_UNKNOWN_ERROR;
@@ -104,8 +115,8 @@
 		char *errmsg = NULL;
 		plist_get_string_val(err_node, &errmsg);
 		debug_info("ERROR: %s: %s", command, errmsg);
-		free(errmsg);
 		ret = MOBILEACTIVATION_E_REQUEST_FAILED;
+		free(errmsg);
 	}
 	return ret;
 }
@@ -160,6 +171,26 @@
 	return ret;
 }
 
+LIBIMOBILEDEVICE_API mobileactivation_error_t mobileactivation_create_activation_session_info(mobileactivation_client_t client, plist_t *blob)
+{
+	if (!client || !blob)
+		return MOBILEACTIVATION_E_INVALID_ARG;
+
+	plist_t result = NULL;
+	mobileactivation_error_t ret = mobileactivation_send_command(client, "CreateTunnel1SessionInfoRequest", NULL, &result);
+	if (ret == MOBILEACTIVATION_E_SUCCESS) {
+		plist_t node = plist_dict_get_item(result, "Value");
+		if (!node) {
+			debug_info("ERROR: CreateTunnel1SessionInfoRequest command returned success but has no value in reply");
+			ret = MOBILEACTIVATION_E_UNKNOWN_ERROR;
+		} else {
+			*blob = plist_copy(node);
+		}
+	}
+
+	return ret;
+}
+
 LIBIMOBILEDEVICE_API mobileactivation_error_t mobileactivation_create_activation_info(mobileactivation_client_t client, plist_t *info)
 {
 	if (!client || !info)
@@ -179,6 +210,30 @@
 	plist_free(result);
 	result = NULL;
 
+	return ret;
+}
+
+LIBIMOBILEDEVICE_API mobileactivation_error_t mobileactivation_create_activation_info_with_session(mobileactivation_client_t client, plist_t handshake_response, plist_t *info)
+{
+	if (!client || !info)
+		return MOBILEACTIVATION_E_INVALID_ARG;
+
+	plist_t result = NULL;
+	plist_t data = plist_data_from_plist(handshake_response);
+	mobileactivation_error_t ret = mobileactivation_send_command(client, "CreateTunnel1ActivationInfoRequest", data, &result);
+	plist_free(data);
+	if (ret == MOBILEACTIVATION_E_SUCCESS) {
+		plist_t node = plist_dict_get_item(result, "Value");
+		if (!node) {
+			debug_info("ERROR: CreateTunnel1ActivationInfoRequest command returned success but has no value in reply");
+			ret = MOBILEACTIVATION_E_UNKNOWN_ERROR;
+		} else {
+			*info = plist_copy(node);
+		}
+	}
+	plist_free(result);
+	result = NULL;
+
 	return ret;	
 }
 
@@ -195,6 +250,22 @@
 	return ret;
 }
 
+LIBIMOBILEDEVICE_API mobileactivation_error_t mobileactivation_activate_with_session(mobileactivation_client_t client, plist_t activation_record)
+{
+	if (!client || !activation_record)
+		return MOBILEACTIVATION_E_INVALID_ARG;
+
+	plist_t result = NULL;
+	plist_t data = plist_data_from_plist(activation_record);
+	mobileactivation_error_t ret = mobileactivation_send_command(client, "HandleActivationInfoWithSessionRequest", data, &result);
+	plist_free(data);
+	plist_free(result);
+	result = NULL;
+
+	return ret;
+}
+
+
 LIBIMOBILEDEVICE_API mobileactivation_error_t mobileactivation_deactivate(mobileactivation_client_t client)
 {
 	if (!client)