Merge pull request #35 from wjywbs/remove-app

Fixes #35 - Add uninstall feature.
diff --git a/MobileDevice.h b/MobileDevice.h
index 837cfd4..e078a44 100644
--- a/MobileDevice.h
+++ b/MobileDevice.h
@@ -453,6 +453,8 @@
 mach_error_t AMDeviceInstallApplication(service_conn_t socket, CFStringRef path, CFDictionaryRef options, am_device_install_application_callback callback, void *user);
 mach_error_t AMDeviceTransferApplication(service_conn_t socket, CFStringRef path, CFDictionaryRef options, am_device_install_application_callback callbackj, void *user);
 
+int AMDeviceSecureUninstallApplication(int unknown0, struct am_device *device, CFStringRef bundle_id, int unknown1, void *callback, int callback_arg);
+
 /* ----------------------------------------------------------------------------
  *   Semi-private routines
  * ------------------------------------------------------------------------- */
diff --git a/ios-deploy.c b/ios-deploy.c
index cadaaed..ebf80fc 100644
--- a/ios-deploy.c
+++ b/ios-deploy.c
@@ -125,7 +125,7 @@
 int AMDeviceMountImage(AMDeviceRef device, CFStringRef image, CFDictionaryRef options, void *callback, int cbarg);
 mach_error_t AMDeviceLookupApplications(AMDeviceRef device, CFDictionaryRef options, CFDictionaryRef *result);
 
-bool found_device = false, debug = false, verbose = false, unbuffered = false, nostart = false, detect_only = false, install = true;
+bool found_device = false, debug = false, verbose = false, unbuffered = false, nostart = false, detect_only = false, install = true, uninstall = false;
 bool interactive = true;
 char *app_path = NULL;
 char *device_id = NULL;
@@ -813,6 +813,42 @@
     }
 }
 
+CFStringRef get_bundle_id(CFURLRef app_url)
+{
+    if (app_url == NULL)
+        return NULL;
+
+    CFURLRef url = CFURLCreateCopyAppendingPathComponent(NULL, app_url, CFSTR("Info.plist"), false);
+
+    if (url == NULL)
+        return NULL;
+
+    CFReadStreamRef stream = CFReadStreamCreateWithFile(NULL, url);
+    CFRelease(url);
+
+    if (stream == NULL)
+        return NULL;
+
+    CFPropertyListRef plist = NULL;
+    if (CFReadStreamOpen(stream) == TRUE) {
+        plist = CFPropertyListCreateWithStream(NULL, stream, 0,
+                                               kCFPropertyListImmutable, NULL, NULL);
+    }
+    CFReadStreamClose(stream);
+    CFRelease(stream);
+
+    if (plist == NULL)
+        return NULL;
+
+    const void *value = CFDictionaryGetValue(plist, CFSTR("CFBundleIdentifier"));
+    CFStringRef bundle_id = NULL;
+    if (value != NULL)
+        bundle_id = CFRetain(value);
+
+    CFRelease(plist);
+    return bundle_id;
+}
+
 void handle_device(AMDeviceRef device) {
     if (found_device) return; // handle one device only
     CFStringRef found_device_id = AMDeviceCopyDeviceIdentifier(device);
@@ -841,7 +877,29 @@
 
     CFRelease(relative_url);
 
+    if (uninstall) {
+        printf("------ Uninstall phase ------\n");
+
+        CFStringRef bundle_id = get_bundle_id(url);
+        if (bundle_id == NULL) {
+            printf("Error: Unable to get bundle id from package %s\n Uninstall failed\n", app_path);
+        } else {
+            AMDeviceConnect(device);
+            assert(AMDeviceIsPaired(device));
+            assert(AMDeviceValidatePairing(device) == 0);
+            assert(AMDeviceStartSession(device) == 0);
+
+            assert(AMDeviceSecureUninstallApplication(0, device, bundle_id, 0, NULL, 0) == 0);
+
+            assert(AMDeviceStopSession(device) == 0);
+            assert(AMDeviceDisconnect(device) == 0);
+
+            printf("[ OK ] Uninstalled package with bundle id %s\n", CFStringGetCStringPtr(bundle_id, CFStringGetSystemEncoding()));
+        }
+    }
+
     if(install) {
+        printf("------ Install phase ------\n");
         printf("[  0%%] Found device (%s), beginning install\n", CFStringGetCStringPtr(found_device_id, CFStringGetSystemEncoding()));
 
         AMDeviceConnect(device);
@@ -929,6 +987,7 @@
         "  -v, --verbose                enable verbose output\n"
         "  -m, --noinstall              directly start debugging without app install (-d not required)\n"
         "  -p, --port <number>          port used for device, default: 12345 \n"
+        "  -r, --uninstall              uninstall the app before install (do not use with -m; app cache and data are cleared) \n"
         "  -V, --version                print the executable version \n",
         app);
 }
@@ -953,11 +1012,12 @@
         { "version", no_argument, NULL, 'V' },
         { "noinstall", no_argument, NULL, 'm' },
         { "port", required_argument, NULL, 'p' },
+        { "uninstall", no_argument, NULL, 'r' },
         { NULL, 0, NULL, 0 },
     };
     char ch;
 
-    while ((ch = getopt_long(argc, argv, "VmcdvunIi:b:a:t:g:x:p:", longopts, NULL)) != -1)
+    while ((ch = getopt_long(argc, argv, "VmcdvunrIi:b:a:t:g:x:p:", longopts, NULL)) != -1)
     {
         switch (ch) {
         case 'm':
@@ -1000,6 +1060,9 @@
         case 'p':
             port = atoi(optarg);
             break;
+        case 'r':
+            uninstall = 1;
+            break;
         default:
             usage(argv[0]);
             return exitcode_error;
@@ -1020,10 +1083,6 @@
         timeout = 5;
     }
 
-    if (!detect_only) {
-        printf("------ Install phase ------\n");
-    }
-
     if (app_path) {
         assert(access(app_path, F_OK) == 0);
     }