Beispiel #1
0
void BoxStorage::tokenRefreshed(BoolCallback callback, Networking::JsonResponse response) {
	Common::JSONValue *json = response.value;
	if (!json) {
		warning("BoxStorage: got NULL instead of JSON");
		if (callback)
			(*callback)(BoolResponse(nullptr, false));
		delete callback;
		return;
	}

	if (!Networking::CurlJsonRequest::jsonIsObject(json, "BoxStorage")) {
		if (callback)
			(*callback)(BoolResponse(nullptr, false));
		delete json;
		delete callback;
		return;
	}

	Common::JSONObject result = json->asObject();
	if (!Networking::CurlJsonRequest::jsonContainsString(result, "access_token", "BoxStorage") ||
		!Networking::CurlJsonRequest::jsonContainsString(result, "refresh_token", "BoxStorage")) {
		warning("BoxStorage: bad response, no token passed");
		debug(9, "%s", json->stringify().c_str());
		if (callback)
			(*callback)(BoolResponse(nullptr, false));
	} else {
		_token = result.getVal("access_token")->asString();
		_refreshToken = result.getVal("refresh_token")->asString();
		CloudMan.save(); //ask CloudManager to save our new refreshToken
		if (callback)
			(*callback)(BoolResponse(nullptr, true));
	}
	delete json;
	delete callback;
}
Beispiel #2
0
void DropboxStorage::codeFlowComplete(Networking::JsonResponse response) {
    Common::JSONValue *json = (Common::JSONValue *)response.value;
    if (json == nullptr) {
        debug(9, "DropboxStorage::codeFlowComplete: got NULL instead of JSON!");
        CloudMan.removeStorage(this);
        return;
    }

    if (!json->isObject()) {
        debug(9, "DropboxStorage::codeFlowComplete: Passed JSON is not an object!");
        CloudMan.removeStorage(this);
        delete json;
        return;
    }

    Common::JSONObject result = json->asObject();
    if (!Networking::CurlJsonRequest::jsonContainsString(result, "access_token", "DropboxStorage::codeFlowComplete") ||
            !Networking::CurlJsonRequest::jsonContainsString(result, "uid", "DropboxStorage::codeFlowComplete")) {
        warning("DropboxStorage: bad response, no token/uid passed");
        debug(9, "%s", json->stringify(true).c_str());
        CloudMan.removeStorage(this);
    } else {
        _token = result.getVal("access_token")->asString();
        _uid = result.getVal("uid")->asString();
        ConfMan.removeKey("dropbox_code", ConfMan.kCloudDomain);
        CloudMan.replaceStorage(this, kStorageDropboxId);
        ConfMan.flushToDisk();
    }

    delete json;
}
void OneDriveUploadRequest::partUploadedCallback(Networking::JsonResponse response) {
	_workingRequest = nullptr;
	if (_ignoreCallback)
		return;

	Networking::ErrorResponse error(this, false, true, "", -1);
	Networking::CurlJsonRequest *rq = (Networking::CurlJsonRequest *)response.request;
	if (rq && rq->getNetworkReadStream())
		error.httpResponseCode = rq->getNetworkReadStream()->httpResponseCode();

	Common::JSONValue *json = response.value;
	if (json == nullptr) {
		error.response = "Failed to parse JSON, null passed!";
		finishError(error);
		return;
	}

	if (json->isObject()) {
		Common::JSONObject object = json->asObject();

		if (object.contains("error")) {
			warning("OneDriveUploadRequest: error: %s", json->stringify(true).c_str());
			error.response = json->stringify(true);
			finishError(error);
			delete json;
			return;
		}

		if (Networking::CurlJsonRequest::jsonContainsString(object, "id", "OneDriveUploadRequest") &&
			Networking::CurlJsonRequest::jsonContainsString(object, "name", "OneDriveUploadRequest") &&
			Networking::CurlJsonRequest::jsonContainsIntegerNumber(object, "size", "OneDriveUploadRequest") &&
			Networking::CurlJsonRequest::jsonContainsString(object, "lastModifiedDateTime", "OneDriveUploadRequest")) {
			//finished
			Common::String path = _savePath;
			uint32 size = object.getVal("size")->asIntegerNumber();
			uint32 timestamp = ISO8601::convertToTimestamp(object.getVal("lastModifiedDateTime")->asString());
			finishUpload(StorageFile(path, size, timestamp, false));
			return;
		}

		if (_uploadUrl == "") {
			if (Networking::CurlJsonRequest::jsonContainsString(object, "uploadUrl", "OneDriveUploadRequest"))
				_uploadUrl = object.getVal("uploadUrl")->asString();
		}
	}

	if (_contentsStream->eos() || _contentsStream->pos() >= _contentsStream->size() - 1) {
		warning("OneDriveUploadRequest: no file info to return");
		finishUpload(StorageFile(_savePath, 0, 0, false));
	} else {
		uploadNextPart();
	}

	delete json;
}
Beispiel #4
0
void SavesSyncRequest::directoryListedErrorCallback(Networking::ErrorResponse error) {
	_workingRequest = nullptr;
	if (_ignoreCallback)
		return;

	bool irrecoverable = error.interrupted || error.failed;
	if (error.failed) {
		Common::JSONValue *value = Common::JSON::parse(error.response.c_str());
		if (value) {
			if (value->isObject()) {
				Common::JSONObject object = value->asObject();

				//Dropbox-related error:
				if (object.contains("error_summary") && object.getVal("error_summary")->isString()) {
					Common::String summary = object.getVal("error_summary")->asString();
					if (summary.contains("not_found")) {
						irrecoverable = false;
					}
				}

				//OneDrive-related error:
				if (object.contains("error") && object.getVal("error")->isObject()) {
					Common::JSONObject errorNode = object.getVal("error")->asObject();
					if (Networking::CurlJsonRequest::jsonContainsString(errorNode, "code", "SavesSyncRequest")) {
						Common::String code = errorNode.getVal("code")->asString();
						if (code == "itemNotFound") {
							irrecoverable = false;
						}
					}
				}
			}
			delete value;
		}

		//Google Drive and Box-related ScummVM-based error
		if (error.response.contains("subdirectory not found")) {
			irrecoverable = false; //base "/ScummVM/" folder not found
		} else if (error.response.contains("no such file found in its parent directory")) {
			irrecoverable = false; //"Saves" folder within "/ScummVM/" not found
		}
	}

	if (irrecoverable) {
		finishError(error);
		return;
	}

	//we're lucky - user just lacks his "/cloud/" folder - let's create one
	Common::String dir = _storage->savesDirectoryPath();
	if (dir.lastChar() == '/')
		dir.deleteLastChar();
	debug(9, "SavesSyncRequest: creating %s", dir.c_str());
	_workingRequest = _storage->createDirectory(
		dir,
		new Common::Callback<SavesSyncRequest, Storage::BoolResponse>(this, &SavesSyncRequest::directoryCreatedCallback),
		new Common::Callback<SavesSyncRequest, Networking::ErrorResponse>(this, &SavesSyncRequest::directoryCreatedErrorCallback)
	);
	if (!_workingRequest)
		finishError(Networking::ErrorResponse(this));
}
Beispiel #5
0
Networking::Request *DropboxStorage::streamFileById(Common::String path, Networking::NetworkReadStreamCallback callback, Networking::ErrorCallback errorCallback) {
    Common::JSONObject jsonRequestParameters;
    jsonRequestParameters.setVal("path", new Common::JSONValue(path));
    Common::JSONValue value(jsonRequestParameters);

    Networking::CurlRequest *request = new Networking::CurlRequest(nullptr, nullptr, DROPBOX_API_FILES_DOWNLOAD); //TODO: is it OK to pass no callbacks?
    request->addHeader("Authorization: Bearer " + _token);
    request->addHeader("Dropbox-API-Arg: " + Common::JSON::stringify(&value));
    request->addHeader("Content-Type: "); //required to be empty (as we do POST, it's usually app/form-url-encoded)

    Networking::NetworkReadStreamResponse response = request->execute();
    if (callback)
        (*callback)(response);
    return response.request; // no leak here, response.request == request
}
Beispiel #6
0
void BoxStorage::createDirectoryInnerCallback(BoolCallback outerCallback, Networking::JsonResponse response) {
	Common::JSONValue *json = response.value;
	if (!json) {
		warning("BoxStorage::createDirectoryInnerCallback: NULL passed instead of JSON");
		delete outerCallback;
		return;
	}

	if (outerCallback) {
		if (Networking::CurlJsonRequest::jsonIsObject(json, "BoxStorage::createDirectoryInnerCallback")) {
			Common::JSONObject info = json->asObject();
			(*outerCallback)(BoolResponse(nullptr, info.contains("id")));
		} else {
			(*outerCallback)(BoolResponse(nullptr, false));
		}
		delete outerCallback;
	}

	delete json;
}
Beispiel #7
0
Networking::Request *BoxStorage::createDirectoryWithParentId(Common::String parentId, Common::String name, BoolCallback callback, Networking::ErrorCallback errorCallback) {
	if (!errorCallback)
		errorCallback = getErrorPrintingCallback();

	Common::String url = BOX_API_FOLDERS;
	Networking::JsonCallback innerCallback = new Common::CallbackBridge<BoxStorage, BoolResponse, Networking::JsonResponse>(this, &BoxStorage::createDirectoryInnerCallback, callback);
	Networking::CurlJsonRequest *request = new BoxTokenRefresher(this, innerCallback, errorCallback, url.c_str());
	request->addHeader("Authorization: Bearer " + accessToken());
	request->addHeader("Content-Type: application/json");

	Common::JSONObject parentObject;
	parentObject.setVal("id", new Common::JSONValue(parentId));

	Common::JSONObject jsonRequestParameters;
	jsonRequestParameters.setVal("name", new Common::JSONValue(name));
	jsonRequestParameters.setVal("parent", new Common::JSONValue(parentObject));

	Common::JSONValue value(jsonRequestParameters);
	request->addPostField(Common::JSON::stringify(&value));

	return addRequest(request);
}
void OneDriveStorage::fileInfoCallback(Networking::NetworkReadStreamCallback outerCallback, Networking::JsonResponse response) {
	Common::JSONValue *json = response.value;
	if (!json) {
		warning("OneDriveStorage::fileInfoCallback: NULL passed instead of JSON");
		if (outerCallback)
			(*outerCallback)(Networking::NetworkReadStreamResponse(response.request, nullptr));
		delete outerCallback;
		return;
	}

	if (!Networking::CurlJsonRequest::jsonIsObject(json, "OneDriveStorage::fileInfoCallback")) {
		if (outerCallback)
			(*outerCallback)(Networking::NetworkReadStreamResponse(response.request, nullptr));
		delete json;
		delete outerCallback;
		return;
	}

	Common::JSONObject result = response.value->asObject();
	if (!Networking::CurlJsonRequest::jsonContainsString(result, "@content.downloadUrl", "OneDriveStorage::fileInfoCallback")) {
		warning("OneDriveStorage: downloadUrl not found in passed JSON");
		debug(9, "%s", response.value->stringify().c_str());
		if (outerCallback)
			(*outerCallback)(Networking::NetworkReadStreamResponse(response.request, nullptr));
		delete json;
		delete outerCallback;
		return;
	}

	const char *url = result.getVal("@content.downloadUrl")->asString().c_str();
	if (outerCallback)
		(*outerCallback)(Networking::NetworkReadStreamResponse(
			response.request,
			new Networking::NetworkReadStream(url, nullptr, "")
		));

	delete json;
	delete outerCallback;
}
void OneDriveStorage::infoInnerCallback(StorageInfoCallback outerCallback, Networking::JsonResponse response) {
	Common::JSONValue *json = response.value;
	if (!json) {
		warning("OneDriveStorage::infoInnerCallback: NULL passed instead of JSON");
		delete outerCallback;
		return;
	}

	if (!Networking::CurlJsonRequest::jsonIsObject(json, "OneDriveStorage::infoInnerCallback")) {
		delete json;
		delete outerCallback;
		return;
	}

	Common::JSONObject jsonInfo = json->asObject();

	Common::String uid, displayName, email;
	uint64 quotaUsed = 0, quotaAllocated = 26843545600LL; // 25 GB, because I actually don't know any way to find out the real one

	if (Networking::CurlJsonRequest::jsonContainsObject(jsonInfo, "createdBy", "OneDriveStorage::infoInnerCallback")) {
		Common::JSONObject createdBy = jsonInfo.getVal("createdBy")->asObject();
		if (Networking::CurlJsonRequest::jsonContainsObject(createdBy, "user", "OneDriveStorage::infoInnerCallback")) {
			Common::JSONObject user = createdBy.getVal("user")->asObject();
			if (Networking::CurlJsonRequest::jsonContainsString(user, "id", "OneDriveStorage::infoInnerCallback"))
				uid = user.getVal("id")->asString();
			if (Networking::CurlJsonRequest::jsonContainsString(user, "displayName", "OneDriveStorage::infoInnerCallback"))
				displayName = user.getVal("displayName")->asString();
		}
	}

	if (Networking::CurlJsonRequest::jsonContainsIntegerNumber(jsonInfo, "size", "OneDriveStorage::infoInnerCallback")) {
		quotaUsed = jsonInfo.getVal("size")->asIntegerNumber();
	}

	Common::String username = email;
	if (username == "")
		username = displayName;
	if (username == "")
		username = uid;
	CloudMan.setStorageUsername(kStorageOneDriveId, username);

	if (outerCallback) {
		(*outerCallback)(StorageInfoResponse(nullptr, StorageInfo(uid, displayName, email, quotaUsed, quotaAllocated)));
		delete outerCallback;
	}

	delete json;
}
Beispiel #10
0
Networking::Request *GoogleDriveStorage::createDirectoryWithParentId(Common::String parentId, Common::String name, BoolCallback callback, Networking::ErrorCallback errorCallback) {
	if (!errorCallback)
		errorCallback = getErrorPrintingCallback();

	Common::String url = GOOGLEDRIVE_API_FILES;
	Networking::JsonCallback innerCallback = new Common::CallbackBridge<GoogleDriveStorage, BoolResponse, Networking::JsonResponse>(this, &GoogleDriveStorage::createDirectoryInnerCallback, callback);
	Networking::CurlJsonRequest *request = new GoogleDriveTokenRefresher(this, innerCallback, errorCallback, url.c_str());
	request->addHeader("Authorization: Bearer " + accessToken());
	request->addHeader("Content-Type: application/json");

	Common::JSONArray parentsArray;
	parentsArray.push_back(new Common::JSONValue(parentId));

	Common::JSONObject jsonRequestParameters;
	jsonRequestParameters.setVal("mimeType", new Common::JSONValue("application/vnd.google-apps.folder"));
	jsonRequestParameters.setVal("name", new Common::JSONValue(name));
	jsonRequestParameters.setVal("parents", new Common::JSONValue(parentsArray));

	Common::JSONValue value(jsonRequestParameters);
	request->addPostField(Common::JSON::stringify(&value));

	return addRequest(request);
}
void GoogleDriveUploadRequest::startUpload() {
	Common::String name = _savePath;
	for (uint32 i = name.size(); i > 0; --i) {
		if (name[i - 1] == '/' || name[i - 1] == '\\') {
			name.erase(0, i);
			break;
		}
	}

	Common::String url = GOOGLEDRIVE_API_FILES;
	if (_resolvedId != "")
		url += "/" + ConnMan.urlEncode(_resolvedId);
	url += "?uploadType=resumable&fields=id,mimeType,modifiedTime,name,size";
	Networking::JsonCallback callback = new Common::Callback<GoogleDriveUploadRequest, Networking::JsonResponse>(this, &GoogleDriveUploadRequest::startUploadCallback);
	Networking::ErrorCallback failureCallback = new Common::Callback<GoogleDriveUploadRequest, Networking::ErrorResponse>(this, &GoogleDriveUploadRequest::startUploadErrorCallback);
	Networking::CurlJsonRequest *request = new GoogleDriveTokenRefresher(_storage, callback, failureCallback, url.c_str());
	request->addHeader("Authorization: Bearer " + _storage->accessToken());
	request->addHeader("Content-Type: application/json");
	if (_resolvedId != "")
		request->usePatch();

	Common::JSONObject jsonRequestParameters;
	if (_resolvedId != "") {
		jsonRequestParameters.setVal("id", new Common::JSONValue(_resolvedId));
	} else {
		Common::JSONArray parentsArray;
		parentsArray.push_back(new Common::JSONValue(_parentId));
		jsonRequestParameters.setVal("parents", new Common::JSONValue(parentsArray));
	}
	jsonRequestParameters.setVal("name", new Common::JSONValue(name));

	Common::JSONValue value(jsonRequestParameters);
	request->addPostField(Common::JSON::stringify(&value));

	_workingRequest = ConnMan.addRequest(request);
}
Beispiel #12
0
void BoxStorage::infoInnerCallback(StorageInfoCallback outerCallback, Networking::JsonResponse response) {
	Common::JSONValue *json = response.value;
	if (!json) {
		warning("BoxStorage::infoInnerCallback: NULL passed instead of JSON");
		delete outerCallback;
		return;
	}

	if (!Networking::CurlJsonRequest::jsonIsObject(json, "BoxStorage::infoInnerCallback")) {
		delete json;
		delete outerCallback;
		return;
	}

	Common::JSONObject info = json->asObject();

	Common::String uid, name, email;
	uint64 quotaUsed = 0, quotaAllocated = 0;

	// can check that "type": "user"
	// there is also "max_upload_size", "phone" and "avatar_url"

	if (Networking::CurlJsonRequest::jsonContainsString(info, "id", "BoxStorage::infoInnerCallback"))
		uid = info.getVal("id")->asString();

	if (Networking::CurlJsonRequest::jsonContainsString(info, "name", "BoxStorage::infoInnerCallback"))
		name = info.getVal("name")->asString();

	if (Networking::CurlJsonRequest::jsonContainsString(info, "login", "BoxStorage::infoInnerCallback"))
		email = info.getVal("login")->asString();

	if (Networking::CurlJsonRequest::jsonContainsIntegerNumber(info, "space_amount", "BoxStorage::infoInnerCallback"))
		quotaAllocated = info.getVal("space_amount")->asIntegerNumber();

	if (Networking::CurlJsonRequest::jsonContainsIntegerNumber(info, "space_used", "BoxStorage::infoInnerCallback"))
		quotaUsed = info.getVal("space_used")->asIntegerNumber();

	Common::String username = email;
	if (username == "") username = name;
	if (username == "") username = uid;
	CloudMan.setStorageUsername(kStorageBoxId, username);

	if (outerCallback) {
		(*outerCallback)(StorageInfoResponse(nullptr, StorageInfo(uid, name, email, quotaUsed, quotaAllocated)));
		delete outerCallback;
	}

	delete json;
}
void OneDriveListDirectoryRequest::listedDirectoryCallback(Networking::JsonResponse response) {
	_workingRequest = nullptr;
	Common::JSONValue *json = response.value;

	if (_ignoreCallback) {
		delete json;
		return;
	}

	if (response.request)
		_date = response.request->date();

	Networking::ErrorResponse error(this);
	Networking::CurlJsonRequest *rq = (Networking::CurlJsonRequest *)response.request;
	if (rq && rq->getNetworkReadStream())
		error.httpResponseCode = rq->getNetworkReadStream()->httpResponseCode();

	if (json == nullptr) {
		error.response = "Failed to parse JSON, null passed!";
		finishError(error);
		return;
	}

	if (!json->isObject()) {
		error.response = "Passed JSON is not an object!";
		finishError(error);
		delete json;
		return;
	}

	Common::JSONObject object = json->asObject();

	//check that ALL keys exist AND HAVE RIGHT TYPE to avoid segfaults
	if (!Networking::CurlJsonRequest::jsonContainsArray(object, "value", "OneDriveListDirectoryRequest")) {
		error.response = "\"value\" not found or that's not an array!";
		finishError(error);
		delete json;
		return;
	}

	Common::JSONArray items = object.getVal("value")->asArray();
	for (uint32 i = 0; i < items.size(); ++i) {
		if (!Networking::CurlJsonRequest::jsonIsObject(items[i], "OneDriveListDirectoryRequest")) continue;

		Common::JSONObject item = items[i]->asObject();

		if (!Networking::CurlJsonRequest::jsonContainsAttribute(item, "folder", "OneDriveListDirectoryRequest", true)) continue;
		if (!Networking::CurlJsonRequest::jsonContainsString(item, "name", "OneDriveListDirectoryRequest")) continue;
		if (!Networking::CurlJsonRequest::jsonContainsIntegerNumber(item, "size", "OneDriveListDirectoryRequest")) continue;
		if (!Networking::CurlJsonRequest::jsonContainsString(item, "lastModifiedDateTime", "OneDriveListDirectoryRequest")) continue;

		Common::String path = _currentDirectory + item.getVal("name")->asString();
		bool isDirectory = item.contains("folder");
		uint32 size = item.getVal("size")->asIntegerNumber();
		uint32 timestamp = ISO8601::convertToTimestamp(item.getVal("lastModifiedDateTime")->asString());

		StorageFile file(path, size, timestamp, isDirectory);
		_files.push_back(file);
		if (_requestedRecursive && file.isDirectory()) {
			_directoriesQueue.push_back(file.path());
		}
	}

	bool hasMore = object.contains("@odata.nextLink");
	if (hasMore) {
		if (!Networking::CurlJsonRequest::jsonContainsString(object, "@odata.nextLink", "OneDriveListDirectoryRequest")) {
			error.response = "\"@odata.nextLink\" is not a string!";
			finishError(error);
			delete json;
			return;
		}

		makeRequest(object.getVal("@odata.nextLink")->asString());
	} else {
		listNextDirectory();
	}

	delete json;
}
Beispiel #14
0
void GoogleDriveStorage::infoInnerCallback(StorageInfoCallback outerCallback, Networking::JsonResponse response) {
	Common::JSONValue *json = response.value;
	if (!json) {
		warning("GoogleDriveStorage::infoInnerCallback: NULL passed instead of JSON");
		delete outerCallback;
		return;
	}

	if (!Networking::CurlJsonRequest::jsonIsObject(json, "GoogleDriveStorage::infoInnerCallback")) {
		delete json;
		delete outerCallback;
		return;
	}

	Common::JSONObject info = json->asObject();

	Common::String uid, name, email;
	uint64 quotaUsed = 0, quotaAllocated = 0;

	if (Networking::CurlJsonRequest::jsonContainsAttribute(info, "user", "GoogleDriveStorage::infoInnerCallback") &&
		Networking::CurlJsonRequest::jsonIsObject(info.getVal("user"), "GoogleDriveStorage::infoInnerCallback")) {
		//"me":true, "kind":"drive#user","photoLink": "",
		//"displayName":"Alexander Tkachev","emailAddress":"*****@*****.**","permissionId":""
		Common::JSONObject user = info.getVal("user")->asObject();
		if (Networking::CurlJsonRequest::jsonContainsString(user, "permissionId", "GoogleDriveStorage::infoInnerCallback"))
			uid = user.getVal("permissionId")->asString(); //not sure it's user's id, but who cares anyway?
		if (Networking::CurlJsonRequest::jsonContainsString(user, "displayName", "GoogleDriveStorage::infoInnerCallback"))
			name = user.getVal("displayName")->asString();
		if (Networking::CurlJsonRequest::jsonContainsString(user, "emailAddress", "GoogleDriveStorage::infoInnerCallback"))
			email = user.getVal("emailAddress")->asString();
	}

	if (Networking::CurlJsonRequest::jsonContainsAttribute(info, "storageQuota", "GoogleDriveStorage::infoInnerCallback") &&
		Networking::CurlJsonRequest::jsonIsObject(info.getVal("storageQuota"), "GoogleDriveStorage::infoInnerCallback")) {
		//"usageInDrive":"6332462","limit":"18253611008","usage":"6332462","usageInDriveTrash":"0"
		Common::JSONObject storageQuota = info.getVal("storageQuota")->asObject();

		if (Networking::CurlJsonRequest::jsonContainsString(storageQuota, "usage", "GoogleDriveStorage::infoInnerCallback")) {
			Common::String usage = storageQuota.getVal("usage")->asString();
			quotaUsed = usage.asUint64();
		}

		if (Networking::CurlJsonRequest::jsonContainsString(storageQuota, "limit", "GoogleDriveStorage::infoInnerCallback")) {
			Common::String limit = storageQuota.getVal("limit")->asString();
			quotaAllocated = limit.asUint64();
		}
	}

	CloudMan.setStorageUsername(kStorageGoogleDriveId, email);

	if (outerCallback) {
		(*outerCallback)(StorageInfoResponse(nullptr, StorageInfo(uid, name, email, quotaUsed, quotaAllocated)));
		delete outerCallback;
	}

	delete json;
}
void GoogleDriveUploadRequest::partUploadedCallback(Networking::JsonResponse response) {
	_workingRequest = nullptr;
	if (_ignoreCallback)
		return;

	Networking::ErrorResponse error(this, false, true, "", -1);
	Networking::CurlJsonRequest *rq = (Networking::CurlJsonRequest *)response.request;
	if (rq) {
		const Networking::NetworkReadStream *stream = rq->getNetworkReadStream();
		if (stream) {
			long code = stream->httpResponseCode();
			error.httpResponseCode = code;
			if (code == 308 && handleHttp308(stream)) {
				delete (Common::JSONValue *)response.value;
				return;
			}
		}
	}

	Common::JSONValue *json = response.value;
	if (json == nullptr) {
		error.response = "Failed to parse JSON, null passed!";
		finishError(error);
		return;
	}

	if (json->isObject()) {
		Common::JSONObject object = json->asObject();

		if (object.contains("error")) {
			warning("GoogleDrive returned error: %s", json->stringify(true).c_str());
			error.response = json->stringify(true);
			finishError(error);
			delete json;
			return;
		}

		if (Networking::CurlJsonRequest::jsonContainsString(object, "id", "GoogleDriveUploadRequest") &&
			Networking::CurlJsonRequest::jsonContainsString(object, "name", "GoogleDriveUploadRequest") &&
			Networking::CurlJsonRequest::jsonContainsString(object, "mimeType", "GoogleDriveUploadRequest")) {
			//finished
			Common::String id = object.getVal("id")->asString();
			Common::String name = object.getVal("name")->asString();
			bool isDirectory = (object.getVal("mimeType")->asString() == "application/vnd.google-apps.folder");
			uint32 size = 0, timestamp = 0;
			if (Networking::CurlJsonRequest::jsonContainsString(object, "size", "GoogleDriveUploadRequest", true))
				size = object.getVal("size")->asString().asUint64();
			if (Networking::CurlJsonRequest::jsonContainsString(object, "modifiedTime", "GoogleDriveUploadRequest", true))
				timestamp = ISO8601::convertToTimestamp(object.getVal("modifiedTime")->asString());

			finishUpload(StorageFile(id, _savePath, name, size, timestamp, isDirectory));
			return;
		}
	}

	if (_contentsStream->eos() || _contentsStream->pos() >= _contentsStream->size() - 1) {
		warning("GoogleDriveUploadRequest: no file info to return");
		finishUpload(StorageFile(_savePath, 0, 0, false));
	} else {
		uploadNextPart();
	}

	delete json;
}