Commit 2e0cbee0 authored by Paweł Wegner's avatar Paweł Wegner

Don't own user callbacks for too long.

parent eab226b8
......@@ -100,12 +100,14 @@ ICloudProvider::MoveItemRequest::Pointer AmazonDrive::moveItemAsync(
IItem::Pointer source, IItem::Pointer destination,
MoveItemCallback callback) {
auto r = std::make_shared<Request<EitherError<void>>>(shared_from_this());
r->set([=](Request<EitherError<void>>::Pointer r) {
move(r, http(), metadata_url(),
std::make_shared<std::vector<std::string>>(
static_cast<Item*>(source.get())->parents()),
source, destination, callback);
});
r->set(
[=](Request<EitherError<void>>::Pointer r) {
move(r, http(), metadata_url(),
std::make_shared<std::vector<std::string>>(
static_cast<Item*>(source.get())->parents()),
source, destination, callback);
},
callback);
return r->run();
}
......
......@@ -141,10 +141,7 @@ void rename(Request<EitherError<void>>::Pointer r, IHttp* http,
if (e.left()) complete(e.left());
r->subrequest(r->provider()->listDirectoryAsync(
e.right(), [=](EitherError<std::vector<IItem::Pointer>> e) {
if (e.left()) {
complete(e.left());
return r->done(e.left());
}
if (e.left()) return r->done(e.left());
rename(r, http, region, e.right(), dest_id, source_id,
[=](EitherError<void> e) {
if (e.left())
......@@ -219,34 +216,34 @@ ICloudProvider::MoveItemRequest::Pointer AmazonS3::moveItemAsync(
IItem::Pointer source, IItem::Pointer destination,
MoveItemCallback callback) {
auto r = std::make_shared<Request<EitherError<void>>>(shared_from_this());
r->set([=](Request<EitherError<void>>::Pointer r) {
auto data = AmazonS3::extract(destination->id());
rename(r, http(), region(), {data.first, data.second + source->filename()},
AmazonS3::extract(source->id()), [=](EitherError<void> e) {
callback(e);
r->done(e);
});
});
r->set(
[=](Request<EitherError<void>>::Pointer r) {
auto data = AmazonS3::extract(destination->id());
rename(r, http(), region(),
{data.first, data.second + source->filename()},
AmazonS3::extract(source->id()),
[=](EitherError<void> e) { r->done(e); });
},
callback);
return r->run();
}
ICloudProvider::RenameItemRequest::Pointer AmazonS3::renameItemAsync(
IItem::Pointer item, const std::string& name, RenameItemCallback callback) {
auto r = std::make_shared<Request<EitherError<void>>>(shared_from_this());
r->set([=](Request<EitherError<void>>::Pointer r) {
auto data = extract(item->id());
auto path = data.second;
if (!path.empty() && path.back() == '/') path.pop_back();
if (path.find_first_of('/') == std::string::npos)
path = "";
else
path = getPath(path) + "/";
rename(r, http(), region(), {data.first, path + name}, extract(item->id()),
[=](EitherError<void> e) {
callback(e);
r->done(e);
});
});
r->set(
[=](Request<EitherError<void>>::Pointer r) {
auto data = extract(item->id());
auto path = data.second;
if (!path.empty() && path.back() == '/') path.pop_back();
if (path.find_first_of('/') == std::string::npos)
path = "";
else
path = getPath(path) + "/";
rename(r, http(), region(), {data.first, path + name},
extract(item->id()), [=](EitherError<void> e) { r->done(e); });
},
callback);
return r->run();
}
......@@ -254,147 +251,131 @@ ICloudProvider::CreateDirectoryRequest::Pointer AmazonS3::createDirectoryAsync(
IItem::Pointer parent, const std::string& name,
CreateDirectoryCallback callback) {
auto r = std::make_shared<Request<EitherError<IItem>>>(shared_from_this());
r->set([=](Request<EitherError<IItem>>::Pointer r) {
auto output = std::make_shared<std::stringstream>();
r->sendRequest(
[=](util::Output) {
auto data = extract(parent->id());
return http()->create("https://" + data.first + ".s3." + region() +
".amazonaws.com/" +
escapePath(data.second + name + "/"),
"PUT");
},
[=](EitherError<util::Output> e) {
if (e.left()) {
callback(e.left());
r->done(e.left());
} else {
auto data = extract(parent->id());
auto item = std::make_shared<Item>(
name,
util::to_base64(to_string({data.first, data.second + "/"})), 0,
IItem::UnknownTimeStamp, IItem::FileType::Directory);
callback(EitherError<IItem>(item));
r->done(EitherError<IItem>(item));
}
},
output);
});
r->set(
[=](Request<EitherError<IItem>>::Pointer r) {
auto output = std::make_shared<std::stringstream>();
r->sendRequest(
[=](util::Output) {
auto data = extract(parent->id());
return http()->create("https://" + data.first + ".s3." +
region() + ".amazonaws.com/" +
escapePath(data.second + name + "/"),
"PUT");
},
[=](EitherError<util::Output> e) {
if (e.left()) {
r->done(e.left());
} else {
auto data = extract(parent->id());
auto item = std::make_shared<Item>(
name,
util::to_base64(to_string({data.first, data.second + "/"})),
0, IItem::UnknownTimeStamp, IItem::FileType::Directory);
r->done(EitherError<IItem>(item));
}
},
output);
},
callback);
return r->run();
}
ICloudProvider::DeleteItemRequest::Pointer AmazonS3::deleteItemAsync(
IItem::Pointer item, DeleteItemCallback callback) {
auto r = std::make_shared<Request<EitherError<void>>>(shared_from_this());
r->set([=](Request<EitherError<void>>::Pointer r) {
auto release = [=] {
auto output = std::make_shared<std::stringstream>();
r->sendRequest(
[=](util::Output) {
auto data = extract(item->id());
return http()->create("https://" + data.first + ".s3." + region() +
".amazonaws.com/" +
escapePath(data.second),
"DELETE");
},
[=](EitherError<util::Output> e) {
if (e.left()) {
callback(e.left());
r->done(e.left());
} else {
callback(nullptr);
r->done(nullptr);
}
},
output);
};
if (item->type() == IItem::FileType::Directory) {
r->subrequest(r->provider()->listDirectoryAsync(
item, [=](EitherError<std::vector<IItem::Pointer>> e) {
if (e.left()) {
callback(e.left());
return r->done(e.left());
}
remove(r, e.right(), [=](EitherError<void> e) {
if (e.left()) {
callback(e.left());
return r->done(e.left());
}
release();
});
}));
} else
release();
});
r->set(
[=](Request<EitherError<void>>::Pointer r) {
auto release = [=] {
auto output = std::make_shared<std::stringstream>();
r->sendRequest(
[=](util::Output) {
auto data = extract(item->id());
return http()->create("https://" + data.first + ".s3." +
region() + ".amazonaws.com/" +
escapePath(data.second),
"DELETE");
},
[=](EitherError<util::Output> e) {
r->done(e.left() ? e.left() : nullptr);
},
output);
};
if (item->type() == IItem::FileType::Directory) {
r->subrequest(r->provider()->listDirectoryAsync(
item, [=](EitherError<std::vector<IItem::Pointer>> e) {
if (e.left()) return r->done(e.left());
remove(r, e.right(), [=](EitherError<void> e) {
if (e.left()) return r->done(e.left());
release();
});
}));
} else
release();
},
callback);
return r->run();
}
ICloudProvider::GetItemDataRequest::Pointer AmazonS3::getItemDataAsync(
const std::string& id, GetItemCallback callback) {
auto r = std::make_shared<Request<EitherError<IItem>>>(shared_from_this());
r->set([=](Request<EitherError<IItem>>::Pointer r) {
if (id == rootDirectory()->id()) {
callback(rootDirectory());
return r->done(rootDirectory());
}
auto data = extract(id);
if (data.second.empty()) {
auto item = std::make_shared<Item>(data.first, id, IItem::UnknownSize,
IItem::UnknownTimeStamp,
IItem::FileType::Directory);
callback(EitherError<IItem>(item));
return r->done(EitherError<IItem>(item));
}
auto factory = [=](util::Output) {
auto request = http()->create(
"https://" + data.first + ".s3." + region() + ".amazonaws.com/",
"GET");
request->setParameter("list-type", "2");
request->setParameter("prefix", data.second);
request->setParameter("delimiter", "/");
return request;
};
auto output = std::make_shared<std::stringstream>();
r->sendRequest(
factory,
[=](EitherError<util::Output> e) {
if (e.left()) {
callback(e.left());
return r->done(e.left());
}
std::stringstream sstream;
sstream << output->rdbuf();
tinyxml2::XMLDocument document;
if (document.Parse(sstream.str().c_str(), sstream.str().size()) !=
tinyxml2::XML_SUCCESS) {
Error e{IHttpRequest::Failure, "invalid xml"};
callback(e);
return r->done(e);
}
auto node = document.RootElement();
auto size = IItem::UnknownSize;
auto timestamp = IItem::UnknownTimeStamp;
if (auto contents_element = node->FirstChildElement("Contents")) {
if (auto size_element = contents_element->FirstChildElement("Size"))
if (auto text = size_element->GetText()) size = std::atoll(text);
if (auto time_element =
contents_element->FirstChildElement("LastModified"))
if (auto text = time_element->GetText())
timestamp = util::parse_time(text);
}
auto type = data.second.back() == '/' ? IItem::FileType::Directory
: IItem::FileType::Unknown;
auto item = std::make_shared<Item>(
getFilename(data.second), id,
type == IItem::FileType::Directory ? IItem::UnknownSize : size,
timestamp, type);
if (item->type() != IItem::FileType::Directory)
item->set_url(getUrl(*item));
callback(EitherError<IItem>(item));
r->done(EitherError<IItem>(item));
},
output);
});
r->set(
[=](Request<EitherError<IItem>>::Pointer r) {
if (id == rootDirectory()->id()) return r->done(rootDirectory());
auto data = extract(id);
if (data.second.empty()) {
auto item = std::make_shared<Item>(data.first, id, IItem::UnknownSize,
IItem::UnknownTimeStamp,
IItem::FileType::Directory);
return r->done(EitherError<IItem>(item));
}
auto factory = [=](util::Output) {
auto request = http()->create(
"https://" + data.first + ".s3." + region() + ".amazonaws.com/",
"GET");
request->setParameter("list-type", "2");
request->setParameter("prefix", data.second);
request->setParameter("delimiter", "/");
return request;
};
auto output = std::make_shared<std::stringstream>();
r->sendRequest(
factory,
[=](EitherError<util::Output> e) {
if (e.left()) return r->done(e.left());
std::stringstream sstream;
sstream << output->rdbuf();
tinyxml2::XMLDocument document;
if (document.Parse(sstream.str().c_str(), sstream.str().size()) !=
tinyxml2::XML_SUCCESS)
return r->done(Error{IHttpRequest::Failure, "invalid xml"});
auto node = document.RootElement();
auto size = IItem::UnknownSize;
auto timestamp = IItem::UnknownTimeStamp;
if (auto contents_element = node->FirstChildElement("Contents")) {
if (auto size_element =
contents_element->FirstChildElement("Size"))
if (auto text = size_element->GetText())
size = std::atoll(text);
if (auto time_element =
contents_element->FirstChildElement("LastModified"))
if (auto text = time_element->GetText())
timestamp = util::parse_time(text);
}
auto type = data.second.back() == '/' ? IItem::FileType::Directory
: IItem::FileType::Unknown;
auto item = std::make_shared<Item>(
getFilename(data.second), id,
type == IItem::FileType::Directory ? IItem::UnknownSize
: size,
timestamp, type);
if (item->type() != IItem::FileType::Directory)
item->set_url(getUrl(*item));
r->done(EitherError<IItem>(item));
},
output);
},
callback);
return r->run();
}
......
......@@ -52,69 +52,63 @@ bool Box::reauthorize(int code) const {
ICloudProvider::GetItemDataRequest::Pointer Box::getItemDataAsync(
const std::string& id, GetItemDataCallback callback) {
auto r = std::make_shared<Request<EitherError<IItem>>>(shared_from_this());
r->set([=](Request<EitherError<IItem>>::Pointer r) {
auto output = std::make_shared<std::stringstream>();
r->sendRequest(
[this, id](util::Output) {
return http()->create(endpoint() + "/2.0/files/" + id, "GET");
},
[=](EitherError<util::Output> e) {
if (e.left()) {
r->sendRequest(
[this, id](util::Output) {
return http()->create(endpoint() + "/2.0/folders/" + id,
"GET");
},
[=](EitherError<util::Output> e) {
if (e.left()) {
callback(e.left());
r->done(e.left());
} else {
try {
Json::Value response;
*output >> response;
auto item = toItem(response);
callback(item);
r->done(item);
} catch (std::exception) {
Error err{IHttpRequest::Failure, output->str()};
callback(err);
r->done(err);
}
}
},
output);
} else {
try {
Json::Value response;
*output >> response;
auto item = toItem(response);
r->sendRequest(
[this, id](util::Output) {
auto request = http()->create(
endpoint() + "/2.0/files/" + id + "/content", "GET",
false);
return request;
},
[=](EitherError<util::Output> e) {
if (e.left() && IHttpRequest::isRedirect(e.left()->code_)) {
static_cast<Item*>(item.get())
->set_url(e.left()->description_);
}
callback(item);
r->done(item);
},
output);
} catch (Json::Exception e) {
Error err{IHttpRequest::Failure, e.what()};
callback(err);
r->done(err);
}
}
},
output);
});
r->set(
[=](Request<EitherError<IItem>>::Pointer r) {
auto output = std::make_shared<std::stringstream>();
r->sendRequest(
[this, id](util::Output) {
return http()->create(endpoint() + "/2.0/files/" + id, "GET");
},
[=](EitherError<util::Output> e) {
if (e.left()) {
r->sendRequest(
[this, id](util::Output) {
return http()->create(endpoint() + "/2.0/folders/" + id,
"GET");
},
[=](EitherError<util::Output> e) {
if (e.left())
r->done(e.left());
else {
try {
Json::Value response;
*output >> response;
r->done(toItem(response));
} catch (std::exception) {
r->done(Error{IHttpRequest::Failure, output->str()});
}
}
},
output);
} else {
try {
Json::Value response;
*output >> response;
auto item = toItem(response);
r->sendRequest(
[this, id](util::Output) {
auto request = http()->create(
endpoint() + "/2.0/files/" + id + "/content", "GET",
false);
return request;
},
[=](EitherError<util::Output> e) {
if (e.left() &&
IHttpRequest::isRedirect(e.left()->code_)) {
static_cast<Item*>(item.get())
->set_url(e.left()->description_);
}
r->done(item);
},
output);
} catch (Json::Exception e) {
r->done(Error{IHttpRequest::Failure, e.what()});
}
}
},
output);
},
callback);
return r->run();
}
......
......@@ -54,72 +54,59 @@ bool Dropbox::reauthorize(int code) const {
ICloudProvider::GetItemDataRequest::Pointer Dropbox::getItemDataAsync(
const std::string& id, GetItemDataCallback callback) {
auto r = std::make_shared<Request<EitherError<IItem>>>(shared_from_this());
r->set([=](Request<EitherError<IItem>>::Pointer r) {
auto output = std::make_shared<std::stringstream>();
r->sendRequest(
[=](util::Output input) {
auto request =
http()->create(endpoint() + "/2/files/get_metadata", "POST");
request->setHeaderParameter("Content-Type", "application/json");
Json::Value parameter;
parameter["path"] = id;
parameter["include_media_info"] = true;
*input << Json::FastWriter().write(parameter);
return request;
},
[=](EitherError<util::Output> e) {
if (e.left()) {
callback(e.left());
r->done(e.left());
} else {
try {
Json::Value response;
*output >> response;
auto item = toItem(response);
if (item->type() == IItem::FileType::Directory) {
callback(item);
return r->done(item);
}
r->sendRequest(
[=](util::Output input) {
auto request = http()->create(
endpoint() + "/2/files/get_temporary_link", "POST");
request->setHeaderParameter("Content-Type",
"application/json");
Json::Value parameter;
parameter["path"] = id;
*input << Json::FastWriter().write(parameter);
return request;
},
[=](EitherError<util::Output> e) {
if (e.left()) {
callback(item);
r->done(item);
} else {
r->set(
[=](Request<EitherError<IItem>>::Pointer r) {
auto output = std::make_shared<std::stringstream>();
r->sendRequest(
[=](util::Output input) {
auto request =
http()->create(endpoint() + "/2/files/get_metadata", "POST");
request->setHeaderParameter("Content-Type", "application/json");
Json::Value parameter;
parameter["path"] = id;
parameter["include_media_info"] = true;
*input << Json::FastWriter().write(parameter);
return request;
},
[=](EitherError<util::Output> e) {
if (e.left()) return r->done(e.left());
try {
Json::Value response;
*output >> response;
auto item = toItem(response);
if (item->type() == IItem::FileType::Directory)
return r->done(item);
r->sendRequest(
[=](util::Output input) {
auto request = http()->create(
endpoint() + "/2/files/get_temporary_link", "POST");
request->setHeaderParameter("Content-Type",
"application/json");
Json::Value parameter;
parameter["path"] = id;
*input << Json::FastWriter().write(parameter);
return request;
},
[=](EitherError<util::Output> e) {
if (e.left()) return r->done(item);
try {
Json::Value response;
*output >> response;
static_cast<Item*>(item.get())
->set_url(response["link"].asString());
callback(item);
r->done(item);
} catch (std::exception) {
Error err{IHttpRequest::Failure, output->str()};
callback(err);
r->done(err);
r->done(Error{IHttpRequest::Failure, output->str()});
}
}
},
output);
} catch (std::exception) {
Error err{IHttpRequest::Failure, output->str()};
callback(err);
r->done(err);
}
}
},
output);
});
},
output);
} catch (std::exception) {
r->done(Error{IHttpRequest::Failure, output->str()});
}
},
output);
},
callback);
return r->run();
}
......
......@@ -212,8 +212,8 @@ class TransferListener : public mega::MegaTransferListener, public Listener {
condition_.notify_all();
}
IDownloadFileCallback::Pointer download_callback_;
IUploadFileCallback::Pointer upload_callback_;
IDownloadFileCallback* download_callback_ = nullptr;
IUploadFileCallback* upload_callback_ = nullptr;
MegaApi* mega_ = nullptr;
int transfer_ = 0;
};
......@@ -282,36 +282,36 @@ class HttpData : public IHttpServer::IResponse::ICallback {
HttpData(Buffer::Pointer d, std::shared_ptr<MegaNz> mega,
std::string filename, Range range)
: status_(AuthInProgress),
buffer_(d),
mega_(mega),
request_(std::make_shared<Request<EitherError<void>>>(mega)) {
auto p = mega.get();
p->ensureAuthorized<EitherError<void>>(
request_,
[=](EitherError<void> e) {
status_ = AuthFailed;
request_->done(e);
buffer_->resume();
: status_(AuthInProgress), buffer_(d), mega_(mega) {
auto r = std::make_shared<Request<EitherError<void>>>(mega);
r->set(
[=](Request<EitherError<void>>::Pointer r) {
auto p = static_cast<MegaNz*>(r->provider().get());
p->ensureAuthorized<EitherError<void>>(r, [=]() {
std::unique_ptr<MegaNode> node(
p->mega()->getNodeByPath(filename.c_str()));
if (!node ||
range.start_ + range.size_ > (uint64_t)node->getSize()) {
status_ = AuthFailed;
if (!node)
r->done(Error{IHttpRequest::Bad, "invalid node"});
else
r->done(Error{IHttpRequest::Bad, "invalid range"});
} else {
status_ = AuthSuccess;
callback_ = util::make_unique<HttpDataCallback>(buffer_);
p->downloadResolver(p->toItem(node.get()), callback_.get(),
range)(r);
p->addStreamRequest(r);
}
buffer_->resume();
});
},
[=]() {
std::unique_ptr<MegaNode> node(
p->mega()->getNodeByPath(filename.c_str()));
if (!node || range.start_ + range.size_ > (uint64_t)node->getSize()) {
status_ = AuthFailed;
if (!node)
request_->done(Error{IHttpRequest::Bad, "invalid node"});
else
request_->done(Error{IHttpRequest::Bad, "invalid range"});
} else {
status_ = AuthSuccess;
p->downloadResolver(p->toItem(node.get()),
util::make_unique<HttpDataCallback>(buffer_),
range)(request_);
p->addStreamRequest(request_);
}
[=](EitherError<void> e) {
if (e.left()) status_ = AuthFailed;
buffer_->resume();
});
request_ = r->run();
}
~HttpData() { mega_->removeStreamRequest(request_); }
......@@ -328,7 +328,8 @@ class HttpData : public IHttpServer::IResponse::ICallback {
std::atomic_int status_;
Buffer::Pointer buffer_;
std::shared_ptr<MegaNz> mega_;
std::shared_ptr<Request<EitherError<void>>> request_;
std::shared_ptr<ICloudProvider::DownloadFileRequest> request_;