From 1c851c273dece4d1a6efa5a6e093557bd0e862c1 Mon Sep 17 00:00:00 2001
From: boxingsquirrel
Date: Sat, 30 Apr 2011 17:31:01 -0400
Subject: Working with iOS versions up to 4.3.2; plus all currently released
 devices
---
 src/Makefile.am      |   12 +-
 src/asr.c            |    6 +-
 src/common.h         |   26 +
 src/idevicerestore.c |   70 ++-
 src/libirecovery.c   | 1293 ++++++++++++++++++++++++++++++++++++++++++++++++++
 src/libirecovery.h   |  232 +++++++++
 src/recovery.c       |   11 +-
 src/recovery.h       |    6 +-
 src/tss.c            |    7 +-
 9 files changed, 1645 insertions(+), 18 deletions(-)
 create mode 100644 src/libirecovery.c
 create mode 100644 src/libirecovery.h
diff --git a/src/Makefile.am b/src/Makefile.am
index a4c8fc3..831a222 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,5 +1,5 @@
-libirecovery_CFLAGS = -I/usr/local/include
-libirecovery_LIBS = -L/usr/local/lib -lirecovery -lusb-1.0
+#libirecovery_CFLAGS = -I/usr/local/include
+#libirecovery_LIBS = -L/usr/local/lib -lusb-1.0
 
 AM_CFLAGS =	                   \
 	$(GLOBAL_CFLAGS)           \
@@ -7,17 +7,17 @@ AM_CFLAGS =	                   \
 	$(libplist_CFLAGS)         \
 	$(libzip_CFLAGS)           \
 	$(libcurl_CFLAGS)          \
-	$(libirecovery_CFLAGS)
+	#$(libirecovery_CFLAGS)
 
 AM_LDFLAGS =\
 	$(libimobiledevice_LIBS)   \
 	$(libplist_LIBS)           \
 	$(libzip_LIBS)             \
 	$(libcurl_LIBS)            \
-	$(libirecovery_LIBS)
+	#$(libirecovery_LIBS)
 
 bin_PROGRAMS = idevicerestore
 
-idevicerestore_SOURCES = idevicerestore.c common.c tss.c img3.c ipsw.c normal.c dfu.c recovery.c restore.c asr.c
+idevicerestore_SOURCES = idevicerestore.c common.c tss.c img3.c ipsw.c normal.c dfu.c recovery.c restore.c asr.c libirecovery.c
 idevicerestore_CFLAGS = $(AM_CFLAGS)
-idevicerestore_LDFLAGS = $(AM_LDFLAGS)
+idevicerestore_LDFLAGS = $(AM_LDFLAGS) -lusb-1.0
diff --git a/src/asr.c b/src/asr.c
index f48170a..7b08f55 100644
--- a/src/asr.c
+++ b/src/asr.c
@@ -106,9 +106,11 @@ int asr_send(idevice_connection_t asr, plist_t* data) {
 	}
 
 	debug("Sent %d bytes:\n", size);
-	if (idevicerestore_debug)
+
+	// TODO: Actually figure out the problem with the commented out code below, instead of just ditching it...
+	/*if (idevicerestore_debug)
 		debug_plist(*data);
-	free(buffer);
+	free(buffer);*/
 	return 0;
 }
 
diff --git a/src/common.h b/src/common.h
index f8c0ae0..b6873a2 100644
--- a/src/common.h
+++ b/src/common.h
@@ -41,6 +41,12 @@ extern "C" {
 #define CPID_IPOD3G       8922
 #define CPID_IPAD1G       8930
 #define CPID_IPHONE4      8930
+#define CPID_IPOD4G       8930
+#define CPID_APPLETV2     8930
+#define CPID_IPHONE42     8930
+#define CPID_IPAD21       8940
+#define CPID_IPAD22       8940
+#define CPID_IPAD23       8940
 
 #define BDID_UNKNOWN        -1
 #define BDID_IPHONE2G        0
@@ -51,6 +57,12 @@ extern "C" {
 #define BDID_IPOD3G          2
 #define BDID_IPAD1G          2
 #define BDID_IPHONE4         0
+#define BDID_IPOD4G          8
+#define BDID_APPLETV2       10
+#define BDID_IPHONE42        6
+#define BDID_IPAD21          4
+#define BDID_IPAD22          6
+#define BDID_IPAD23          2
 
 #define DEVICE_UNKNOWN      -1
 #define DEVICE_IPHONE2G      0
@@ -61,6 +73,12 @@ extern "C" {
 #define DEVICE_IPOD3G        5
 #define DEVICE_IPAD1G        6
 #define DEVICE_IPHONE4       7
+#define DEVICE_IPOD4G        8
+#define DEVICE_APPLETV2      9
+#define DEVICE_IPHONE42     10
+#define DEVICE_IPAD21       11
+#define DEVICE_IPAD22       12
+#define DEVICE_IPAD23       13
 
 #define MODE_UNKNOWN        -1
 #define MODE_DFU             0
@@ -74,6 +92,8 @@ extern "C" {
 #define FLAG_CUSTOM          8
 #define FLAG_EXCLUDE        16
 
+extern int use_apple_server;
+
 struct dfu_client_t;
 struct normal_client_t;
 struct restore_clien_t;
@@ -135,6 +155,12 @@ static struct idevicerestore_device_t idevicerestore_devices[] = {
 	{  5, "iPod3,1",   "N18AP",  2,  8922 },
 	{  6, "iPad1,1",   "K48AP",  2,  8930 },
 	{  7, "iPhone3,1", "N90AP",  0,  8930 },
+	{  8, "iPod4,1", "N81AP",  8,  8930 },
+	{  9, "AppleTV2,1", "K66AP",  10,  8930 },
+	{  10, "iPhone3,1", "N92AP",  6,  8930 },
+	{  11, "iPad2,1", "K93AP",  4,  8940 },
+	{  12, "iPad2,2", "K94AP",  6,  8940 },
+	{  13, "iPad2,3", "K95AP",  2,  8940 },
 	{ -1,  NULL,        NULL,   -1,    -1 }
 };
 
diff --git a/src/idevicerestore.c b/src/idevicerestore.c
index f3fdbdc..3026551 100644
--- a/src/idevicerestore.c
+++ b/src/idevicerestore.c
@@ -36,12 +36,15 @@
 #include "recovery.h"
 #include "idevicerestore.h"
 
+int use_apple_server;
+
 static struct option longopts[] = {
 	{ "uuid",    required_argument, NULL, 'u' },
 	{ "debug",   no_argument,       NULL, 'd' },
 	{ "help",    no_argument,       NULL, 'h' },
 	{ "erase",   no_argument,       NULL, 'e' },
 	{ "custom",  no_argument,       NULL, 'c' },
+	{ "cydia",   no_argument,       NULL, 's' },
 	{ "exclude", no_argument,       NULL, 'x' },
 	{ NULL, 0, NULL, 0 }
 };
@@ -55,6 +58,7 @@ void usage(int argc, char* argv[]) {
 	printf("  -h, --help\t\tprints usage information\n");
 	printf("  -e, --erase\t\tperform a full restore, erasing all data\n");
 	printf("  -c, --custom\t\trestore with a custom firmware\n");
+	printf("  -s, --cydia\t\tuse Cydia's signature service instead of Apple's\n");
 	printf("  -x, --exclude\t\texclude nor/baseband upgrade\n");
 	printf("\n");
 }
@@ -65,6 +69,7 @@ int main(int argc, char* argv[]) {
 	char* ipsw = NULL;
 	char* uuid = NULL;
 	int tss_enabled = 0;
+	use_apple_server=1;
 
 	// create an instance of our context
 	struct idevicerestore_client_t* client = (struct idevicerestore_client_t*) malloc(sizeof(struct idevicerestore_client_t));
@@ -74,7 +79,7 @@ int main(int argc, char* argv[]) {
 	}
 	memset(client, '\0', sizeof(struct idevicerestore_client_t));
 
-	while ((opt = getopt_long(argc, argv, "dhcexu:", longopts, &optindex)) > 0) {
+	while ((opt = getopt_long(argc, argv, "dhcesxu:", longopts, &optindex)) > 0) {
 		switch (opt) {
 		case 'h':
 			usage(argc, argv);
@@ -93,6 +98,9 @@ int main(int argc, char* argv[]) {
 			client->flags |= FLAG_CUSTOM;
 			break;
 
+		case 's':
+			use_apple_server=0;
+
 		case 'x':
 			client->flags |= FLAG_EXCLUDE;
 			break;
@@ -363,7 +371,63 @@ int check_device(struct idevicerestore_client_t* client) {
 			break;
 
 		case CPID_IPAD1G:
-			device = DEVICE_IPAD1G;
+			// All the A4 devices are the same...BoardID'll solve that problem!
+			if (get_bdid(client, &bdid) < 0) {
+				error("ERROR: Unable to get device BDID\n");
+				break;
+			}
+
+			switch (bdid) {
+			case BDID_IPAD1G:
+				device = DEVICE_IPAD1G;
+				break;
+
+			case BDID_IPHONE4:
+				device = DEVICE_IPHONE4;
+				break;
+
+			case BDID_IPOD4G:
+				device = DEVICE_IPOD4G;
+				break;
+
+			case BDID_APPLETV2:
+				device = DEVICE_APPLETV2;
+				break;
+
+			case BDID_IPHONE42:
+				device = DEVICE_IPHONE42;
+				break;
+
+			default:
+				device = DEVICE_UNKNOWN;
+				break;
+			}
+			break;
+
+		case CPID_IPAD21:
+			// All the A5 devices are the same too...
+			if (get_bdid(client, &bdid) < 0) {
+				error("ERROR: Unable to get device BDID\n");
+				break;
+			}
+
+			switch (bdid) {
+			case BDID_IPAD21:
+				device = DEVICE_IPAD21;
+				break;
+
+			case BDID_IPAD22:
+				device = DEVICE_IPAD22;
+				break;
+
+			case BDID_IPAD23:
+				device = DEVICE_IPAD23;
+				break;
+
+			default:
+				device = DEVICE_UNKNOWN;
+				break;
+			}
 			break;
 
 		default:
@@ -657,7 +721,7 @@ void build_identity_print_information(plist_t build_identity) {
 		info("This restore will erase your device data.\n");
 
 	if (!strcmp(value, "Update"))
-		info("This restore will update your device without loosing data.\n");
+		info("This restore will update your device without losing data.\n");
 
 	free(value);
 
diff --git a/src/libirecovery.c b/src/libirecovery.c
new file mode 100644
index 0000000..3e31028
--- /dev/null
+++ b/src/libirecovery.c
@@ -0,0 +1,1293 @@
+/**
+  * GreenPois0n iRecovery - libirecovery.c
+  * Copyright (C) 2010 Chronic-Dev Team
+  * Copyright (C) 2010 Joshua Hill
+  * Copyright (C) 2008-2011 Nicolas Haunold
+  *
+  * 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 3 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 .
+ **/
+
+#include 
+#include 
+#include 
+#include 
+
+#ifndef WIN32
+#include 
+#else
+#define WIN32_LEAN_AND_MEAN
+#include 
+#include 
+#endif
+
+#include "libirecovery.h"
+
+#define BUFFER_SIZE 0x1000
+#define debug(...) if(libirecovery_debug) fprintf(stderr, __VA_ARGS__)
+
+static int libirecovery_debug = 1;
+#ifndef WIN32
+static libusb_context* libirecovery_context = NULL;
+#endif
+
+int irecv_write_file(const char* filename, const void* data, size_t size);
+int irecv_read_file(const char* filename, char** data, uint32_t* size);
+
+#ifdef WIN32
+static const GUID GUID_DEVINTERFACE_IBOOT = {0xED82A167L, 0xD61A, 0x4AF6, {0x9A, 0xB6, 0x11, 0xE5, 0x22, 0x36, 0xC5, 0x76}};
+static const GUID GUID_DEVINTERFACE_DFU = {0xB8085869L, 0xFEB9, 0x404B, {0x8C, 0xB1, 0x1E, 0x5C, 0x14, 0xFA, 0x8C, 0x54}};
+
+typedef struct usb_control_request {
+	uint8_t bmRequestType;
+	uint8_t bRequest;
+	uint16_t wValue;
+	uint16_t wIndex;
+	uint16_t wLength;
+
+	char data[];
+} usb_control_request;
+
+irecv_error_t mobiledevice_openpipes(irecv_client_t client);
+void mobiledevice_closepipes(irecv_client_t client);
+
+irecv_error_t mobiledevice_connect(irecv_client_t* client) {
+	irecv_error_t ret;
+
+	SP_DEVICE_INTERFACE_DATA currentInterface;
+	HDEVINFO usbDevices;
+	DWORD i;
+	LPSTR path;
+	irecv_client_t _client = (irecv_client_t) malloc(sizeof(struct irecv_client));
+	memset(_client, 0, sizeof(struct irecv_client));
+
+	// Get DFU paths
+	usbDevices = SetupDiGetClassDevs(&GUID_DEVINTERFACE_DFU, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
+	if(!usbDevices) {
+		return IRECV_E_UNABLE_TO_CONNECT;
+	}
+	currentInterface.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
+	for(i = 0; SetupDiEnumDeviceInterfaces(usbDevices, NULL, &GUID_DEVINTERFACE_DFU, i, ¤tInterface); i++) {
+		DWORD requiredSize = 0;
+		PSP_DEVICE_INTERFACE_DETAIL_DATA details;
+		SetupDiGetDeviceInterfaceDetail(usbDevices, ¤tInterface, NULL, 0, &requiredSize, NULL);
+		details = (PSP_DEVICE_INTERFACE_DETAIL_DATA) malloc(requiredSize);
+		details->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
+		if(!SetupDiGetDeviceInterfaceDetail(usbDevices, ¤tInterface, details, requiredSize, NULL, NULL)) {
+			irecv_close(_client);
+			free(details);
+			SetupDiDestroyDeviceInfoList(usbDevices);
+			return IRECV_E_UNABLE_TO_CONNECT;
+		} else {
+			LPSTR result = (LPSTR) malloc(requiredSize - sizeof(DWORD));
+			memcpy((void*) result, details->DevicePath, requiredSize - sizeof(DWORD));
+			free(details);
+			path = (LPSTR) malloc(requiredSize - sizeof(DWORD));
+			memcpy((void*) path, (void*) result, requiredSize - sizeof(DWORD));
+			TCHAR* pathEnd = strstr(path, "#{");
+			*pathEnd = '\0';
+			_client->DfuPath = result;
+			break;
+		}
+	}
+	SetupDiDestroyDeviceInfoList(usbDevices);
+	// Get iBoot path
+	usbDevices = SetupDiGetClassDevs(&GUID_DEVINTERFACE_IBOOT, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
+	if(!usbDevices) {
+		irecv_close(_client);
+		return IRECV_E_UNABLE_TO_CONNECT;
+	}
+	currentInterface.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
+	for(i = 0; SetupDiEnumDeviceInterfaces(usbDevices, NULL, &GUID_DEVINTERFACE_IBOOT, i, ¤tInterface); i++) {
+		DWORD requiredSize = 0;
+		PSP_DEVICE_INTERFACE_DETAIL_DATA details;
+		SetupDiGetDeviceInterfaceDetail(usbDevices, ¤tInterface, NULL, 0, &requiredSize, NULL);
+		details = (PSP_DEVICE_INTERFACE_DETAIL_DATA) malloc(requiredSize);
+		details->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
+		if(!SetupDiGetDeviceInterfaceDetail(usbDevices, ¤tInterface, details, requiredSize, NULL, NULL)) {
+			irecv_close(_client);
+			free(details);
+			SetupDiDestroyDeviceInfoList(usbDevices);
+			return IRECV_E_UNABLE_TO_CONNECT;
+		} else {
+			LPSTR result = (LPSTR) malloc(requiredSize - sizeof(DWORD));
+			memcpy((void*) result, details->DevicePath, requiredSize - sizeof(DWORD));
+			free(details);
+
+			if(strstr(result, path) == NULL) {
+				free(result);
+				continue;
+			}
+			
+			_client->iBootPath = result;
+			break;
+		}
+	}
+	SetupDiDestroyDeviceInfoList(usbDevices);
+	free(path);
+	
+	ret = mobiledevice_openpipes(_client);
+	if (ret != IRECV_E_SUCCESS) return ret;
+	
+	*client = _client;
+	return IRECV_E_SUCCESS;
+}
+
+irecv_error_t mobiledevice_openpipes(irecv_client_t client) {
+	if (client->iBootPath && !(client->hIB = CreateFile(client->iBootPath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL))) {
+		irecv_close(client);
+		return IRECV_E_UNABLE_TO_CONNECT;
+	}
+	if (client->DfuPath && !(client->hDFU = CreateFile(client->DfuPath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL))) {
+		irecv_close(client);
+		return IRECV_E_UNABLE_TO_CONNECT;
+	}
+
+	if (client->iBootPath == NULL) {
+		client->mode = kDfuMode;
+		client->handle = client->hDFU;
+	} else {
+		client->mode = kRecoveryMode2;
+		client->handle = client->hIB;
+	}
+	
+	return IRECV_E_SUCCESS;
+}
+
+void mobiledevice_closepipes(irecv_client_t client) {
+	if (client->hDFU!=NULL) {
+		CloseHandle(client->hDFU);
+		client->hDFU = NULL;
+	}
+	if (client->hIB!=NULL) {
+		CloseHandle(client->hIB);
+		client->hIB = NULL;
+	}
+}
+#endif
+
+int check_context(irecv_client_t client) {
+	if (client == NULL || client->handle == NULL) {
+		return IRECV_E_NO_DEVICE;
+	}
+
+	return IRECV_E_SUCCESS;
+}
+
+void irecv_init() {
+#ifndef WIN32
+	libusb_init(&libirecovery_context);
+#endif
+}
+
+void irecv_exit() {
+#ifndef WIN32
+	if (libirecovery_context != NULL) {
+		libusb_exit(libirecovery_context);
+		libirecovery_context = NULL;
+	}
+#endif
+}
+
+#ifdef __APPLE__
+	void dummy_callback() { }
+#endif
+
+int irecv_control_transfer( irecv_client_t client,
+							uint8_t bmRequestType,
+							uint8_t bRequest,
+							uint16_t wValue,
+							uint16_t wIndex,
+							unsigned char *data,
+							uint16_t wLength,
+							unsigned int timeout) {
+#ifndef WIN32
+	return libusb_control_transfer(client->handle, bmRequestType, bRequest, wValue, wIndex, data, wLength, timeout);
+#else
+	DWORD count = 0;
+	DWORD ret;
+	BOOL bRet;
+	OVERLAPPED overlapped;
+	
+	if (data == NULL) wLength = 0;
+	
+	usb_control_request* packet = (usb_control_request*) malloc(sizeof(usb_control_request) + wLength);
+	packet->bmRequestType = bmRequestType;
+	packet->bRequest = bRequest;
+	packet->wValue = wValue;
+	packet->wIndex = wIndex;
+	packet->wLength = wLength;
+	
+	if (bmRequestType < 0x80 && wLength > 0) {
+		memcpy(packet->data, data, wLength);
+	}
+	
+	memset(&overlapped, 0, sizeof(overlapped));
+	overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+	DeviceIoControl(client->handle, 0x2200A0, packet, sizeof(usb_control_request) + wLength, packet, sizeof(usb_control_request) + wLength, NULL, &overlapped);
+	ret = WaitForSingleObject(overlapped.hEvent, timeout);
+	bRet = GetOverlappedResult(client->handle, &overlapped, &count, FALSE);
+	CloseHandle(overlapped.hEvent);
+	if (!bRet) {
+		CancelIo(client->handle);
+		free(packet);
+		return -1;
+	}
+	
+	count -= sizeof(usb_control_request);
+	if (count > 0) {
+		if (bmRequestType >= 0x80) {
+			memcpy(data, packet->data, count);
+		}
+	}
+	free(packet);
+	return count;
+#endif
+}
+
+int irecv_bulk_transfer(irecv_client_t client,
+							unsigned char endpoint,
+							unsigned char *data,
+							int length,
+							int *transferred,
+							unsigned int timeout) {
+	int ret;
+
+#ifndef WIN32
+	ret = libusb_bulk_transfer(client->handle, endpoint, data, length, transferred, timeout);
+	if (ret < 0) {
+		libusb_clear_halt(client->handle, endpoint);
+	}
+#else
+	if (endpoint==0x4) {
+		ret = DeviceIoControl(client->handle, 0x220195, data, length, data, length, (PDWORD) transferred, NULL);
+	} else {
+		ret = 0;
+	}
+	ret==0?-1:0;
+#endif
+
+	return ret;
+}
+
+int irecv_get_string_descriptor_ascii(irecv_client_t client, uint8_t desc_index, unsigned char * buffer, int size) {
+#ifndef WIN32
+	return libusb_get_string_descriptor_ascii(client->handle, desc_index, buffer, size);
+#else
+	irecv_error_t ret;
+	unsigned short langid = 0;
+	unsigned char data[255];
+	int di, si;
+	memset(data, 0, sizeof(data));
+	memset(buffer, 0, size);
+
+	ret = irecv_control_transfer(client, 0x80, 0x06, (0x03 << 8) | desc_index, langid, data, sizeof(data), 1000);
+	
+	if (ret < 0) return ret;
+	if (data[1] != 0x03) return IRECV_E_UNKNOWN_ERROR;
+	if (data[0] > ret) return IRECV_E_UNKNOWN_ERROR; 
+
+	for (di = 0, si = 2; si < data[0]; si += 2) {
+		if (di >= (size - 1)) break;
+		if (data[si + 1]) {
+			/* high byte */
+			buffer[di++] = '?';
+        } else {
+            buffer[di++] = data[si];
+		}
+	}
+	buffer[di] = 0;
+	
+	return di;
+#endif
+}
+
+irecv_error_t irecv_open(irecv_client_t* pclient) {
+#ifndef WIN32
+	int i = 0;
+	struct libusb_device* usb_device = NULL;
+	struct libusb_device** usb_device_list = NULL;
+	struct libusb_device_handle* usb_handle = NULL;
+	struct libusb_device_descriptor usb_descriptor;
+
+	*pclient = NULL;
+	if(libirecovery_debug) {
+		irecv_set_debug_level(libirecovery_debug);
+	}
+
+	irecv_error_t error = IRECV_E_SUCCESS;
+	int usb_device_count = libusb_get_device_list(libirecovery_context, &usb_device_list);
+	for (i = 0; i < usb_device_count; i++) {
+		usb_device = usb_device_list[i];
+		libusb_get_device_descriptor(usb_device, &usb_descriptor);
+		if (usb_descriptor.idVendor == APPLE_VENDOR_ID) {
+			/* verify this device is in a mode we understand */
+			if (usb_descriptor.idProduct == kRecoveryMode1 ||
+				usb_descriptor.idProduct == kRecoveryMode2 ||
+				usb_descriptor.idProduct == kRecoveryMode3 ||
+				usb_descriptor.idProduct == kRecoveryMode4 ||
+				usb_descriptor.idProduct == kDfuMode) {
+
+				debug("opening device %04x:%04x...\n", usb_descriptor.idVendor, usb_descriptor.idProduct);
+
+				libusb_open(usb_device, &usb_handle);
+				if (usb_handle == NULL) {
+					libusb_free_device_list(usb_device_list, 1);
+					libusb_close(usb_handle);
+					libusb_exit(libirecovery_context);
+					return IRECV_E_UNABLE_TO_CONNECT;
+				}
+				libusb_free_device_list(usb_device_list, 1);
+
+				irecv_client_t client = (irecv_client_t) malloc(sizeof(struct irecv_client));
+				if (client == NULL) {
+					libusb_close(usb_handle);
+					libusb_exit(libirecovery_context);
+					return IRECV_E_OUT_OF_MEMORY;
+				}
+
+				memset(client, '\0', sizeof(struct irecv_client));
+				client->interface = 0;
+				client->handle = usb_handle;
+				client->mode = usb_descriptor.idProduct;
+				
+
+				error = irecv_set_configuration(client, 1);
+				if (error != IRECV_E_SUCCESS) {
+					return error;
+				}
+				
+				if (client->mode != kDfuMode) {
+					error = irecv_set_interface(client, 0, 0);
+					error = irecv_set_interface(client, 1, 1);
+				} else {
+					error = irecv_set_interface(client, 0, 0);
+				}
+
+				if (error != IRECV_E_SUCCESS) {
+					return error;
+				}
+
+				/* cache usb serial */
+				irecv_get_string_descriptor_ascii(client, usb_descriptor.iSerialNumber, (unsigned char*) client->serial, 255);
+				
+				*pclient = client;
+				return IRECV_E_SUCCESS;
+			}
+		}
+	}
+
+	return IRECV_E_UNABLE_TO_CONNECT;
+#else
+	int ret = mobiledevice_connect(pclient);
+	if (ret == IRECV_E_SUCCESS) {
+		irecv_get_string_descriptor_ascii(*pclient, 3, (unsigned char*) (*pclient)->serial, 255);
+	}
+	return ret;
+#endif
+}
+
+irecv_error_t irecv_set_configuration(irecv_client_t client, int configuration) {
+	if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE;
+	
+#ifndef WIN32
+	debug("Setting to configuration %d\n", configuration);
+
+	int current = 0;
+	libusb_get_configuration(client->handle, ¤t);
+	if (current != configuration) {
+		if (libusb_set_configuration(client->handle, configuration) < 0) {
+			return IRECV_E_USB_CONFIGURATION;
+		}
+	}
+
+	client->config = configuration;
+#endif
+
+	return IRECV_E_SUCCESS;
+}
+
+irecv_error_t irecv_set_interface(irecv_client_t client, int interface, int alt_interface) {
+	if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE;
+	
+#ifndef WIN32
+	// pod2g 2011-01-07: we may want to claim multiple interfaces
+	//libusb_release_interface(client->handle, client->interface);
+
+	debug("Setting to interface %d:%d\n", interface, alt_interface);
+	if (libusb_claim_interface(client->handle, interface) < 0) {
+		return IRECV_E_USB_INTERFACE;
+	}
+
+	if (libusb_set_interface_alt_setting(client->handle, interface, alt_interface) < 0) {
+		return IRECV_E_USB_INTERFACE;
+	}
+
+	client->interface = interface;
+	client->alt_interface = alt_interface;
+#endif
+
+	return IRECV_E_SUCCESS;
+}
+
+irecv_error_t irecv_reset(irecv_client_t client) {
+	if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE;
+	
+#ifndef WIN32
+	libusb_reset_device(client->handle);
+#else
+	int ret;
+	DWORD count;
+	ret = DeviceIoControl(client->handle, 0x22000C, NULL, 0, NULL, 0, &count, NULL);
+#endif
+
+	return IRECV_E_SUCCESS;
+}
+
+irecv_error_t irecv_open_attempts(irecv_client_t* pclient, int attempts) {
+	int i;
+
+	for (i = 0; i < attempts; i++) {
+		if (irecv_open(pclient) != IRECV_E_SUCCESS) {
+			debug("Connection failed. Waiting 1 sec before retry.\n");
+			sleep(1);
+		} else {
+			return IRECV_E_SUCCESS;
+		}		
+	}
+
+	return IRECV_E_UNABLE_TO_CONNECT;       
+}
+
+irecv_error_t irecv_event_subscribe(irecv_client_t client, irecv_event_type type, irecv_event_cb_t callback, void* user_data) {
+	switch(type) {
+	case IRECV_RECEIVED:
+		client->received_callback = callback;
+		break;
+
+	case IRECV_PROGRESS:
+		client->progress_callback = callback;
+
+	case IRECV_CONNECTED:
+		client->connected_callback = callback;
+
+	case IRECV_PRECOMMAND:
+		client->precommand_callback = callback;
+		break;
+
+	case IRECV_POSTCOMMAND:
+		client->postcommand_callback = callback;
+		break;
+
+	case IRECV_DISCONNECTED:
+		client->disconnected_callback = callback;
+
+	default:
+		return IRECV_E_UNKNOWN_ERROR;
+	}
+
+	return IRECV_E_SUCCESS;
+}
+
+irecv_error_t irecv_event_unsubscribe(irecv_client_t client, irecv_event_type type) {
+	switch(type) {
+	case IRECV_RECEIVED:
+		client->received_callback = NULL;
+		break;
+
+	case IRECV_PROGRESS:
+		client->progress_callback = NULL;
+
+	case IRECV_CONNECTED:
+		client->connected_callback = NULL;
+
+	case IRECV_PRECOMMAND:
+		client->precommand_callback = NULL;
+		break;
+
+	case IRECV_POSTCOMMAND:
+		client->postcommand_callback = NULL;
+		break;
+
+	case IRECV_DISCONNECTED:
+		client->disconnected_callback = NULL;
+
+	default:
+		return IRECV_E_UNKNOWN_ERROR;
+	}
+
+	return IRECV_E_SUCCESS;
+}
+
+irecv_error_t irecv_close(irecv_client_t client) {
+	if (client != NULL) {
+		if(client->disconnected_callback != NULL) {
+			irecv_event_t event;
+			event.size = 0;
+			event.data = NULL;
+			event.progress = 0;
+			event.type = IRECV_DISCONNECTED;
+			client->disconnected_callback(client, &event);
+		}
+#ifndef WIN32
+		if (client->handle != NULL) {
+			if (client->mode != kDfuMode) {
+				libusb_release_interface(client->handle, client->interface);
+			}
+			libusb_close(client->handle);
+			client->handle = NULL;
+		}
+#else
+		if (client->iBootPath!=NULL) {
+			free(client->iBootPath);
+			client->iBootPath = NULL;
+		}
+		if (client->DfuPath!=NULL) {
+			free(client->DfuPath);
+			client->DfuPath = NULL;
+		}
+		mobiledevice_closepipes(client);
+#endif
+		free(client);
+		client = NULL;
+	}
+
+	return IRECV_E_SUCCESS;
+}
+
+void irecv_set_debug_level(int level) {
+	libirecovery_debug = level;
+#ifndef WIN32
+	if(libirecovery_context) {
+		libusb_set_debug(libirecovery_context, libirecovery_debug);
+	}
+#endif
+}
+
+static irecv_error_t irecv_send_command_raw(irecv_client_t client, char* command) {
+	unsigned int length = strlen(command);
+	if (length >= 0x100) {
+		length = 0xFF;
+	}
+
+	if (length > 0) {
+		int ret = irecv_control_transfer(client, 0x40, 0, 0, 0, (unsigned char*) command, length + 1, 1000);
+	}
+
+	return IRECV_E_SUCCESS;
+}
+
+irecv_error_t irecv_send_command(irecv_client_t client, char* command) {
+	irecv_error_t error = 0;
+	if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE;
+
+	unsigned int length = strlen(command);
+	if (length >= 0x100) {
+		length = 0xFF;
+	}
+
+	irecv_event_t event;
+	if(client->precommand_callback != NULL) {
+		event.size = length;
+		event.data = command;
+		event.type = IRECV_PRECOMMAND;
+		if(client->precommand_callback(client, &event)) {
+			return IRECV_E_SUCCESS;
+		}
+	}
+
+	error = irecv_send_command_raw(client, command);
+	if (error != IRECV_E_SUCCESS) {
+		debug("Failed to send command %s\n", command);
+		if (error != IRECV_E_PIPE)
+			return error;
+	}
+
+	if(client->postcommand_callback != NULL) {
+		event.size = length;
+		event.data = command;
+		event.type = IRECV_POSTCOMMAND;
+		if(client->postcommand_callback(client, &event)) {
+			return IRECV_E_SUCCESS;
+		}
+	}
+
+	return IRECV_E_SUCCESS;
+}
+
+irecv_error_t irecv_send_file(irecv_client_t client, const char* filename, int dfuNotifyFinished) {
+	if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE;
+
+	FILE* file = fopen(filename, "rb");
+	if (file == NULL) {
+		return IRECV_E_FILE_NOT_FOUND;
+	}
+
+	fseek(file, 0, SEEK_END);
+	long length = ftell(file);
+	fseek(file, 0, SEEK_SET);
+
+	char* buffer = (char*) malloc(length);
+	if (buffer == NULL) {
+		fclose(file);
+		return IRECV_E_OUT_OF_MEMORY;
+	}
+
+	long bytes = fread(buffer, 1, length, file);
+	fclose(file);
+
+	if (bytes != length) {
+		free(buffer);
+		return IRECV_E_UNKNOWN_ERROR;
+	}
+
+	irecv_error_t error = irecv_send_buffer(client, buffer, length, dfuNotifyFinished);
+	free(buffer);
+	return error;
+}
+
+irecv_error_t irecv_get_status(irecv_client_t client, unsigned int* status) {
+	if (check_context(client) != IRECV_E_SUCCESS) {
+		*status = 0;
+		return IRECV_E_NO_DEVICE;
+	}
+
+	unsigned char buffer[6];
+	memset(buffer, '\0', 6);
+	if (irecv_control_transfer(client, 0xA1, 3, 0, 0, buffer, 6, 1000) != 6) {
+		*status = 0;
+		return IRECV_E_USB_STATUS;
+	}
+
+	*status = (unsigned int) buffer[4];
+	return IRECV_E_SUCCESS;
+}
+
+irecv_error_t irecv_send_buffer(irecv_client_t client, unsigned char* buffer, unsigned long length, int dfuNotifyFinished) {
+	irecv_error_t error = 0;
+	int recovery_mode = (client->mode != kDfuMode);
+	if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE;
+
+	int packet_size = 0x800;
+	int last = length % packet_size;
+	int packets = length / packet_size;
+	if (last != 0) {
+		packets++;
+	} else {
+		last = packet_size;
+	}
+
+	/* initiate transfer */
+	if (recovery_mode) {
+		error = irecv_control_transfer(client, 0x41, 0, 0, 0, NULL, 0, 1000);
+	} else {
+		error = irecv_control_transfer(client, 0x21, 4, 0, 0, NULL, 0, 1000);
+	}
+	if (error != IRECV_E_SUCCESS) {
+		return error;
+	}
+
+	int i = 0;
+	double progress = 0;
+	unsigned long count = 0;
+	unsigned int status = 0;
+	int bytes = 0;
+	for (i = 0; i < packets; i++) {
+		int size = (i + 1) < packets ? packet_size : last;
+
+		/* Use bulk transfer for recovery mode and control transfer for DFU and WTF mode */
+		if (recovery_mode) {
+			error = irecv_bulk_transfer(client, 0x04, &buffer[i * packet_size], size, &bytes, 1000);
+		} else {
+			bytes = irecv_control_transfer(client, 0x21, 1, 0, 0, &buffer[i * packet_size], size, 1000);
+		}
+
+		if (bytes != size) {
+			return IRECV_E_USB_UPLOAD;
+		}
+
+		if (!recovery_mode) {
+			error = irecv_get_status(client, &status);
+		}
+
+		if (error != IRECV_E_SUCCESS) {
+			return error;
+		}
+
+		if (!recovery_mode && status != 5) {
+			return IRECV_E_USB_UPLOAD;
+		}
+
+		count += size;
+		if(client->progress_callback != NULL) {
+			irecv_event_t event;
+			event.progress = ((double) count/ (double) length) * 100.0;
+			event.type = IRECV_PROGRESS;
+			event.data = "Uploading";
+			event.size = count;
+			client->progress_callback(client, &event);
+		} else {
+			debug("Sent: %d bytes - %lu of %lu\n", bytes, count, length);
+		}
+	}
+
+	if (dfuNotifyFinished && !recovery_mode) {
+		irecv_control_transfer(client, 0x21, 1, 0, 0, (unsigned char*) buffer, 0, 1000);
+
+		for (i = 0; i < 3; i++) {
+			error = irecv_get_status(client, &status);
+			if (error != IRECV_E_SUCCESS) {
+				return error;
+			}
+		}
+		irecv_reset(client);
+	}
+
+	return IRECV_E_SUCCESS;
+}
+
+irecv_error_t irecv_receive(irecv_client_t client) {
+	char buffer[BUFFER_SIZE];
+	memset(buffer, '\0', BUFFER_SIZE);
+	if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE;
+
+	int bytes = 0;
+	while (irecv_bulk_transfer(client, 0x81, (unsigned char*) buffer, BUFFER_SIZE, &bytes, 500) == 0) {
+		if (bytes > 0) {
+			if (client->received_callback != NULL) {
+				irecv_event_t event;
+				event.size = bytes;
+				event.data = buffer;
+				event.type = IRECV_RECEIVED;
+				if (client->received_callback(client, &event) != 0) {
+					return IRECV_E_SUCCESS;
+				}
+			}
+			if (bytes < BUFFER_SIZE) break;
+		} else break;
+	}
+
+	return IRECV_E_SUCCESS;
+}
+
+irecv_error_t irecv_getenv(irecv_client_t client, const char* variable, char** value) {
+	int ret = 0;
+	char command[256];
+	if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE;
+	*value = NULL;
+
+	if(variable == NULL) {
+		return IRECV_E_UNKNOWN_ERROR;
+	}
+
+	memset(command, '\0', sizeof(command));
+	snprintf(command, sizeof(command)-1, "getenv %s", variable);
+	irecv_error_t error = irecv_send_command_raw(client, command);
+	if(error == IRECV_E_PIPE) {
+		return IRECV_E_SUCCESS;
+	}
+	if(error != IRECV_E_SUCCESS) {
+		return error;
+	}
+
+	char* response = (char*) malloc(256);
+	if (response == NULL) {
+		return IRECV_E_OUT_OF_MEMORY;
+	}
+
+	memset(response, '\0', 256);
+	ret = irecv_control_transfer(client, 0xC0, 0, 0, 0, (unsigned char*) response, 255, 1000);
+
+	*value = response;
+	return IRECV_E_SUCCESS;
+}
+
+irecv_error_t irecv_getret(irecv_client_t client, unsigned int* value) {
+	int ret = 0;
+	if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE;
+	*value = 0;
+
+	char* response = (char*) malloc(256);
+	if (response == NULL) {
+		return IRECV_E_OUT_OF_MEMORY;
+	}
+
+	memset(response, '\0', 256);
+	ret = irecv_control_transfer(client, 0xC0, 0, 0, 0, (unsigned char*) response, 255, 1000);
+
+	*value = (unsigned int) *response;
+	return IRECV_E_SUCCESS;
+}
+
+irecv_error_t irecv_get_cpid(irecv_client_t client, unsigned int* cpid) {
+	if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE;
+
+	char* cpid_string = strstr(client->serial, "CPID:");
+	if (cpid_string == NULL) {
+		*cpid = 0;
+		return IRECV_E_UNKNOWN_ERROR;
+	}
+	sscanf(cpid_string, "CPID:%d", cpid);
+
+	return IRECV_E_SUCCESS;
+}
+
+irecv_error_t irecv_get_bdid(irecv_client_t client, unsigned int* bdid) {
+	if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE;
+
+	char* bdid_string = strstr(client->serial, "BDID:");
+	if (bdid_string == NULL) {
+		*bdid = 0;
+		return IRECV_E_UNKNOWN_ERROR;
+	}
+	sscanf(bdid_string, "BDID:%d", bdid);
+
+	return IRECV_E_SUCCESS;
+}
+
+irecv_error_t irecv_get_ecid(irecv_client_t client, unsigned long long* ecid) {
+	if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE;
+
+	char* ecid_string = strstr(client->serial, "ECID:");
+	if (ecid_string == NULL) {
+		*ecid = 0;
+		return IRECV_E_UNKNOWN_ERROR;
+	}
+	sscanf(ecid_string, "ECID:%qX", ecid);
+
+	return IRECV_E_SUCCESS;
+}
+
+irecv_error_t irecv_get_srnm(irecv_client_t client, unsigned char* srnm) {
+	if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE;
+
+	char* srnmp;
+	char* srnm_string = strstr(client->serial, "SRNM:[");
+	if(srnm_string == NULL) {
+		srnm = NULL;
+		return IRECV_E_UNKNOWN_ERROR;
+	}
+
+	sscanf(srnm_string, "SRNM:[%s]", srnm);
+	srnmp = strrchr(srnm, ']');
+	if(srnmp != NULL) {
+		*srnmp = '\0';
+	}
+
+	return IRECV_E_SUCCESS;
+}
+
+irecv_error_t irecv_get_imei(irecv_client_t client, unsigned char* imei) {
+	if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE;
+
+	char* imeip;
+	char* imei_string = strstr(client->serial, "IMEI:[");
+	if (imei_string == NULL) {
+		*imei = 0;
+		return IRECV_E_UNKNOWN_ERROR;
+	}
+
+
+	sscanf(imei_string, "IMEI:[%s]", imei);
+	imeip = strrchr(imei, ']');
+	if(imeip != NULL) {
+		*imeip = '\0';
+	}
+
+	return IRECV_E_SUCCESS;
+}
+
+irecv_error_t irecv_send_exploit(irecv_client_t client) {
+	if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE;
+	irecv_control_transfer(client, 0x21, 2, 0, 0, NULL, 0, 1000);
+	return IRECV_E_SUCCESS;
+}
+
+irecv_error_t irecv_execute_script(irecv_client_t client, const char* filename) {
+	irecv_error_t error = IRECV_E_SUCCESS;
+	if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE;
+
+	char* file_data = NULL;
+	unsigned int file_size = 0;
+	if(irecv_read_file(filename, &file_data, &file_size) < 0) {
+		return IRECV_E_FILE_NOT_FOUND;
+	}
+
+	char* line = strtok(file_data, "\n");
+	while(line != NULL) {
+		if(line[0] != '#') {
+			error = irecv_send_command(client, line);
+			if(error != IRECV_E_SUCCESS) {
+				return error;
+			}
+
+			error = irecv_receive(client);
+			if(error != IRECV_E_SUCCESS) {
+				return error;
+			}
+		}
+		line = strtok(NULL, "\n");
+	}
+
+	return IRECV_E_SUCCESS;
+}
+
+irecv_error_t irecv_saveenv(irecv_client_t client) {
+	irecv_error_t error = irecv_send_command_raw(client, "saveenv");
+	if(error != IRECV_E_SUCCESS) {
+		return error;
+	}
+	return IRECV_E_SUCCESS;
+}
+
+irecv_error_t irecv_setenv(irecv_client_t client, const char* variable, const char* value) {
+	char command[256];
+	if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE;
+
+	if(variable == NULL || value == NULL) {
+		return IRECV_E_UNKNOWN_ERROR;
+	}
+
+	memset(command, '\0', sizeof(command));
+	snprintf(command, sizeof(command)-1, "setenv %s %s", variable, value);
+	irecv_error_t error = irecv_send_command_raw(client, command);
+	if(error != IRECV_E_SUCCESS) {
+		return error;
+	}
+
+	return IRECV_E_SUCCESS;
+}
+
+const char* irecv_strerror(irecv_error_t error) {
+	switch (error) {
+	case IRECV_E_SUCCESS:
+		return "Command completed successfully";
+
+	case IRECV_E_NO_DEVICE:
+		return "Unable to find device";
+
+	case IRECV_E_OUT_OF_MEMORY:
+		return "Out of memory";
+
+	case IRECV_E_UNABLE_TO_CONNECT:
+		return "Unable to connect to device";
+
+	case IRECV_E_INVALID_INPUT:
+		return "Invalid input";
+
+	case IRECV_E_FILE_NOT_FOUND:
+		return "File not found";
+
+	case IRECV_E_USB_UPLOAD:
+		return "Unable to upload data to device";
+
+	case IRECV_E_USB_STATUS:
+		return "Unable to get device status";
+
+	case IRECV_E_USB_INTERFACE:
+		return "Unable to set device interface";
+
+	case IRECV_E_USB_CONFIGURATION:
+		return "Unable to set device configuration";
+
+	case IRECV_E_PIPE:
+		return "Broken pipe";
+
+	case IRECV_E_TIMEOUT:
+		return "Timeout talking to device";
+
+	default:
+		return "Unknown error";
+	}
+
+	return NULL;
+}
+
+int irecv_write_file(const char* filename, const void* data, size_t size) {
+	size_t bytes = 0;
+	FILE* file = NULL;
+
+	debug("Writing data to %s\n", filename);
+	file = fopen(filename, "wb");
+	if (file == NULL) {
+		//error("read_file: Unable to open file %s\n", filename);
+		return -1;
+	}
+
+	bytes = fwrite(data, 1, size, file);
+	fclose(file);
+
+	if (bytes != size) {
+		//error("ERROR: Unable to write entire file: %s: %d of %d\n", filename, bytes, size);
+		return -1;
+	}
+
+	return size;
+}
+
+int irecv_read_file(const char* filename, char** data, uint32_t* size) {
+	size_t bytes = 0;
+	size_t length = 0;
+	FILE* file = NULL;
+	char* buffer = NULL;
+	debug("Reading data from %s\n", filename);
+
+	*size = 0;
+	*data = NULL;
+
+	file = fopen(filename, "rb");
+	if (file == NULL) {
+		//error("read_file: File %s not found\n", filename);
+		return -1;
+	}
+
+	fseek(file, 0, SEEK_END);
+	length = ftell(file);
+	rewind(file);
+
+	buffer = (char*) malloc(length);
+	if(buffer == NULL) {
+		//error("ERROR: Out of memory\n");
+		fclose(file);
+		return -1;
+	}
+	bytes = fread(buffer, 1, length, file);
+	fclose(file);
+
+	if(bytes != length) {
+		//error("ERROR: Unable to read entire file\n");
+		free(buffer);
+		return -1;
+	}
+
+	*size = length;
+	*data = buffer;
+	return 0;
+}
+
+irecv_error_t irecv_reset_counters(irecv_client_t client) {
+	if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE;
+	if (client->mode == kDfuMode) {
+		irecv_control_transfer(client, 0x21, 4, 0, 0, 0, 0, 1000);
+	}
+	return IRECV_E_SUCCESS;
+}
+
+irecv_error_t irecv_recv_buffer(irecv_client_t client, char* buffer, unsigned long length) {
+	irecv_error_t error = 0;
+	int recovery_mode = (client->mode != kDfuMode);
+
+	if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE;
+
+	int packet_size = recovery_mode ? 0x2000: 0x800;
+	int last = length % packet_size;
+	int packets = length / packet_size;
+	if (last != 0) {
+		packets++;
+	} else {
+		last = packet_size;
+	}
+
+	int i = 0;
+	int bytes = 0;
+	double progress = 0;
+	unsigned long count = 0;
+	unsigned int status = 0;
+	for (i = 0; i < packets; i++) {
+		unsigned short size = (i+1) < packets ? packet_size : last;
+		bytes = irecv_control_transfer(client, 0xA1, 2, 0, 0, &buffer[i * packet_size], size, 1000);
+		
+		if (bytes != size) {
+			return IRECV_E_USB_UPLOAD;
+		}
+
+		count += size;
+		if(client->progress_callback != NULL) {
+			irecv_event_t event;
+			event.progress = ((double) count/ (double) length) * 100.0;
+			event.type = IRECV_PROGRESS;
+			event.data = "Downloading";
+			event.size = count;
+			client->progress_callback(client, &event);
+		} else {
+			debug("Sent: %d bytes - %lu of %lu\n", bytes, count, length);
+		}
+	}
+
+	return IRECV_E_SUCCESS;
+}
+
+irecv_error_t irecv_finish_transfer(irecv_client_t client) {
+	int i = 0;
+	unsigned int status = 0;
+
+	if (check_context(client) != IRECV_E_SUCCESS) return IRECV_E_NO_DEVICE;
+
+	irecv_control_transfer(client, 0x21, 1, 0, 0, 0, 0, 1000);
+
+	for(i = 0; i < 3; i++){
+		irecv_get_status(client, &status);
+	}
+	irecv_reset(client);
+	return IRECV_E_SUCCESS;
+}
+
+irecv_error_t irecv_get_device(irecv_client_t client, irecv_device_t* device) {
+	int device_id = DEVICE_UNKNOWN;
+	uint32_t bdid = 0;
+	uint32_t cpid = 0;
+
+	if (irecv_get_cpid(client, &cpid) < 0) {
+		return IRECV_E_UNKNOWN_ERROR;
+	}
+
+	switch (cpid) {
+	case CPID_IPHONE2G:
+		// iPhone1,1 iPhone1,2 and iPod1,1 all share the same ChipID
+		//   so we need to check the BoardID
+		if (irecv_get_bdid(client, &bdid) < 0) {
+			break;
+		}
+
+		switch (bdid) {
+		case BDID_IPHONE2G:
+			device_id = DEVICE_IPHONE2G;
+			break;
+
+		case BDID_IPHONE3G:
+			device_id = DEVICE_IPHONE3G;
+			break;
+
+		case BDID_IPOD1G:
+			device_id = DEVICE_IPOD1G;
+			break;
+
+		default:
+			device_id = DEVICE_UNKNOWN;
+			break;
+		}
+		break;
+
+	case CPID_IPHONE3GS:
+		device_id = DEVICE_IPHONE3GS;
+		break;
+
+	case CPID_IPOD2G:
+		device_id = DEVICE_IPOD2G;
+		break;
+
+	case CPID_IPOD3G:
+		device_id = DEVICE_IPOD3G;
+		break;
+
+	case CPID_IPAD1G:
+		// iPhone3,1 iPhone3,3 iPad4,1 and iPad1,1 all share the same ChipID
+		//   so we need to check the BoardID
+		if (irecv_get_bdid(client, &bdid) < 0) {
+			break;
+		}
+
+		switch (bdid) {
+		case BDID_IPAD1G:
+			device_id = DEVICE_IPAD1G;
+			break;
+
+		case BDID_IPHONE4:
+			device_id = DEVICE_IPHONE4;
+			break;
+
+		case BDID_IPOD4G:
+			device_id = DEVICE_IPOD4G;
+			break;
+
+		case BDID_APPLETV2:
+			device_id = DEVICE_APPLETV2;
+			break;
+
+		case BDID_IPHONE42:
+			device_id = DEVICE_IPHONE42;
+			break;
+
+		default:
+			device_id = DEVICE_UNKNOWN;
+			break;
+		}
+		break;
+
+	default:
+		device_id = DEVICE_UNKNOWN;
+		break;
+	}
+
+	*device = &irecv_devices[device_id];
+	return IRECV_E_SUCCESS;
+}
+
+irecv_client_t irecv_reconnect(irecv_client_t client, int initial_pause) {
+	irecv_error_t error = 0;
+	irecv_client_t new_client = NULL;
+	irecv_event_cb_t progress_callback = client->progress_callback;
+
+	if (check_context(client) == IRECV_E_SUCCESS) {
+		irecv_close(client);
+	}
+
+	if (initial_pause > 0) {
+		debug("Waiting %d seconds for the device to pop up...\n", initial_pause);
+		sleep(initial_pause);
+	}
+	
+	error = irecv_open_attempts(&new_client, 10);
+	if(error != IRECV_E_SUCCESS) {
+		return NULL;
+	}
+
+	new_client->progress_callback = progress_callback;
+	return new_client;
+}
+
+void irecv_hexdump(unsigned char* buf, unsigned int len, unsigned int addr) {
+	int i, j;
+	printf("0x%08x: ", addr);
+	for (i = 0; i < len; i++) {
+		if (i % 16 == 0 && i != 0) {
+			for (j=i-16; j < i; j++) {
+	unsigned char car = buf[j];
+	if (car < 0x20 || car > 0x7f) car = '.';
+	printf("%c", car);
+			}
+			printf("\n");
+			addr += 0x10;
+			printf("0x%08x: ", addr);
+		}
+		printf("%02x ", buf[i]);
+	}
+
+	int done = (i % 16);
+	int remains = 16 - done;
+	if (done > 0) {
+		for (j = 0; j < remains; j++) {
+			printf("	 ");
+		}
+	}
+
+	if ((i - done) >= 0) {
+		if (done == 0 && i > 0) done = 16;
+		for (j = (i - done); j < i; j++) {
+			unsigned char car = buf[j];
+			if (car < 0x20 || car > 0x7f) car = '.';
+			printf("%c", car);
+		}
+	}
+	printf("\n");
+}
diff --git a/src/libirecovery.h b/src/libirecovery.h
new file mode 100644
index 0000000..12e6698
--- /dev/null
+++ b/src/libirecovery.h
@@ -0,0 +1,232 @@
+/**
+  * GreenPois0n iRecovery - libirecovery.h
+  * Copyright (C) 2010 Chronic-Dev Team
+  * Copyright (C) 2010 Joshua Hill
+  *
+  * 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 3 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 .
+ **/
+
+#ifndef LIBIRECOVERY_H
+#define LIBIRECOVERY_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef WIN32
+#include 
+#else
+#define WIN32_LEAN_AND_MEAN
+#include 
+#define sleep(n) Sleep(1000 * n)
+#endif
+
+#define APPLE_VENDOR_ID 0x05AC
+
+#define CPID_UNKNOWN        -1
+#define CPID_IPHONE2G     8900
+#define CPID_IPOD1G       8900
+#define CPID_IPHONE3G     8900
+#define CPID_IPOD2G       8720
+#define CPID_IPHONE3GS    8920
+#define CPID_IPOD3G       8922
+#define CPID_IPAD1G       8930
+#define CPID_IPHONE4      8930
+#define CPID_IPOD4G       8930
+#define CPID_APPLETV2     8930
+#define CPID_IPHONE42     8930
+#define CPID_IPAD21       8940
+#define CPID_IPAD22       8940
+#define CPID_IPAD23       8940
+
+#define BDID_UNKNOWN        -1
+#define BDID_IPHONE2G        0
+#define BDID_IPOD1G          2
+#define BDID_IPHONE3G        4
+#define BDID_IPOD2G          0
+#define BDID_IPHONE3GS       0
+#define BDID_IPOD3G          2
+#define BDID_IPAD1G          2
+#define BDID_IPHONE4         0
+#define BDID_IPOD4G          8
+#define BDID_APPLETV2       10
+#define BDID_IPHONE42        6
+#define BDID_IPAD21          4
+#define BDID_IPAD22          6
+#define BDID_IPAD23          2
+
+#define DEVICE_UNKNOWN      -1
+#define DEVICE_IPHONE2G      0
+#define DEVICE_IPOD1G        1
+#define DEVICE_IPHONE3G      2
+#define DEVICE_IPOD2G        3
+#define DEVICE_IPHONE3GS     4
+#define DEVICE_IPOD3G        5
+#define DEVICE_IPAD1G        6
+#define DEVICE_IPHONE4       7
+#define DEVICE_IPOD4G        8
+#define DEVICE_APPLETV2      9
+#define DEVICE_IPHONE42     10
+#define DEVICE_IPAD21       11
+#define DEVICE_IPAD22       12
+#define DEVICE_IPAD23       13
+
+enum {
+	kRecoveryMode1 = 0x1280,
+	kRecoveryMode2 = 0x1281,
+	kRecoveryMode3 = 0x1282,
+	kRecoveryMode4 = 0x1283,
+	kDfuMode = 0x1227
+};
+
+typedef enum {
+	IRECV_E_SUCCESS = 0,
+	IRECV_E_NO_DEVICE = -1,
+	IRECV_E_OUT_OF_MEMORY = -2,
+	IRECV_E_UNABLE_TO_CONNECT = -3,
+	IRECV_E_INVALID_INPUT = -4,
+	IRECV_E_FILE_NOT_FOUND = -5,
+	IRECV_E_USB_UPLOAD = -6,
+	IRECV_E_USB_STATUS = -7,
+	IRECV_E_USB_INTERFACE = -8,
+	IRECV_E_USB_CONFIGURATION = -9,
+	IRECV_E_PIPE = -10,
+	IRECV_E_TIMEOUT = -11,
+	IRECV_E_UNKNOWN_ERROR = -255
+} irecv_error_t;
+
+typedef enum {
+	IRECV_RECEIVED = 1,
+	IRECV_PRECOMMAND = 2,
+	IRECV_POSTCOMMAND = 3,
+	IRECV_CONNECTED = 4,
+	IRECV_DISCONNECTED = 5,
+	IRECV_PROGRESS = 6
+} irecv_event_type;
+
+typedef struct {
+	int size;
+	char* data;
+	double progress;
+	irecv_event_type type;
+} irecv_event_t;
+
+struct irecv_client;
+typedef struct irecv_client* irecv_client_t;
+typedef struct irecv_device* irecv_device_t;
+typedef int(*irecv_event_cb_t)(irecv_client_t client, const irecv_event_t* event);
+
+struct irecv_client {
+	int debug;
+	int config;
+	int interface;
+	int alt_interface;
+	unsigned short mode;
+	char serial[256];
+	
+#ifndef WIN32
+	libusb_device_handle* handle;
+#else
+	HANDLE handle;
+	HANDLE hDFU;
+	HANDLE hIB;
+	LPSTR iBootPath;
+	LPSTR DfuPath;
+#endif
+	
+	irecv_event_cb_t progress_callback;
+	irecv_event_cb_t received_callback;
+	irecv_event_cb_t connected_callback;
+	irecv_event_cb_t precommand_callback;
+	irecv_event_cb_t postcommand_callback;
+	irecv_event_cb_t disconnected_callback;
+};
+
+struct irecv_device {
+	int index;
+	const char* product;
+	const char* model;
+	unsigned int board_id;
+	unsigned int chip_id;
+	const char* url;
+};
+
+static struct irecv_device irecv_devices[] = {
+	{  0, "iPhone1,1",  "m68ap",  0,  8900,
+	"http://appldnld.apple.com.edgesuite.net/content.info.apple.com/iPhone/061-7481.20100202.4orot/iPhone1,1_3.1.3_7E18_Restore.ipsw" },
+	{  1, "iPod1,1",    "n45ap",  2,  8900,
+	NULL },
+	{  2, "iPhone1,2",  "n82ap",  4,  8900,
+	"http://appldnld.apple.com/iPhone4/061-7932.20100908.3fgt5/iPhone1,2_4.1_8B117_Restore.ipsw" },
+	{  3, "iPod2,1",    "n72ap",  0,  8720,
+	"http://appldnld.apple.com/iPhone4/061-7937.20100908.ghj4f/iPod2,1_4.1_8B117_Restore.ipsw" },
+	{  4, "iPhone2,1",  "n88ap",  0,  8920,
+	"http://appldnld.apple.com/iPhone4/061-7938.20100908.F3rCk/iPhone2,1_4.1_8B117_Restore.ipsw" },
+	{  5, "iPod3,1",    "n18ap",  2,  8922,
+	"http://appldnld.apple.com/iPhone4/061-7941.20100908.sV9KE/iPod3,1_4.1_8B117_Restore.ipsw" },
+	{  6, "iPad1,1",    "k48ap",  2,  8930,
+	"http://appldnld.apple.com/iPad/061-8801.20100811.CvfR5/iPad1,1_3.2.2_7B500_Restore.ipsw" },
+	{  7, "iPhone3,1",  "n90ap",  0,  8930,
+	"http://appldnld.apple.com/iPhone4/061-7939.20100908.Lcyg3/iPhone3,1_4.1_8B117_Restore.ipsw" },
+	{  8, "iPod4,1",    "n81ap",  8,  8930,
+	"http://appldnld.apple.com/iPhone4/061-8490.20100901.hyjtR/iPod4,1_4.1_8B117_Restore.ipsw" },
+	{  9, "AppleTV2,1", "k66ap", 10,  8930,
+	"http://appldnld.apple.com/AppleTV/061-8940.20100926.Tvtnz/AppleTV2,1_4.1_8M89_Restore.ipsw" },
+	{ 10, "iPhone3,3",  "n92ap",  6,  8930,
+	"http://appldnld.apple.com/iPhone4/041-0177.20110131.Pyvrz/iPhone3,3_4.2.6_8E200_Restore.ipsw" },
+	{ -1,  NULL,        NULL,   -1,    -1,
+	NULL }
+};
+
+void irecv_set_debug_level(int level);
+const char* irecv_strerror(irecv_error_t error);
+irecv_error_t irecv_open_attempts(irecv_client_t* pclient, int attempts);
+irecv_error_t irecv_open(irecv_client_t* client);
+irecv_error_t irecv_reset(irecv_client_t client);
+irecv_error_t irecv_close(irecv_client_t client);
+irecv_error_t irecv_receive(irecv_client_t client);
+irecv_error_t irecv_send_exploit(irecv_client_t client);
+irecv_error_t irecv_execute_script(irecv_client_t client, const char* filename);
+irecv_error_t irecv_set_configuration(irecv_client_t client, int configuration);
+
+irecv_error_t irecv_event_subscribe(irecv_client_t client, irecv_event_type type, irecv_event_cb_t callback, void *user_data);
+irecv_error_t irecv_event_unsubscribe(irecv_client_t client, irecv_event_type type);
+
+irecv_error_t irecv_send_file(irecv_client_t client, const char* filename, int dfuNotifyFinished);
+irecv_error_t irecv_send_command(irecv_client_t client, char* command);
+irecv_error_t irecv_send_buffer(irecv_client_t client, unsigned char* buffer, unsigned long length, int dfuNotifyFinished);
+
+irecv_error_t irecv_saveenv(irecv_client_t client);
+irecv_error_t irecv_getret(irecv_client_t client, unsigned int* value);
+irecv_error_t irecv_getenv(irecv_client_t client, const char* variable, char** value);
+irecv_error_t irecv_setenv(irecv_client_t client, const char* variable, const char* value);
+irecv_error_t irecv_set_interface(irecv_client_t client, int interface, int alt_interface);
+irecv_error_t irecv_get_cpid(irecv_client_t client, unsigned int* cpid);
+irecv_error_t irecv_get_bdid(irecv_client_t client, unsigned int* bdid);
+irecv_error_t irecv_get_ecid(irecv_client_t client, unsigned long long* ecid);
+void irecv_hexdump(unsigned char* buf, unsigned int len, unsigned int addr);
+
+void irecv_init();
+void irecv_exit();
+irecv_client_t irecv_reconnect(irecv_client_t client, int initial_pause);
+irecv_error_t irecv_reset_counters(irecv_client_t client);
+irecv_error_t irecv_finish_transfer(irecv_client_t client);
+irecv_error_t irecv_recv_buffer(irecv_client_t client, char* buffer, unsigned long length);
+irecv_error_t irecv_get_device(irecv_client_t client, irecv_device_t* device);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/recovery.c b/src/recovery.c
index 40b207e..0642028 100644
--- a/src/recovery.c
+++ b/src/recovery.c
@@ -108,7 +108,9 @@ int recovery_check_mode() {
 	irecv_client_t recovery = NULL;
 	irecv_error_t recovery_error = IRECV_E_SUCCESS;
 
-	recovery_error = irecv_open(&recovery);
+	irecv_init();
+	recovery_error=irecv_open(&recovery);
+
 	if (recovery_error != IRECV_E_SUCCESS) {
 		return -1;
 	}
@@ -120,13 +122,15 @@ int recovery_check_mode() {
 
 	irecv_close(recovery);
 	recovery = NULL;
+
 	return 0;
 }
 
 static int recovery_enable_autoboot(struct idevicerestore_client_t* client) {
 	irecv_error_t recovery_error = IRECV_E_SUCCESS;
 
-	recovery_error = irecv_setenv(client->recovery->client, "auto-boot", "true");
+	//recovery_error = irecv_setenv(client->recovery->client, "auto-boot", "true");
+	recovery_error = irecv_send_command(client->recovery->client, "setenv auto-boot true");
 	if (recovery_error != IRECV_E_SUCCESS) {
 		error("ERROR: Unable to set auto-boot environmental variable\n");
 		return -1;
@@ -236,7 +240,8 @@ int recovery_send_component(struct idevicerestore_client_t* client, plist_t buil
 
 	info("Sending %s (%d bytes)...\n", component, size);
 
-	error = irecv_send_buffer(client->recovery->client, data, size);
+	// FIXME: Did I do this right????
+	error = irecv_send_buffer(client->recovery->client, data, size, 0);
 	free(path);
 	if (error != IRECV_E_SUCCESS) {
 		error("ERROR: Unable to send %s component: %s\n", component, irecv_strerror(error));
diff --git a/src/recovery.h b/src/recovery.h
index b7cc0e4..278bd93 100644
--- a/src/recovery.h
+++ b/src/recovery.h
@@ -31,8 +31,10 @@ extern "C" {
 
 #include "common.h"
 
-struct irecv_client;
-typedef struct irecv_client* irecv_client_t;
+#include "libirecovery.h"
+
+//struct irecv_client;
+//typedef struct irecv_client* irecv_client_t;
 struct recovery_client_t {
 	irecv_client_t client;
 	const char* ipsw;
diff --git a/src/tss.c b/src/tss.c
index 0d4422c..eac3d32 100644
--- a/src/tss.c
+++ b/src/tss.c
@@ -171,8 +171,11 @@ plist_t tss_send_request(plist_t tss_request) {
 		curl_easy_setopt(handle, CURLOPT_POSTFIELDS, request);
 		curl_easy_setopt(handle, CURLOPT_USERAGENT, "InetURL/1.0");
 		curl_easy_setopt(handle, CURLOPT_POSTFIELDSIZE, strlen(request));
-		curl_easy_setopt(handle, CURLOPT_URL, "http://cydia.saurik.com/TSS/controller?action=2");
-		//curl_easy_setopt(handle, CURLOPT_URL, "http://gs.apple.com/TSS/controller?action=2");
+		if (use_apple_server==0) {
+			curl_easy_setopt(handle, CURLOPT_URL, "http://cydia.saurik.com/TSS/controller?action=2");
+		} else {
+			curl_easy_setopt(handle, CURLOPT_URL, "http://gs.apple.com/TSS/controller?action=2");
+		}
 
 		curl_easy_perform(handle);
 		curl_slist_free_all(header);
-- 
cgit v1.1-32-gdbae