Example #1
0
DeviceStatus Device::readFileWithAfc(string path, afc_client_t afc, char *&buffer, int &bytesRead) {
    buffer = NULL;
    bytesRead = 0;
    FileType type = this->getFileTypeWithAfc(afc, path);
    if (type == TypeInvalid) {
        return StatusError;
    } else if (type == TypeDirectory) {
        return StatusError;
    }
    int64_t length = this->getFileLengthWithAfc(path, afc);
    if (length == 0) {
        return StatusOK;
    }
    uint64_t handle = 0;
    if (afc_file_open(afc, path.c_str(), afc_file_mode_t::AFC_FOPEN_RDONLY, &handle) != AFC_E_SUCCESS) {
        return StatusError;
    }
    buffer = new char[length];
    if (buffer == NULL) {
        afc_file_close(afc, handle);
        return StatusError;
    }
    uint32_t tBytesRead;
    for (; bytesRead < length; bytesRead += tBytesRead) {
        if (afc_file_read(afc, handle, buffer + bytesRead, 0x400000, &tBytesRead) != AFC_E_SUCCESS) {
            bytesRead += tBytesRead;
            break;
        }
    }
    afc_file_close(afc, handle);
    return StatusOK;
}
Example #2
0
static afc_error_t cmd_cat(int argc, const char *argv[])
{
	afc_error_t result;
	char *path;
	uint64_t handle, size;
	char **infolist;
	uint32_t readsize;
	char buffer[0x1000];

	if (argc != 1) {
		warnx("usage: cat <file>");
		return -1;
	}

	if ((path = build_absolute_path(argv[0])) == NULL)
		return AFC_E_INTERNAL_ERROR;

	result = afc_get_file_info(afc, path, &infolist);
	if (result != AFC_E_SUCCESS) {
		afc_warn(result, "%s", argv[0]);
		free(path);
		return result;
	}
	size = atoll(infolist[1]);
	for (int i = 0; infolist[i] != NULL; i++)
		free(infolist[i]);
	free(infolist);

	result = afc_file_open(afc, path, AFC_FOPEN_RDONLY, &handle);
	if (result != AFC_E_SUCCESS) {
		afc_warn(result, "%s", argv[0]);
		free(path);
		return result;
	}

	for (int i = 0; i < size; ) {
		result = afc_file_read(afc, handle, buffer, sizeof(buffer), &readsize);
		if (result != AFC_E_SUCCESS) {
			afc_warn(result, "%s", argv[0]);
			afc_file_close(afc, handle);
			free(path);
			return result;
		}
		fwrite(buffer, 1, readsize, stdout);
		i += readsize;
	}

	afc_file_close(afc, handle);
	free(path);

	return AFC_E_SUCCESS;
}
Example #3
0
static void check_afc(gpointer data)
{
    //prepare a buffer
    unsigned int buffersize = BUFFER_SIZE * sizeof(unsigned int);
    int *buf = (int *) malloc(buffersize);
    int *buf2 = (int *) malloc(buffersize);
    unsigned int bytes = 0;
    uint64_t position = 0;

    //fill buffer
    int i = 0;
    for (i = 0; i < BUFFER_SIZE; i++) {
        buf[i] = ((param *) data)->id * i;
    }

    //now  writes buffer on device
    uint64_t file = 0;
    char path[50];
    sprintf(path, "/Buf%i", ((param *) data)->id);
    afc_file_open(((param *) data)->afc, path, AFC_FOPEN_RW, &file);
    afc_file_write(((param *) data)->afc, file, (char *) buf, buffersize, &bytes);
    afc_file_close(((param *) data)->afc, file);
    file = 0;
    if (bytes != buffersize)
        printf("Write operation failed\n");

    //now read it
    bytes = 0;
    afc_file_open(((param *) data)->afc, path, AFC_FOPEN_RDONLY, &file);
    afc_file_read(((param *) data)->afc, file, (char *) buf2, buffersize/2, &bytes);
    afc_file_read(((param *) data)->afc, file, (char *) buf2 + (buffersize/2), buffersize/2, &bytes);
    if(AFC_E_SUCCESS != afc_file_tell(((param *) data)->afc, file, &position))
        printf("Tell operation failed\n");
    afc_file_close(((param *) data)->afc, file);
    if (position != buffersize)
        printf("Read operation failed\n");

    //compare buffers
    for (i = 0; i < BUFFER_SIZE; i++) {
        if (buf[i] != buf2[i]) {
            printf("Buffers are differents, stream corrupted\n");
            break;
        }
    }

    //cleanup
    afc_remove_path(((param *) data)->afc, path);
    g_thread_exit(0);
}
Example #4
0
int afc_send_file(afc_client_t afc, const char* local, const char* remote) {
    FILE* fd = NULL;
    uint64_t fh = 0;
    afc_error_t err = 0;
    unsigned int got = 0;
    unsigned int gave = 0;
    unsigned char buffer[0x800];

    fd = fopen(local, "rb");
    if (fd != NULL ) {
        err = afc_file_open(afc, remote, AFC_FOPEN_WR, &fh);
        if (err == AFC_E_SUCCESS) {

            while (!feof(fd)) {
                memset(buffer, '\0', sizeof(buffer));
                got = fread(buffer, 1, sizeof(buffer), fd);
                if (got > 0) {
                    afc_file_write(afc, fh, (const char*) buffer, got, &gave);
                    if (gave != got) {
                        printf("Error!!\n");
                        break;
                    }
                }
            }

            afc_file_close(afc, fh);
        }
        fclose(fd);
    } else
        return -1;
    return 0;
}
Example #5
0
uint64_t Device::getFileLengthWithAfc(string path, afc_client_t afc) {
    uint64_t handle = 0;
    if (afc_file_open(afc, path.c_str(), afc_file_mode_t::AFC_FOPEN_RDONLY, &handle) != AFC_E_SUCCESS) {
        return 0;
    }
    if (afc_file_seek(afc, handle, 0, SEEK_END) != AFC_E_SUCCESS) {
        afc_file_close(afc, handle);
        return 0;
    }
    uint64_t length = 0;
    if (afc_file_tell(afc, handle, &length) != AFC_E_SUCCESS) {
        afc_file_close(afc, handle);
        return 0;
    }
    afc_file_close(afc, handle);
    return length;
}
Example #6
0
static int ifuse_release(const char *path, struct fuse_file_info *fi)
{
	afc_client_t afc = fuse_get_context()->private_data;

	afc_file_close(afc, fi->fh);

	return 0;
}
static int afc_upload_file(afc_client_t afc, const char* filename, const char* dstfn)
{
	FILE *f = NULL;
	uint64_t af = 0;
	char buf[1048576];

	f = fopen(filename, "rb");
	if (!f) {
		fprintf(stderr, "fopen: %s: %s\n", appid, strerror(errno));
		return -1;
	}

	if ((afc_file_open(afc, dstfn, AFC_FOPEN_WRONLY, &af) != AFC_E_SUCCESS) || !af) {
		fclose(f);
		fprintf(stderr, "afc_file_open on '%s' failed!\n", dstfn);
		return -1;
	}

	size_t amount = 0;
	do {
		amount = fread(buf, 1, sizeof(buf), f);
		if (amount > 0) {
			uint32_t written, total = 0;
			while (total < amount) {
				written = 0;
				afc_error_t aerr = afc_file_write(afc, af, buf, amount, &written);
				if (aerr != AFC_E_SUCCESS) {
					fprintf(stderr, "AFC Write error: %d\n", aerr);
					break;
				}
				total += written;
			}
			if (total != amount) {
				fprintf(stderr, "Error: wrote only %d of %zu\n", total, amount);
				afc_file_close(afc, af);
				fclose(f);
				return -1;
			}
		}
	} while (amount > 0);

	afc_file_close(afc, af);
	fclose(f);

	return 0;
}
Example #8
0
int itdb_iphone_stop_sync(void *sync_ctx)
{
    lockdownd_client_t client = NULL;
    itdbprep_t prepdata = sync_ctx;

    printf("libitdbprep: %s called\n", __func__);

    if (!prepdata) {
	printf("%s called but prepdata is NULL!\n", __func__);
	return -1;
    }

    if (!prepdata->afc) {
	printf("%s called but prepdata->afc is NULL!\n", __func__);
    } else {
	/* remove .status-com.apple.itdbprep.command.runPostProcess */
	if (afc_remove_path(prepdata->afc, "/iTunes_Control/iTunes/iTunes Library.itlp/DBTemp/.status-com.apple.itdprep.command.runPostProcess") != IDEVICE_E_SUCCESS) {
	    fprintf(stderr, "Could not delete '.status-com.apple.itdprep.command.runPostProcess'\n");
	}
	/* remove ddd.itdbm */
	if (afc_remove_path(prepdata->afc, "/iTunes_Control/iTunes/iTunes Library.itlp/DBTemp/ddd.itdbm") != IDEVICE_E_SUCCESS) {
	    fprintf(stderr, "Could not delete 'ddd.itdbm'\n");
	}
	if (prepdata->lockfile) {
	    afc_file_lock(prepdata->afc, prepdata->lockfile, AFC_LOCK_UN);
	    afc_file_close(prepdata->afc, prepdata->lockfile);
	    prepdata->lockfile = 0;
	} else {
	    printf("%s called but lockfile is 0\n", __func__);
	}
	afc_client_free(prepdata->afc);
	prepdata->afc = NULL;
    }

    if (LOCKDOWN_E_SUCCESS != lockdownd_client_new_with_handshake(prepdata->device, &client, "libgpod")) {
	fprintf(stderr, "Error: Could not establish lockdownd connection!\n");
	goto leave;
    }

    if(itdb_iphone_post_notification(prepdata->device, client, NP_SYNC_DID_FINISH)) {
	fprintf(stderr, "failed to post syncDidFinish\n");
    }
    printf("%s: posted syncDidFinish\n", __func__);

    lockdownd_client_free(client);

leave:
    idevice_free(prepdata->device);

    g_free(prepdata);

    return 0;

}
Example #9
0
FileType Device::getFileTypeWithAfc(afc_client_t afc, string path) {
    uint64_t handle = 0;
    if (afc_file_open(afc, path.c_str(), afc_file_mode_t::AFC_FOPEN_RDONLY, &handle) != AFC_E_SUCCESS) {
        return TypeInvalid;
    }
    afc_file_close(afc, handle);
    char **dir = NULL;
    if (afc_read_directory(afc, path.c_str(), &dir) != AFC_E_SUCCESS) {
        return TypeFile;
    }
    afc_dictionary_free(dir);
    return TypeDirectory;
}
Example #10
0
static void gst_afc_src_finalize(GObject* object) {
  GstAfcSrc* self = GST_AFCSRC(object);
  free(self->location_);
  free(self->uuid_);
  free(self->path_);
  free(self->buffer_);

  if (self->file_handle_)
    afc_file_close(self->afc_, self->file_handle_);

  if (self->afc_)
    afc_client_free(self->afc_);

  if (self->device_)
    idevice_free(self->device_);


  G_OBJECT_CLASS(parent_class)->finalize(object);
}
Example #11
0
int itdb_iphone_start_sync(Itdb_Device *device, void **prepdata)
{
    int res = 0;
    int sync_starting = 0;
    itdbprep_t pdata_loc = NULL;
    const char *uuid;
    lockdownd_client_t client = NULL;
#ifdef HAVE_LIBIMOBILEDEVICE_1_1_5
    lockdownd_service_descriptor_t service = NULL;
#else
    uint16_t afcport = 0;
#endif
    int i;

    uuid = itdb_device_get_uuid (device);

    if (!uuid) {
	fprintf(stderr, "Couldn't find get device UUID itdbprep processing won't work!");
	return -ENODEV;
    }

    printf("libitdbprep: %s called with uuid=%s\n", __func__, uuid);

    *prepdata = NULL;

    pdata_loc = g_new0 (struct itdbprep_int, 1);
    res = idevice_new(&pdata_loc->device, uuid);
    if (IDEVICE_E_SUCCESS != res) {
	fprintf(stderr, "No iPhone found, is it plugged in?\n");
	res = -ENODEV;
	goto leave_with_err;
    }

    if (LOCKDOWN_E_SUCCESS != lockdownd_client_new_with_handshake(pdata_loc->device, &client, "libgpod")) {
	fprintf(stderr, "Error: Could not establish lockdownd connection!\n");
	res = -1;
	goto leave_with_err;
    }

#ifdef HAVE_LIBIMOBILEDEVICE_1_1_5
    lockdownd_start_service(client, "com.apple.afc", &service);
    if (!service || !service->port) {
	fprintf(stderr, "Error: Could not start AFC service!\n");
	res = -1;
	goto leave_with_err;
    }
    afc_client_new(pdata_loc->device, service, &pdata_loc->afc);
#else
    lockdownd_start_service(client, "com.apple.afc", &afcport);
    if (!afcport) {
	fprintf(stderr, "Error: Could not start AFC service!\n");
	res = -1;
	goto leave_with_err;
    }
    afc_client_new(pdata_loc->device, afcport, &pdata_loc->afc);
#endif
    if (!pdata_loc->afc) {
	fprintf(stderr, "Error: Could not start AFC client!\n");
	res = -1;
	goto leave_with_err;
    }

    if (itdb_iphone_post_notification(pdata_loc->device, client, NP_SYNC_WILL_START)) {
	fprintf(stderr, "could not post syncWillStart notification!\n");
	res = -1;
	goto leave_with_err;
    }
    printf("%s: posted syncWillStart\n", __func__);
    sync_starting = 1;

    /* OPEN AND LOCK /com.apple.itunes.lock_sync */
    afc_file_open(pdata_loc->afc, "/com.apple.itunes.lock_sync", AFC_FOPEN_RW, &pdata_loc->lockfile);

    if (!pdata_loc->lockfile) {
	fprintf(stderr, "could not open lockfile\n");
	res = -1;
	goto leave_with_err;
    }

    if (itdb_iphone_post_notification(pdata_loc->device, client, "com.apple.itunes-mobdev.syncLockRequest")) {
	fprintf(stderr, "could not post syncLockRequest\n");
	res = -1;
	goto leave_with_err;
    }
    printf("%s: posted syncLockRequest\n", __func__);

    for (i=0; i<LOCK_ATTEMPTS; i++) {
	fprintf(stderr, "Locking for sync, attempt %d...\n", i);
	res = afc_file_lock(pdata_loc->afc, pdata_loc->lockfile, AFC_LOCK_EX);
	if (res == AFC_E_SUCCESS) {
	    break;
	} else if (res == AFC_E_OP_WOULD_BLOCK) {
	    usleep(LOCK_WAIT);
	    continue;
	} else {
	    fprintf(stderr, "ERROR: could not lock file! error code: %d\n", res);
	    res = -1;
	    goto leave_with_err;
	}
    }
    if (i == LOCK_ATTEMPTS) {
	fprintf(stderr, "ERROR: timeout while locking for sync\n");
	res = -1;
	goto leave_with_err;
    }

    if (itdb_iphone_post_notification(pdata_loc->device, client, NP_SYNC_DID_START)) {
	fprintf(stderr, "could not post syncDidStart\n");
	res = -1;
	goto leave_with_err;
    }
    printf("%s: posted syncDidStart\n", __func__);

    lockdownd_client_free(client);
    client = NULL;

    *prepdata = pdata_loc;

    return 0;

leave_with_err:
    if (client && sync_starting) {
	itdb_iphone_post_notification(pdata_loc->device, client, "com.apple.itunes-mobdev.syncFailedToStart");
	printf("%s: posted syncFailedToStart\n", __func__);
    }

    if (pdata_loc) {
	if (pdata_loc->afc) {
	    if (pdata_loc->lockfile) {
		afc_file_lock(pdata_loc->afc, pdata_loc->lockfile, AFC_LOCK_UN);
		afc_file_close(pdata_loc->afc, pdata_loc->lockfile);
		pdata_loc->lockfile = 0;
	    }
	    afc_client_free(pdata_loc->afc);
	    pdata_loc->afc = NULL;
	}
	if (pdata_loc->device) {
	    idevice_free(pdata_loc->device);
	    pdata_loc->device = NULL;
	}
	g_free(pdata_loc);
	pdata_loc = NULL;
    }

    if (client) {
	lockdownd_client_free(client);
	client = NULL;
    }

    *prepdata = NULL;

    return res;
}
Example #12
0
G_GNUC_INTERNAL gboolean
iphone_write_sysinfo_extended (const char *uuid, const char *xml)
{
	lockdownd_client_t client = NULL;
	idevice_t device = NULL;
	afc_client_t afc = NULL;
	idevice_error_t ret = IDEVICE_E_UNKNOWN_ERROR;
	afc_error_t afc_ret;
	uint16_t afcport = 0;
	uint64_t handle;
	uint32_t bytes_written;
	const char device_dir[] = "/iTunes_Control/Device";
	const char sysinfoextended_path[] = "/iTunes_Control/Device/SysInfoExtended";

	ret = idevice_new(&device, uuid);
	if (IDEVICE_E_SUCCESS != ret) {
		printf("No device found with uuid %s, is it plugged in?\n", uuid);
		return FALSE;
	}

	if (LOCKDOWN_E_SUCCESS != lockdownd_client_new_with_handshake(device, &client, "libgpod")) {
		idevice_free(device);
		return FALSE;
	}

	if (LOCKDOWN_E_SUCCESS != lockdownd_start_service(client, "com.apple.afc", &afcport)) {
		lockdownd_client_free(client);
		idevice_free(device);
		return FALSE;
	}
	g_assert (afcport != 0);
	if (AFC_E_SUCCESS != afc_client_new(device, afcport, &afc)) {
		lockdownd_client_free(client);
		idevice_free(device);
		return FALSE;
	}
	afc_ret = afc_make_directory(afc, device_dir);
	if ((AFC_E_SUCCESS != afc_ret) && (AFC_E_OBJECT_EXISTS != afc_ret)) {
		afc_client_free(afc);
		lockdownd_client_free(client);
		idevice_free(device);
		return FALSE;
	}
	if (AFC_E_SUCCESS != afc_file_open(afc, sysinfoextended_path,
					   AFC_FOPEN_WRONLY, &handle)) {
		afc_client_free(afc);
		lockdownd_client_free(client);
		idevice_free(device);
		return FALSE;
	}

	if (AFC_E_SUCCESS != afc_file_write(afc, handle, xml, strlen(xml), &bytes_written)) {
		afc_file_close(afc, handle);
		afc_client_free(afc);
		lockdownd_client_free(client);
		idevice_free(device);
		return FALSE;
	}

	afc_file_close(afc, handle);
	afc_client_free(afc);
	lockdownd_client_free(client);
	idevice_free(device);

	return TRUE;
}
int main(int argc, char *argv[])
{
	unsigned int bytes = 0;
	uint16_t i = 0;
	lockdownd_service_descriptor_t service = NULL;
	lockdownd_client_t client = NULL;
	idevice_t phone = NULL;
	uint64_t lockfile = 0;
	np_client_t gnp = NULL;

	if (argc > 1 && !strcasecmp(argv[1], "--debug")) {
		idevice_set_debug_level(1);
	} else {
		idevice_set_debug_level(0);
	}

	if (IDEVICE_E_SUCCESS != idevice_new(&phone, NULL)) {
		printf("No device found, is it plugged in?\n");
		return -1;
	}

	char *udid = NULL;
	if (IDEVICE_E_SUCCESS == idevice_get_udid(phone, &udid)) {
		printf("DeviceUniqueID : %s\n", udid);
	}
	if (udid)
		free(udid);

	if (LOCKDOWN_E_SUCCESS != lockdownd_client_new_with_handshake(phone, &client, "ideviceclient")) {
		idevice_free(phone);
		printf("Exiting.\n");
		return -1;
	}

	char *nnn = NULL;
	if (LOCKDOWN_E_SUCCESS == lockdownd_get_device_name(client, &nnn)) {
		printf("DeviceName : %s\n", nnn);
		free(nnn);
	}

	lockdownd_start_service(client, "com.apple.afc", &service);

	if (service && service->port) {
		afc_client_t afc = NULL;
		afc_client_new(phone, service, &afc);

		if (afc) {
			service->port = 0;
			service->ssl_enabled = 0;
			lockdownd_start_service(client, "com.apple.mobile.notification_proxy", &service);
			if (service->port) {
				printf("Notification Proxy started.\n");
				np_client_new(phone, service, &gnp);
			} else {
				printf("ERROR: Notification proxy could not be started.\n");
			}

			if (gnp) {
				const char *nspec[5] = {
					NP_SYNC_CANCEL_REQUEST,
					NP_SYNC_SUSPEND_REQUEST,
					NP_SYNC_RESUME_REQUEST,
					NP_ITDBPREP_DID_END,
					NULL
				};
				np_observe_notifications(gnp, nspec);
				np_set_notify_callback(gnp, notifier, NULL);
			}

			perform_notification(phone, client, NP_SYNC_WILL_START);

			afc_file_open(afc, "/com.apple.itunes.lock_sync", AFC_FOPEN_RW, &lockfile);
			if (lockfile) {
				printf("locking file\n");
				afc_file_lock(afc, lockfile, AFC_LOCK_EX);

				perform_notification(phone, client, NP_SYNC_DID_START);
			}

			char **dirs = NULL;
			afc_read_directory(afc, "/eafaedf", &dirs);
			if (!dirs)
				afc_read_directory(afc, "/", &dirs);
			printf("Directory time.\n");
			for (i = 0; dirs[i]; i++) {
				printf("/%s\n", dirs[i]);
				free(dirs[i]);
			}
			if (dirs)
				free(dirs);

			dirs = NULL;
			afc_get_device_info(afc, &dirs);
			if (dirs) {
				for (i = 0; dirs[i]; i += 2) {
					printf("%s: %s\n", dirs[i], dirs[i + 1]);
					free(dirs[i]);
				}
				free(dirs);
			}

			uint64_t my_file = 0;
			char **info = NULL;
			uint64_t fsize = 0;
			if (AFC_E_SUCCESS == afc_get_file_info(afc, "/readme.libimobiledevice.fx", &info) && info) {
				for (i = 0; info[i]; i += 2) {
					printf("%s: %s\n", info[i], info[i+1]);
					if (!strcmp(info[i], "st_size")) {
						fsize = atoll(info[i+1]);
					}
				}
			}

			if (AFC_E_SUCCESS ==
				afc_file_open(afc, "/readme.libimobiledevice.fx", AFC_FOPEN_RDONLY, &my_file) && my_file) {
				printf("A file size: %llu\n", (long long)fsize);
				char *file_data = (char *) malloc(sizeof(char) * fsize);
				afc_file_read(afc, my_file, file_data, fsize, &bytes);
				if (bytes > 0) {
					printf("The file's data:\n");
					fwrite(file_data, 1, bytes, stdout);
				}
				printf("\nClosing my file.\n");
				afc_file_close(afc, my_file);
				free(file_data);
			} else
				printf("couldn't open a file\n");

			afc_file_open(afc, "/readme.libimobiledevice.fx", AFC_FOPEN_WR, &my_file);
			if (my_file) {
				char *outdatafile = strdup("this is a bitchin text file\n");
				afc_file_write(afc, my_file, outdatafile, strlen(outdatafile), &bytes);
				free(outdatafile);
				if (bytes > 0)
					printf("Wrote a surprise. ;)\n");
				else
					printf("I wanted to write a surprise, but... :(\n");
				afc_file_close(afc, my_file);
			}
			printf("Deleting a file...\n");
			bytes = afc_remove_path(afc, "/delme");
			if (bytes)
				printf("Success.\n");
			else
				printf("Failure. (expected unless you have a /delme file on your phone)\n");

			printf("Renaming a file...\n");
			bytes = afc_rename_path(afc, "/renme", "/renme2");
			if (bytes > 0)
				printf("Success.\n");
			else
				printf("Failure. (expected unless you have a /renme file on your phone)\n");

			printf("Seek & read\n");
			afc_file_open(afc, "/readme.libimobiledevice.fx", AFC_FOPEN_RDONLY, &my_file);
			if (AFC_E_SUCCESS != afc_file_seek(afc, my_file, 5, SEEK_CUR))
				printf("WARN: SEEK DID NOT WORK\n");
			char *threeletterword = (char *) malloc(sizeof(char) * 5);
			afc_file_read(afc, my_file, threeletterword, 3, &bytes);
			threeletterword[3] = '\0';
			if (bytes > 0)
				printf("Result: %s\n", threeletterword);
			else
				printf("Couldn't read!\n");
			free(threeletterword);
			afc_file_close(afc, my_file);
		}

		if (gnp && lockfile) {
			printf("XXX sleeping\n");
			sleep(5);

			printf("XXX unlocking file\n");
			afc_file_lock(afc, lockfile, AFC_LOCK_UN);

			printf("XXX closing file\n");
			afc_file_close(afc, lockfile);

			printf("XXX sleeping\n");
			sleep(5);
			//perform_notification(phone, client, NP_SYNC_DID_FINISH);
		}

		if (gnp) {
			np_client_free(gnp);
			gnp = NULL;
		}

		afc_client_free(afc);

		lockdownd_service_descriptor_free(service);
		service = NULL;
	} else {
		printf("Start service failure.\n");
	}

	printf("All done.\n");

	lockdownd_client_free(client);
	idevice_free(phone);

	return 0;
}
Example #14
0
int main(int argc, char **argv)
{
	idevice_t dev = NULL;
	lockdownd_client_t client = NULL;
	house_arrest_client_t hac = NULL;
	house_arrest_error_t res;
	int i;
	char *udid = NULL;
	const char *appid = NULL;
	int test_file_io = 0;

	/* parse cmdline args */
	for (i = 1; i < argc; i++) {
		if (!strcmp(argv[i], "-d") || !strcmp(argv[i], "--debug")) {
			idevice_set_debug_level(1);
			continue;
		}
		else if (!strcmp(argv[i], "-u") || !strcmp(argv[i], "--udid")) {
			i++;
			if (!argv[i] || (strlen(argv[i]) != 40)) {
				print_usage(argc, argv);
				return 0;
			}
			udid = strdup(argv[i]);
			continue;
		}
		else if (!strcmp(argv[i], "-t") || !strcmp(argv[i], "--test")) {
			test_file_io = 1;
			continue;
		}
		else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
			print_usage(argc, argv);
			return 0;
		}
		else {
			appid = argv[i];
			break;
		}
	}

	if (!appid) {
		print_usage(argc, argv);
		return 0;
	}

	if (idevice_new(&dev, udid) != IDEVICE_E_SUCCESS) {
		printf("no device connected?!\n");
		goto leave_cleanup;
	}

	if (lockdownd_client_new_with_handshake(dev, &client, NULL) != LOCKDOWN_E_SUCCESS) {
		printf("could not connect to lockdownd!\n");
		goto leave_cleanup;
	}

	uint16_t port = 0;
	if (lockdownd_start_service(client, "com.apple.mobile.house_arrest", &port) != LOCKDOWN_E_SUCCESS) {
		printf("could not start house_arrest service!\n");
		goto leave_cleanup;
	}

	if (client) {
		lockdownd_client_free(client);
		client = NULL;
	}

	if (house_arrest_client_new(dev, port, &hac) != HOUSE_ARREST_E_SUCCESS) {
		printf("could not connect to house_arrest service!\n");
		goto leave_cleanup;
	}

	res = house_arrest_send_command(hac, "VendDocuments", appid);
	if (res != HOUSE_ARREST_E_SUCCESS) {
		printf("error %d when trying to get VendDocuments\n", res);
		goto leave_cleanup;
	}

	plist_t dict = NULL;
	if (house_arrest_get_result(hac, &dict) != HOUSE_ARREST_E_SUCCESS) {
		if (house_arrest_get_result(hac, &dict) != HOUSE_ARREST_E_SUCCESS) {
			printf("hmmm....\n");
			goto leave_cleanup;
		}
	}

	plist_t node = plist_dict_get_item(dict, "Error");
	if (node) {
		char *str = NULL;
		plist_get_string_val(node, &str);
		printf("Error: %s\n", str);
		if (str) free(str);
		plist_free(dict);
		dict = NULL;
		goto leave_cleanup;
	}
	node = plist_dict_get_item(dict, "Status");
	if (node) {
		char *str = NULL;
		plist_get_string_val(node, &str);
		if (str && (strcmp(str, "Complete") != 0)) {
			printf("Warning: Status is not 'Complete' but '%s'\n", str);
		}
		if (str) free(str);
		plist_free(dict);
		dict = NULL;
	}
	if (dict) {
		plist_free(dict);
	}

	afc_client_t afc = NULL;
	afc_error_t ae = afc_client_new_from_house_arrest_client(hac, &afc);
	if (ae != AFC_E_SUCCESS) {
		printf("afc error %d\n", ae);
	}
	if (ae == AFC_E_SUCCESS) {
		char **list = NULL;
		afc_read_directory(afc, "/", &list);
		printf("Directory contents:\n");
		if (list) {
			while (list[0]) {
				if (strcmp(list[0], ".") && strcmp(list[0], "..")) {
					puts(list[0]);
				}
				list++;
			}
		}

		if (test_file_io) {
			uint64_t tf = 0;
			printf("\n==== Performing file tests ====\n");
			printf("Opening file 'foobar' for writing: ");
			if (afc_file_open(afc, "/foobar", AFC_FOPEN_RW, &tf) == AFC_E_SUCCESS) {
				uint32_t wb = 0;
				printf("OK\n");

				printf("Writing to file: ");
				if (afc_file_write(afc, tf, "test\r\n", 6, &wb) != AFC_E_SUCCESS) {
					printf("ERROR\n");
				} else {
					printf("OK\n");
				}
				afc_file_close(afc, tf);
				printf("Deleting file 'foobar': ");
				if (afc_remove_path(afc, "/foobar") == AFC_E_SUCCESS) {
					printf("OK\n");
				} else {
					printf("ERROR\n");
				}
			} else {
				printf("ERROR\n");
			}
		}
		afc_client_free(afc);
	} else {
		printf("failed to connect to afc service, error %d\n", ae);
	}

leave_cleanup:
	if (hac) {
		house_arrest_client_free(hac);
	}
	if (client) {
		lockdownd_client_free(client);
	}
	if (dev) {
		idevice_free(dev);
	}

	return 0;
}
int main(int argc, char **argv)
{
	idevice_t device = NULL;
	lockdownd_client_t lckd = NULL;
	mobile_image_mounter_client_t mim = NULL;
	afc_client_t afc = NULL;
	lockdownd_service_descriptor_t service = NULL;
	int res = -1;
	char *image_path = NULL;
	char *image_sig_path = NULL;

	parse_opts(argc, argv);

	argc -= optind;
	argv += optind;

	if (!list_mode) {
		if (argc < 1) {
			printf("ERROR: No IMAGE_FILE has been given!\n");
			return -1;
		}
		image_path = strdup(argv[0]);
		if (argc >= 2) {
			image_sig_path = strdup(argv[1]);
		} else {
			if (asprintf(&image_sig_path, "%s.signature", image_path) < 0) {
				printf("Out of memory?!\n");
				return -1;
			}
		}
	}

	if (IDEVICE_E_SUCCESS != idevice_new(&device, udid)) {
		printf("No device found, is it plugged in?\n");
		return -1;
	}

	if (LOCKDOWN_E_SUCCESS != lockdownd_client_new_with_handshake(device, &lckd, "ideviceimagemounter")) {
		printf("ERROR: could not connect to lockdown. Exiting.\n");
		goto leave;
	}

	lockdownd_start_service(lckd, "com.apple.mobile.mobile_image_mounter", &service);

	if (!service || service->port == 0) {
		printf("ERROR: Could not start mobile_image_mounter service!\n");
		goto leave;
	}

	if (mobile_image_mounter_new(device, service, &mim) != MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
		printf("ERROR: Could not connect to mobile_image_mounter!\n");
		goto leave;
	}	

	if (service) {
		lockdownd_service_descriptor_free(service);
		service = NULL;
	}

	if (!list_mode) {
		struct stat fst;
		if ((lockdownd_start_service(lckd, "com.apple.afc", &service) !=
			 LOCKDOWN_E_SUCCESS) || !service || !service->port) {
			fprintf(stderr, "Could not start com.apple.afc!\n");
			goto leave;
		}
		if (afc_client_new(device, service, &afc) != AFC_E_SUCCESS) {
			fprintf(stderr, "Could not connect to AFC!\n");
			goto leave;
		}
		if (service) {
			lockdownd_service_descriptor_free(service);
			service = NULL;
		}
		if (stat(image_path, &fst) != 0) {
			fprintf(stderr, "ERROR: stat: %s: %s\n", image_path, strerror(errno));
			goto leave;
		}
		if (stat(image_sig_path, &fst) != 0) {
			fprintf(stderr, "ERROR: stat: %s: %s\n", image_sig_path, strerror(errno));
			goto leave;
		}
	}

	lockdownd_client_free(lckd);
	lckd = NULL;

	mobile_image_mounter_error_t err;
	plist_t result = NULL;

	if (list_mode) {
		/* list mounts mode */
		if (!imagetype) {
			imagetype = strdup("Developer");
		}
		err = mobile_image_mounter_lookup_image(mim, imagetype, &result);
		free(imagetype);
		if (err == MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
			res = 0;
			if (xml_mode) {
				print_xml(result);
			} else {
				plist_dict_to_string(result);
			}
		} else {
			printf("Error: lookup_image returned %d\n", err);
		}
	} else {
		char sig[8192];
		size_t sig_length = 0;
		FILE *f = fopen(image_sig_path, "rb");
		if (!f) {
			fprintf(stderr, "Error opening signature file '%s': %s\n", image_sig_path, strerror(errno));
			goto leave;
		}
		sig_length = fread(sig, 1, sizeof(sig), f);
		fclose(f);
		if (sig_length == 0) {
			fprintf(stderr, "Could not read signature from file '%s'\n", image_sig_path);
			goto leave;
		}

		f = fopen(image_path, "rb");
		if (!f) {
			fprintf(stderr, "Error opening image file '%s': %s\n", image_path, strerror(errno));
			goto leave;
		}

		char *targetname = NULL;
		if (asprintf(&targetname, "%s/%s", PKG_PATH, "staging.dimage") < 0) {
			fprintf(stderr, "Out of memory!?\n");
			goto leave;
		}
		char *mountname = NULL;
		if (asprintf(&mountname, "%s/%s", PATH_PREFIX, targetname) < 0) {
			fprintf(stderr, "Out of memory!?\n");
			goto leave;
		}

		printf("Copying '%s' --> '%s'\n", image_path, targetname);

		char **strs = NULL;
		if (afc_get_file_info(afc, PKG_PATH, &strs) != AFC_E_SUCCESS) {
			if (afc_make_directory(afc, PKG_PATH) != AFC_E_SUCCESS) {
				fprintf(stderr, "WARNING: Could not create directory '%s' on device!\n", PKG_PATH);
			}
		}
		if (strs) {
			int i = 0;
			while (strs[i]) {
				free(strs[i]);
				i++;
			}
			free(strs);
		}

		uint64_t af = 0;
		if ((afc_file_open(afc, targetname, AFC_FOPEN_WRONLY, &af) !=
			 AFC_E_SUCCESS) || !af) {
			fclose(f);
			fprintf(stderr, "afc_file_open on '%s' failed!\n", targetname);
			goto leave;
		}

		char buf[8192];
		size_t amount = 0;
		do {
			amount = fread(buf, 1, sizeof(buf), f);
			if (amount > 0) {
				uint32_t written, total = 0;
				while (total < amount) {
					written = 0;
					if (afc_file_write(afc, af, buf, amount, &written) !=
						AFC_E_SUCCESS) {
						fprintf(stderr, "AFC Write error!\n");
						break;
					}
					total += written;
				}
				if (total != amount) {
					fprintf(stderr, "Error: wrote only %d of %d\n", total,
							(unsigned int)amount);
					afc_file_close(afc, af);
					fclose(f);
					goto leave;
				}
			}
		}
		while (amount > 0);

		afc_file_close(afc, af);
		fclose(f);

		printf("done.\n");

		printf("Mounting...\n");
		if (!imagetype) {
			imagetype = strdup("Developer");
		}
		err = mobile_image_mounter_mount_image(mim, mountname, sig, sig_length, imagetype, &result);
		free(imagetype);
		if (err == MOBILE_IMAGE_MOUNTER_E_SUCCESS) {
			if (result) {
				plist_t node = plist_dict_get_item(result, "Status");
				if (node) {
					char *status = NULL;
					plist_get_string_val(node, &status);
					if (status) {
						if (!strcmp(status, "Complete")) {
							printf("Done.\n");
							res = 0;
						} else {
							printf("unexpected status value:\n");
							if (xml_mode) {
								print_xml(result);
							} else {
								plist_dict_to_string(result);
							}
						}
						free(status);
					} else {
						printf("unexpected result:\n");
						if (xml_mode) {
							print_xml(result);
						} else {
							plist_dict_to_string(result);
						}
					}
				}
				node = plist_dict_get_item(result, "Error");
				if (node) {
					char *error = NULL;
					plist_get_string_val(node, &error);
					if (error) {
						printf("Error: %s\n", error);
						free(error);
					} else {
						printf("unexpected result:\n");
						if (xml_mode) {
							print_xml(result);
						} else {
							plist_dict_to_string(result);
						}
					}

				} else {
					if (xml_mode) {
						print_xml(result);
					} else {
						plist_dict_to_string(result);
					}
				}
			}
		} else {
			printf("Error: mount_image returned %d\n", err);

		}
	}

	if (result) {
		plist_free(result);
	}

	/* perform hangup command */
	mobile_image_mounter_hangup(mim);
	/* free client */
	mobile_image_mounter_free(mim);

leave:
	if (afc) {
		afc_client_free(afc);
	}
	if (lckd) {
		lockdownd_client_free(lckd);
	}
	idevice_free(device);

	if (image_path)
			free(image_path);
	if (image_sig_path)
		free(image_sig_path);

	return res;
}
Example #16
0
DeviceStatus Device::backup() {
    mobilebackup2_client_t mb_client = NULL;
    DeviceStatus status = StatusError;
    if (mobilebackup2_client_start_service(this->mDevice, &mb_client, NULL) == MOBILEBACKUP2_E_SUCCESS) {
        afc_client_t afc = this->getAfcFromApp("");
        if (afc) {
            uint64_t handle;
            if (afc_file_open(afc, "/com.apple.itunes.lock_sync", AFC_FOPEN_RW, &handle) == AFC_E_SUCCESS
                && afc_file_lock(afc, handle, AFC_LOCK_EX) == AFC_E_SUCCESS) {
                if (mobilebackup2_send_request(mb_client, "Backup", this->mUdid.c_str(), this->mUdid.c_str(), NULL) == MOBILEBACKUP2_E_SUCCESS) {
                    plist_t msg = NULL;
                    char *dl = NULL;
                    if (mobilebackup2_receive_message(mb_client, &msg, &dl) == MOBILEBACKUP2_E_SUCCESS) {
                        plist_t files = plist_array_get_item(msg, 1);
                        int count = plist_array_get_size(files);
                        
                        for (int i = 0; i < count; i++) {
                            plist_t val = plist_array_get_item(files, i);
                            if (plist_get_node_type(val) != PLIST_STRING) {
                                continue;
                            }
                            char *str = NULL;
                            plist_get_string_val(val, &str);
                            if (!str) {
                                continue;
                            }
                            
                            
                            
                            
                            
                            
                            
                            uint32_t nlen = 0, bytes = 0;
                            mobilebackup2_error_t err;
                            err = mobilebackup2_send_raw(mb_client, (const char*)&nlen, sizeof(nlen), &bytes);
                            const char *path = "/tmp/test";
                            uint32_t pathlen = strlen(path);
                            err = mobilebackup2_send_raw(mb_client, path, pathlen, &bytes);
                            
                            
                            
                            
                            
                            
                            
                            
                            
                            free(str);
                        }
                        
                        uint32_t zero = 0, sent = 0;
                        mobilebackup2_send_raw(mb_client, (char*)&zero, sizeof(zero), &sent);
                        plist_free(msg);
                        free(dl);
                        status = StatusOK;
                    }
                }
                afc_file_close(afc, handle);
            }
        }
        mobilebackup2_client_free(mb_client);
    }
    return status;
}
int main(int argc, char **argv)
{
	idevice_t phone = NULL;
	lockdownd_client_t client = NULL;
	instproxy_client_t ipc = NULL;
	instproxy_error_t err;
	np_client_t np = NULL;
	afc_client_t afc = NULL;
#ifdef HAVE_LIBIMOBILEDEVICE_1_1_5
	lockdownd_service_descriptor_t service = NULL;
#else
	uint16_t service = 0;
#endif
	int res = 0;
	char *bundleidentifier = NULL;

	parse_opts(argc, argv);

	argc -= optind;
	argv += optind;

	if (IDEVICE_E_SUCCESS != idevice_new(&phone, udid)) {
		fprintf(stderr, "No iOS device found, is it plugged in?\n");
		return -1;
	}

	if (LOCKDOWN_E_SUCCESS != lockdownd_client_new_with_handshake(phone, &client, "ideviceinstaller")) {
		fprintf(stderr, "Could not connect to lockdownd. Exiting.\n");
		goto leave_cleanup;
	}

	if ((lockdownd_start_service
		 (client, "com.apple.mobile.notification_proxy",
		  &service) != LOCKDOWN_E_SUCCESS) || !service) {
		fprintf(stderr,
				"Could not start com.apple.mobile.notification_proxy!\n");
		goto leave_cleanup;
	}

	np_error_t nperr = np_client_new(phone, service, &np);
#ifdef HAVE_LIBIMOBILEDEVICE_1_1_5
	if (service) {
		lockdownd_service_descriptor_free(service);
	}
	service = NULL;
#else
	service = 0;
#endif
	if (nperr != NP_E_SUCCESS) {
		fprintf(stderr, "Could not connect to notification_proxy!\n");
		goto leave_cleanup;
	}

#ifdef HAVE_LIBIMOBILEDEVICE_1_1
	np_set_notify_callback(np, notifier, NULL);
#else
	np_set_notify_callback(np, notifier);
#endif

	const char *noties[3] = { NP_APP_INSTALLED, NP_APP_UNINSTALLED, NULL };

	np_observe_notifications(np, noties);

run_again:
#ifdef HAVE_LIBIMOBILEDEVICE_1_1_5
	if (service) {
		lockdownd_service_descriptor_free(service);
	}
	service = NULL;
#else
	service = 0;
#endif
	if ((lockdownd_start_service(client, "com.apple.mobile.installation_proxy",
		  &service) != LOCKDOWN_E_SUCCESS) || !service) {
		fprintf(stderr,
				"Could not start com.apple.mobile.installation_proxy!\n");
		goto leave_cleanup;
	}

	err = instproxy_client_new(phone, service, &ipc);
#ifdef HAVE_LIBIMOBILEDEVICE_1_1_5
	if (service) {
		lockdownd_service_descriptor_free(service);
	}
	service = NULL;
#else
	service = 0;
#endif
	if (err != INSTPROXY_E_SUCCESS) {
		fprintf(stderr, "Could not connect to installation_proxy!\n");
		goto leave_cleanup;
	}

	setbuf(stdout, NULL);

	if (last_status) {
		free(last_status);
		last_status = NULL;
	}
	notification_expected = 0;

	if (cmd == CMD_LIST_APPS) {
		int xml_mode = 0;
		plist_t client_opts = instproxy_client_options_new();
		instproxy_client_options_add(client_opts, "ApplicationType", "User", NULL);
		plist_t apps = NULL;

		/* look for options */
		if (options) {
			char *opts = strdup(options);
			char *elem = strtok(opts, ",");
			while (elem) {
				if (!strcmp(elem, "list_system")) {
					if (!client_opts) {
						client_opts = instproxy_client_options_new();
					}
					instproxy_client_options_add(client_opts, "ApplicationType", "System", NULL);
				} else if (!strcmp(elem, "list_all")) {
					instproxy_client_options_free(client_opts);
					client_opts = NULL;
				} else if (!strcmp(elem, "list_user")) {
					/* do nothing, we're already set */
				} else if (!strcmp(elem, "xml")) {
					xml_mode = 1;
				}
				elem = strtok(NULL, ",");
			}
			free(opts);
		}

		err = instproxy_browse(ipc, client_opts, &apps);
		instproxy_client_options_free(client_opts);
		if (err != INSTPROXY_E_SUCCESS) {
			fprintf(stderr, "ERROR: instproxy_browse returned %d\n", err);
			goto leave_cleanup;
		}
		if (!apps || (plist_get_node_type(apps) != PLIST_ARRAY)) {
			fprintf(stderr,
					"ERROR: instproxy_browse returnd an invalid plist!\n");
			goto leave_cleanup;
		}
		if (xml_mode) {
			char *xml = NULL;
			uint32_t len = 0;

			plist_to_xml(apps, &xml, &len);
			if (xml) {
				puts(xml);
				free(xml);
			}
			plist_free(apps);
			goto leave_cleanup;
		}
		printf("Total: %d apps\n", plist_array_get_size(apps));
		uint32_t i = 0;
		for (i = 0; i < plist_array_get_size(apps); i++) {
			plist_t app = plist_array_get_item(apps, i);
			plist_t p_appid =
				plist_dict_get_item(app, "CFBundleIdentifier");
			char *s_appid = NULL;
			char *s_dispName = NULL;
			char *s_version = NULL;
			plist_t dispName =
				plist_dict_get_item(app, "CFBundleDisplayName");
			plist_t version = plist_dict_get_item(app, "CFBundleVersion");

			if (p_appid) {
				plist_get_string_val(p_appid, &s_appid);
			}
			if (!s_appid) {
				fprintf(stderr, "ERROR: Failed to get APPID!\n");
				break;
			}

			if (dispName) {
				plist_get_string_val(dispName, &s_dispName);
			}
			if (version) {
				plist_get_string_val(version, &s_version);
			}

			if (!s_dispName) {
				s_dispName = strdup(s_appid);
			}
			if (s_version) {
				printf("%s - %s %s\n", s_appid, s_dispName, s_version);
				free(s_version);
			} else {
				printf("%s - %s\n", s_appid, s_dispName);
			}
			free(s_dispName);
			free(s_appid);
		}
		plist_free(apps);
	} else if (cmd == CMD_INSTALL || cmd == CMD_UPGRADE) {
		plist_t sinf = NULL;
		plist_t meta = NULL;
		char *pkgname = NULL;
		struct stat fst;
		uint64_t af = 0;
		char buf[8192];

#ifdef HAVE_LIBIMOBILEDEVICE_1_1_5
		if (service) {
			lockdownd_service_descriptor_free(service);
		}
		service = NULL;
#else
		service = 0;
#endif
		if ((lockdownd_start_service(client, "com.apple.afc", &service) !=
			 LOCKDOWN_E_SUCCESS) || !service) {
			fprintf(stderr, "Could not start com.apple.afc!\n");
			goto leave_cleanup;
		}

		lockdownd_client_free(client);
		client = NULL;

		if (afc_client_new(phone, service, &afc) != INSTPROXY_E_SUCCESS) {
			fprintf(stderr, "Could not connect to AFC!\n");
			goto leave_cleanup;
		}

		if (stat(appid, &fst) != 0) {
			fprintf(stderr, "ERROR: stat: %s: %s\n", appid, strerror(errno));
			goto leave_cleanup;
		}

		char **strs = NULL;
		if (afc_get_file_info(afc, PKG_PATH, &strs) != AFC_E_SUCCESS) {
			if (afc_make_directory(afc, PKG_PATH) != AFC_E_SUCCESS) {
				fprintf(stderr, "WARNING: Could not create directory '%s' on device!\n", PKG_PATH);
			}
		}
		if (strs) {
			int i = 0;
			while (strs[i]) {
				free(strs[i]);
				i++;
			}
			free(strs);
		}

		plist_t client_opts = instproxy_client_options_new();

		/* open install package */
		int errp = 0;
		struct zip *zf = NULL;
		
		if ((strlen(appid) > 5) && (strcmp(&appid[strlen(appid)-5], ".ipcc") == 0)) {
			zf = zip_open(appid, 0, &errp);
			if (!zf) {
				fprintf(stderr, "ERROR: zip_open: %s: %d\n", appid, errp);
				goto leave_cleanup;
			}

			char* ipcc = strdup(appid);
			if ((asprintf(&pkgname, "%s/%s", PKG_PATH, basename(ipcc)) > 0) && pkgname) {
				afc_make_directory(afc, pkgname);
			}

			printf("Uploading %s package contents... ", basename(ipcc));

			/* extract the contents of the .ipcc file to PublicStaging/<name>.ipcc directory */
			zip_uint64_t numzf = zip_get_num_entries(zf, 0);
			zip_uint64_t i = 0;
			for (i = 0; numzf > 0 && i < numzf; i++) {
				const char* zname = zip_get_name(zf, i, 0);
				char* dstpath = NULL;
				if (!zname) continue;
				if (zname[strlen(zname)-1] == '/') {
					// directory
					if ((asprintf(&dstpath, "%s/%s/%s", PKG_PATH, basename(ipcc), zname) > 0) && dstpath) {
						afc_make_directory(afc, dstpath);						}
					free(dstpath);
					dstpath = NULL;
				} else {
					// file
					struct zip_file* zfile = zip_fopen_index(zf, i, 0);
					if (!zfile) continue;

					if ((asprintf(&dstpath, "%s/%s/%s", PKG_PATH, basename(ipcc), zname) <= 0) || !dstpath || (afc_file_open(afc, dstpath, AFC_FOPEN_WRONLY, &af) != AFC_E_SUCCESS)) {
						fprintf(stderr, "ERROR: can't open afc://%s for writing\n", dstpath);
						free(dstpath);
						dstpath = NULL;
						zip_fclose(zfile);
						continue;
					}

					struct zip_stat zs;
					zip_stat_init(&zs);
					if (zip_stat_index(zf, i, 0, &zs) != 0) {
						fprintf(stderr, "ERROR: zip_stat_index %" PRIu64 " failed!\n", i);
						free(dstpath);
						dstpath = NULL;
						zip_fclose(zfile);
						continue;
					}

					free(dstpath);
					dstpath = NULL;

					zip_uint64_t zfsize = 0;
					while (zfsize < zs.size) {
						zip_int64_t amount = zip_fread(zfile, buf, sizeof(buf));
						if (amount == 0) {
							break;
						}

						if (amount > 0) {
							uint32_t written, total = 0;
							while (total < amount) {
								written = 0;
								if (afc_file_write(afc, af, buf, amount, &written) !=
									AFC_E_SUCCESS) {
									fprintf(stderr, "AFC Write error!\n");
									break;
								}
								total += written;
							}
							if (total != amount) {
								fprintf(stderr, "Error: wrote only %d of %" PRIi64 "\n", total, amount);
								afc_file_close(afc, af);
								zip_fclose(zfile);
								free(dstpath);
								goto leave_cleanup;
							}
						}

						zfsize += amount;
					}

					afc_file_close(afc, af);
					af = 0;

					zip_fclose(zfile);
				}
			}
			free(ipcc);
			printf("DONE.\n");

			instproxy_client_options_add(client_opts, "PackageType", "CarrierBundle", NULL);
		} else if (S_ISDIR(fst.st_mode)) {
			/* upload developer app directory */
			instproxy_client_options_add(client_opts, "PackageType", "Developer", NULL);

			if (asprintf(&pkgname, "%s/%s", PKG_PATH, basename(appid)) < 0) {
				fprintf(stderr, "ERROR: Out of memory allocating pkgname!?\n");
				goto leave_cleanup;
			}

			printf("Uploading %s package contents... ", basename(appid));
			afc_upload_dir(afc, appid, pkgname);
			printf("DONE.\n");
		} else {
			zf = zip_open(appid, 0, &errp);
			if (!zf) {
				fprintf(stderr, "ERROR: zip_open: %s: %d\n", appid, errp);
				goto leave_cleanup;
			}

			/* extract iTunesMetadata.plist from package */
			char *zbuf = NULL;
			uint32_t len = 0;
			plist_t meta_dict = NULL;
			if (zip_get_contents(zf, ITUNES_METADATA_PLIST_FILENAME, 0, &zbuf, &len) == 0) {
				meta = plist_new_data(zbuf, len);
				if (memcmp(zbuf, "bplist00", 8) == 0) {
					plist_from_bin(zbuf, len, &meta_dict);
				} else {
					plist_from_xml(zbuf, len, &meta_dict);
				}
			} else {
				fprintf(stderr, "WARNING: could not locate %s in archive!\n", ITUNES_METADATA_PLIST_FILENAME);
			}
			if (zbuf) {
				free(zbuf);
			}

			/* determine .app directory in archive */
			zbuf = NULL;
			len = 0;
			plist_t info = NULL;
			char* filename = NULL;
			char* app_directory_name = NULL;

			if (zip_get_app_directory(zf, &app_directory_name)) {
				fprintf(stderr, "Unable to locate app directory in archive!\n");
				goto leave_cleanup;
			}

			/* construct full filename to Info.plist */
			filename = (char*)malloc(strlen(app_directory_name)+10+1);
			strcpy(filename, app_directory_name);
			free(app_directory_name);
			app_directory_name = NULL;
			strcat(filename, "Info.plist");

			if (zip_get_contents(zf, filename, 0, &zbuf, &len) < 0) {
				fprintf(stderr, "WARNING: could not locate %s in archive!\n", filename);
				free(filename);
				zip_unchange_all(zf);
				zip_close(zf);
				goto leave_cleanup;
			}
			free(filename);
			if (memcmp(zbuf, "bplist00", 8) == 0) {
				plist_from_bin(zbuf, len, &info);
			} else {
				plist_from_xml(zbuf, len, &info);
			}
			free(zbuf);

			if (!info) {
				fprintf(stderr, "Could not parse Info.plist!\n");
				zip_unchange_all(zf);
				zip_close(zf);
				goto leave_cleanup;
			}

			char *bundleexecutable = NULL;

			plist_t bname = plist_dict_get_item(info, "CFBundleExecutable");
			if (bname) {
				plist_get_string_val(bname, &bundleexecutable);
			}

			bname = plist_dict_get_item(info, "CFBundleIdentifier");
			if (bname) {
				plist_get_string_val(bname, &bundleidentifier);
			}
			plist_free(info);
			info = NULL;

			if (!bundleexecutable) {
				fprintf(stderr, "Could not determine value for CFBundleExecutable!\n");
				zip_unchange_all(zf);
				zip_close(zf);
				goto leave_cleanup;
			}

			char *sinfname = NULL;
			if (asprintf(&sinfname, "Payload/%s.app/SC_Info/%s.sinf", bundleexecutable, bundleexecutable) < 0) {
				fprintf(stderr, "Out of memory!?\n");
				goto leave_cleanup;
			}
			free(bundleexecutable);

			/* extract .sinf from package */
			zbuf = NULL;
			len = 0;
			if (zip_get_contents(zf, sinfname, 0, &zbuf, &len) == 0) {
				sinf = plist_new_data(zbuf, len);
			} else {
				fprintf(stderr, "WARNING: could not locate %s in archive!\n", sinfname);
			}
			free(sinfname);
			if (zbuf) {
				free(zbuf);
			}

			/* copy archive to device */
			pkgname = NULL;
			if (asprintf(&pkgname, "%s/%s", PKG_PATH, bundleidentifier) < 0) {
				fprintf(stderr, "Out of memory!?\n");
				goto leave_cleanup;
			}

			printf("Copying '%s' to device... ", appid);

			if (afc_upload_file(afc, appid, pkgname) < 0) {
				free(pkgname);
				goto leave_cleanup;
			}

			printf("DONE.\n");

			if (bundleidentifier) {
				instproxy_client_options_add(client_opts, "CFBundleIdentifier", bundleidentifier, NULL);
			}
			if (sinf) {
				instproxy_client_options_add(client_opts, "ApplicationSINF", sinf, NULL);
			}
			if (meta) {
				instproxy_client_options_add(client_opts, "iTunesMetadata", meta, NULL);
			}
		}
		if (zf) {
			zip_unchange_all(zf);
			zip_close(zf);
		}

		/* perform installation or upgrade */
		if (cmd == CMD_INSTALL) {
			printf("Installing '%s'\n", bundleidentifier);
#ifdef HAVE_LIBIMOBILEDEVICE_1_1
			instproxy_install(ipc, pkgname, client_opts, status_cb, NULL);
#else
			instproxy_install(ipc, pkgname, client_opts, status_cb);
#endif
		} else {
			printf("Upgrading '%s'\n", bundleidentifier);
#ifdef HAVE_LIBIMOBILEDEVICE_1_1
			instproxy_upgrade(ipc, pkgname, client_opts, status_cb, NULL);
#else
			instproxy_upgrade(ipc, pkgname, client_opts, status_cb);
#endif
		}
		instproxy_client_options_free(client_opts);
		free(pkgname);
		wait_for_op_complete = 1;
		notification_expected = 1;
	} else if (cmd == CMD_UNINSTALL) {
		printf("Uninstalling '%s'\n", appid);
#ifdef HAVE_LIBIMOBILEDEVICE_1_1
		instproxy_uninstall(ipc, appid, NULL, status_cb, NULL);
#else
		instproxy_uninstall(ipc, appid, NULL, status_cb);
#endif
		wait_for_op_complete = 1;
		notification_expected = 0;
	} else if (cmd == CMD_LIST_ARCHIVES) {
		int xml_mode = 0;
		plist_t dict = NULL;
		plist_t lres = NULL;

		/* look for options */
		if (options) {
			char *opts = strdup(options);
			char *elem = strtok(opts, ",");
			while (elem) {
				if (!strcmp(elem, "xml")) {
					xml_mode = 1;
				}
				elem = strtok(NULL, ",");
			}
		}

		err = instproxy_lookup_archives(ipc, NULL, &dict);
		if (err != INSTPROXY_E_SUCCESS) {
			fprintf(stderr, "ERROR: lookup_archives returned %d\n", err);
			goto leave_cleanup;
		}
		if (!dict) {
			fprintf(stderr,
					"ERROR: lookup_archives did not return a plist!?\n");
			goto leave_cleanup;
		}

		lres = plist_dict_get_item(dict, "LookupResult");
		if (!lres || (plist_get_node_type(lres) != PLIST_DICT)) {
			plist_free(dict);
			fprintf(stderr, "ERROR: Could not get dict 'LookupResult'\n");
			goto leave_cleanup;
		}

		if (xml_mode) {
			char *xml = NULL;
			uint32_t len = 0;

			plist_to_xml(lres, &xml, &len);
			if (xml) {
				puts(xml);
				free(xml);
			}
			plist_free(dict);
			goto leave_cleanup;
		}
		plist_dict_iter iter = NULL;
		plist_t node = NULL;
		char *key = NULL;

		printf("Total: %d archived apps\n", plist_dict_get_size(lres));
		plist_dict_new_iter(lres, &iter);
		if (!iter) {
			plist_free(dict);
			fprintf(stderr, "ERROR: Could not create plist_dict_iter!\n");
			goto leave_cleanup;
		}
		do {
			key = NULL;
			node = NULL;
			plist_dict_next_item(lres, iter, &key, &node);
			if (key && (plist_get_node_type(node) == PLIST_DICT)) {
				char *s_dispName = NULL;
				char *s_version = NULL;
				plist_t dispName =
					plist_dict_get_item(node, "CFBundleDisplayName");
				plist_t version =
					plist_dict_get_item(node, "CFBundleVersion");
				if (dispName) {
					plist_get_string_val(dispName, &s_dispName);
				}
				if (version) {
					plist_get_string_val(version, &s_version);
				}
				if (!s_dispName) {
					s_dispName = strdup(key);
				}
				if (s_version) {
					printf("%s - %s %s\n", key, s_dispName, s_version);
					free(s_version);
				} else {
					printf("%s - %s\n", key, s_dispName);
				}
				free(s_dispName);
				free(key);
			}
		}
		while (node);
		plist_free(dict);
	} else if (cmd == CMD_ARCHIVE) {
		char *copy_path = NULL;
		int remove_after_copy = 0;
		int skip_uninstall = 1;
		int app_only = 0;
		int docs_only = 0;
		plist_t client_opts = NULL;

		/* look for options */
		if (options) {
			char *opts = strdup(options);
			char *elem = strtok(opts, ",");
			while (elem) {
				if (!strcmp(elem, "uninstall")) {
					skip_uninstall = 0;
				} else if (!strcmp(elem, "app_only")) {
					app_only = 1;
					docs_only = 0;
				} else if (!strcmp(elem, "docs_only")) {
					docs_only = 1;
					app_only = 0;
				} else if ((strlen(elem) > 5) && !strncmp(elem, "copy=", 5)) {
					copy_path = strdup(elem+5);
				} else if (!strcmp(elem, "remove")) {
					remove_after_copy = 1;
				}
				elem = strtok(NULL, ",");
			}
		}

		if (skip_uninstall || app_only || docs_only) {
			client_opts = instproxy_client_options_new();
			if (skip_uninstall) {
				instproxy_client_options_add(client_opts, "SkipUninstall", 1, NULL);
			}
			if (app_only) {
				instproxy_client_options_add(client_opts, "ArchiveType", "ApplicationOnly", NULL);
			} else if (docs_only) {
				instproxy_client_options_add(client_opts, "ArchiveType", "DocumentsOnly", NULL);
			}
		}

		if (copy_path) {
			struct stat fst;
			if (stat(copy_path, &fst) != 0) {
				fprintf(stderr, "ERROR: stat: %s: %s\n", copy_path, strerror(errno));
				free(copy_path);
				goto leave_cleanup;
			}

			if (!S_ISDIR(fst.st_mode)) {
				fprintf(stderr, "ERROR: '%s' is not a directory as expected.\n", copy_path);
				free(copy_path);
				goto leave_cleanup;
			}

#ifdef HAVE_LIBIMOBILEDEVICE_1_1_5
			if (service) {
				lockdownd_service_descriptor_free(service);
			}
			service = NULL;
#else
			service = 0;
#endif
			if ((lockdownd_start_service(client, "com.apple.afc", &service) != LOCKDOWN_E_SUCCESS) || !service) {
				fprintf(stderr, "Could not start com.apple.afc!\n");
				free(copy_path);
				goto leave_cleanup;
			}

			lockdownd_client_free(client);
			client = NULL;

			if (afc_client_new(phone, service, &afc) != INSTPROXY_E_SUCCESS) {
				fprintf(stderr, "Could not connect to AFC!\n");
				goto leave_cleanup;
			}
		}

#ifdef HAVE_LIBIMOBILEDEVICE_1_1
		instproxy_archive(ipc, appid, client_opts, status_cb, NULL);
#else
		instproxy_archive(ipc, appid, client_opts, status_cb);
#endif
		instproxy_client_options_free(client_opts);
		wait_for_op_complete = 1;
		if (skip_uninstall) {
			notification_expected = 0;
		} else {
			notification_expected = 1;
		}

		idevice_wait_for_operation_to_complete();

		if (copy_path) {
			if (err_occured) {
				afc_client_free(afc);
				afc = NULL;
				goto leave_cleanup;
			}
			FILE *f = NULL;
			uint64_t af = 0;
			/* local filename */
			char *localfile = NULL;
			if (asprintf(&localfile, "%s/%s.ipa", copy_path, appid) < 0) {
				fprintf(stderr, "Out of memory!?\n");
				goto leave_cleanup;
			}
			free(copy_path);

			f = fopen(localfile, "wb");
			if (!f) {
				fprintf(stderr, "ERROR: fopen: %s: %s\n", localfile, strerror(errno));
				free(localfile);
				goto leave_cleanup;
			}

			/* remote filename */
			char *remotefile = NULL;
			if (asprintf(&remotefile, "%s/%s.zip", APPARCH_PATH, appid) < 0) {
				fprintf(stderr, "Out of memory!?\n");
				goto leave_cleanup;
			}

			uint32_t fsize = 0;
			char **fileinfo = NULL;
			if ((afc_get_file_info(afc, remotefile, &fileinfo) != AFC_E_SUCCESS) || !fileinfo) {
				fprintf(stderr, "ERROR getting AFC file info for '%s' on device!\n", remotefile);
				fclose(f);
				free(remotefile);
				free(localfile);
				goto leave_cleanup;
			}

			int i;
			for (i = 0; fileinfo[i]; i+=2) {
				if (!strcmp(fileinfo[i], "st_size")) {
					fsize = atoi(fileinfo[i+1]);
					break;
				}
			}
			i = 0;
			while (fileinfo[i]) {
				free(fileinfo[i]);
				i++;
			}
			free(fileinfo);

			if (fsize == 0) {
				fprintf(stderr, "Hm... remote file length could not be determined. Cannot copy.\n");
				fclose(f);
				free(remotefile);
				free(localfile);
				goto leave_cleanup;
			}

			if ((afc_file_open(afc, remotefile, AFC_FOPEN_RDONLY, &af) != AFC_E_SUCCESS) || !af) {
				fclose(f);
				fprintf(stderr, "ERROR: could not open '%s' on device for reading!\n", remotefile);
				free(remotefile);
				free(localfile);
				goto leave_cleanup;
			}

			/* copy file over */
			printf("Copying '%s' --> '%s'... ", remotefile, localfile);
			free(remotefile);
			free(localfile);

			uint32_t amount = 0;
			uint32_t total = 0;
			char buf[8192];

			do {
				if (afc_file_read(afc, af, buf, sizeof(buf), &amount) != AFC_E_SUCCESS) {
					fprintf(stderr, "AFC Read error!\n");
					break;
				}

				if (amount > 0) {
					size_t written = fwrite(buf, 1, amount, f);
					if (written != amount) {
						fprintf(stderr, "Error when writing %d bytes to local file!\n", amount);
						break;
					}
					total += written;
				}
			} while (amount > 0);

			afc_file_close(afc, af);
			fclose(f);

			printf("DONE.\n");

			if (total != fsize) {
				fprintf(stderr, "WARNING: remote and local file sizes don't match (%d != %d)\n", fsize, total);
				if (remove_after_copy) {
					fprintf(stderr, "NOTE: archive file will NOT be removed from device\n");
					remove_after_copy = 0;
				}
			}

			if (remove_after_copy) {
				/* remove archive if requested */
				printf("Removing '%s'\n", appid);
				cmd = CMD_REMOVE_ARCHIVE;
				free(options);
				options = NULL;
				if (LOCKDOWN_E_SUCCESS != lockdownd_client_new_with_handshake(phone, &client, "ideviceinstaller")) {
					fprintf(stderr, "Could not connect to lockdownd. Exiting.\n");
					goto leave_cleanup;
				}
				goto run_again;
			}
		}
		goto leave_cleanup;
	} else if (cmd == CMD_RESTORE) {
#ifdef HAVE_LIBIMOBILEDEVICE_1_1
		instproxy_restore(ipc, appid, NULL, status_cb, NULL);
#else
		instproxy_restore(ipc, appid, NULL, status_cb);
#endif
		wait_for_op_complete = 1;
		notification_expected = 1;
	} else if (cmd == CMD_REMOVE_ARCHIVE) {
#ifdef HAVE_LIBIMOBILEDEVICE_1_1
		instproxy_remove_archive(ipc, appid, NULL, status_cb, NULL);
#else
		instproxy_remove_archive(ipc, appid, NULL, status_cb);
#endif
		wait_for_op_complete = 1;
	} else {
		printf
			("ERROR: no operation selected?! This should not be reached!\n");
		res = -2;
		goto leave_cleanup;
	}

	if (client) {
		/* not needed anymore */
		lockdownd_client_free(client);
		client = NULL;
	}

	idevice_wait_for_operation_to_complete();

leave_cleanup:
	if (bundleidentifier) {
		free(bundleidentifier);
	}
	if (np) {
		np_client_free(np);
	}
	if (ipc) {
		instproxy_client_free(ipc);
	}
	if (afc) {
		afc_client_free(afc);
	}
	if (client) {
		lockdownd_client_free(client);
	}
	idevice_free(phone);

	if (udid) {
		free(udid);
	}
	if (appid) {
		free(appid);
	}
	if (options) {
		free(options);
	}

	return res;
}
static int afc_client_copy_and_remove_crash_reports(afc_client_t afc, const char* device_directory, const char* host_directory)
{
	afc_error_t afc_error;
	int k;
	int res = -1;
	int crash_report_count = 0;
	uint64_t handle;
	char source_filename[512];
	char target_filename[512];

	if (!afc)
		return res;

	char** list = NULL;
	afc_error = afc_read_directory(afc, device_directory, &list);
	if (afc_error != AFC_E_SUCCESS) {
		fprintf(stderr, "ERROR: Could not read device directory '%s'\n", device_directory);
		return res;
	}

	/* ensure we have a trailing slash */
	strcpy(source_filename, device_directory);
	if (source_filename[strlen(source_filename)-1] != '/') {
		strcat(source_filename, "/");
	}
	int device_directory_length = strlen(source_filename);

	/* ensure we have a trailing slash */
	strcpy(target_filename, host_directory);
	if (target_filename[strlen(target_filename)-1] != '/') {
		strcat(target_filename, "/");
	}
	int host_directory_length = strlen(target_filename);

	/* loop over file entries */
	for (k = 0; list[k]; k++) {
		if (!strcmp(list[k], ".") || !strcmp(list[k], "..")) {
			continue;
		}

		char **fileinfo = NULL;
		struct stat stbuf;
		stbuf.st_size = 0;

		/* assemble absolute source filename */
		strcpy(((char*)source_filename) + device_directory_length, list[k]);

		/* assemble absolute target filename */
		char* p = strrchr(list[k], '.');
		if (p != NULL && !strncmp(p, ".synced", 7)) {
			/* make sure to strip ".synced" extension as seen on iOS 5 */
			strncpy(((char*)target_filename) + host_directory_length, list[k], strlen(list[k]) - 7);
		} else {
			strcpy(((char*)target_filename) + host_directory_length, list[k]);
		}

		/* get file information */
		afc_get_file_info(afc, source_filename, &fileinfo);
		if (!fileinfo) {
			printf("Failed to read information for '%s'. Skipping...\n", source_filename);
			continue;
		}

		/* parse file information */
		int i;
		for (i = 0; fileinfo[i]; i+=2) {
			if (!strcmp(fileinfo[i], "st_size")) {
				stbuf.st_size = atoll(fileinfo[i+1]);
			} else if (!strcmp(fileinfo[i], "st_ifmt")) {
				if (!strcmp(fileinfo[i+1], "S_IFREG")) {
					stbuf.st_mode = S_IFREG;
				} else if (!strcmp(fileinfo[i+1], "S_IFDIR")) {
					stbuf.st_mode = S_IFDIR;
				} else if (!strcmp(fileinfo[i+1], "S_IFLNK")) {
					stbuf.st_mode = S_IFLNK;
				} else if (!strcmp(fileinfo[i+1], "S_IFBLK")) {
					stbuf.st_mode = S_IFBLK;
				} else if (!strcmp(fileinfo[i+1], "S_IFCHR")) {
					stbuf.st_mode = S_IFCHR;
				} else if (!strcmp(fileinfo[i+1], "S_IFIFO")) {
					stbuf.st_mode = S_IFIFO;
				} else if (!strcmp(fileinfo[i+1], "S_IFSOCK")) {
					stbuf.st_mode = S_IFSOCK;
				}
			} else if (!strcmp(fileinfo[i], "st_nlink")) {
				stbuf.st_nlink = atoi(fileinfo[i+1]);
			} else if (!strcmp(fileinfo[i], "st_mtime")) {
				stbuf.st_mtime = (time_t)(atoll(fileinfo[i+1]) / 1000000000);
			} else if (!strcmp(fileinfo[i], "LinkTarget")) {
				/* report latest crash report filename */
				printf("Link: %s\n", (char*)target_filename + strlen(target_directory));

				/* remove any previous symlink */
				if (file_exists(target_filename)) {
					remove(target_filename);
				}

#ifndef WIN32
				/* use relative filename */
				char* b = strrchr(fileinfo[i+1], '/');
				if (b == NULL) {
					b = fileinfo[i+1];
				} else {
					b++;
				}

				/* create a symlink pointing to latest log */
				if (symlink(b, target_filename) < 0) {
					fprintf(stderr, "Can't create symlink to %s\n", b);
				}
#endif

				if (!keep_crash_reports)
					afc_remove_path(afc, source_filename);

				res = 0;
			}
		}

		/* free file information */
		afc_dictionary_free(fileinfo);

		/* recurse into child directories */
		if (S_ISDIR(stbuf.st_mode)) {
#ifdef WIN32
			mkdir(target_filename);
#else
			mkdir(target_filename, 0755);
#endif
			res = afc_client_copy_and_remove_crash_reports(afc, source_filename, target_filename);

			/* remove directory from device */
			if (!keep_crash_reports)
				afc_remove_path(afc, source_filename);
		} else if (S_ISREG(stbuf.st_mode)) {
			/* copy file to host */
			afc_error = afc_file_open(afc, source_filename, AFC_FOPEN_RDONLY, &handle);
			if(afc_error != AFC_E_SUCCESS) {
				if (afc_error == AFC_E_OBJECT_NOT_FOUND) {
					continue;
				}
				fprintf(stderr, "Unable to open device file '%s' (%d). Skipping...\n", source_filename, afc_error);
				continue;
			}

			FILE* output = fopen(target_filename, "wb");
			if(output == NULL) {
				fprintf(stderr, "Unable to open local file '%s'. Skipping...\n", target_filename);
				afc_file_close(afc, handle);
				continue;
			}

			printf("%s: %s\n", (keep_crash_reports ? "Copy": "Move") , (char*)target_filename + strlen(target_directory));

			uint32_t bytes_read = 0;
			uint32_t bytes_total = 0;
			unsigned char data[0x1000];

			afc_error = afc_file_read(afc, handle, (char*)data, 0x1000, &bytes_read);
			while(afc_error == AFC_E_SUCCESS && bytes_read > 0) {
				fwrite(data, 1, bytes_read, output);
				bytes_total += bytes_read;
				afc_error = afc_file_read(afc, handle, (char*)data, 0x1000, &bytes_read);
			}
			afc_file_close(afc, handle);
			fclose(output);

			if ((uint32_t)stbuf.st_size != bytes_total) {
				fprintf(stderr, "File size mismatch. Skipping...\n");
				continue;
			}

			/* remove file from device */
			if (!keep_crash_reports) {
				afc_remove_path(afc, source_filename);
			}

			/* extract raw crash information into separate '.crash' file */
			if (extract_raw_crash_reports) {
				extract_raw_crash_report(target_filename);
			}

			crash_report_count++;

			res = 0;
		}
	}
	afc_dictionary_free(list);

	/* no reports, no error */
	if (crash_report_count == 0)
		res = 0;

	return res;
}