Commit 9c8d98dc authored by Paweł Wegner's avatar Paweł Wegner

Request: use RAII instead of set.

parent 92fa1b96
......@@ -97,22 +97,21 @@ IItem::Pointer AmazonDrive::rootDirectory() const {
ICloudProvider::MoveItemRequest::Pointer AmazonDrive::moveItemAsync(
IItem::Pointer source, IItem::Pointer destination,
MoveItemCallback callback) {
auto r = std::make_shared<Request<EitherError<IItem>>>(shared_from_this());
r->set(
[=](Request<EitherError<IItem>>::Pointer r) {
move<EitherError<IItem>>(
r, http(), metadata_url(),
std::make_shared<std::vector<std::string>>(
static_cast<Item*>(source.get())->parents()),
source, destination, [=](EitherError<void> e) {
if (e.left())
r->done(e.left());
else
r->done(source);
});
},
callback);
return r->run();
return std::make_shared<Request<EitherError<IItem>>>(
shared_from_this(), callback,
[=](Request<EitherError<IItem>>::Pointer r) {
move<EitherError<IItem>>(
r, http(), metadata_url(),
std::make_shared<std::vector<std::string>>(
static_cast<Item*>(source.get())->parents()),
source, destination, [=](EitherError<void> e) {
if (e.left())
r->done(e.left());
else
r->done(source);
});
})
->run();
}
AuthorizeRequest::Pointer AmazonDrive::authorizeAsync() {
......@@ -306,11 +305,11 @@ AmazonDrive::Auth::Auth() {
}
std::string AmazonDrive::Auth::authorizeLibraryUrl() const {
std::string url =
"https://www.amazon.com/ap/oa?client_id=" + client_id() +
"&redirect_uri=" + redirect_uri() +
"&response_type=code&scope=clouddrive:write+clouddrive:read_all&state=" +
state();
std::string url = "https://www.amazon.com/ap/oa?client_id=" + client_id() +
"&redirect_uri=" + redirect_uri() +
"&response_type=code&scope=clouddrive:write+clouddrive:"
"read_all&state=" +
state();
return url;
}
......
......@@ -209,56 +209,57 @@ AuthorizeRequest::Pointer AmazonS3::authorizeAsync() {
ICloudProvider::MoveItemRequest::Pointer AmazonS3::moveItemAsync(
IItem::Pointer source, IItem::Pointer destination,
MoveItemCallback callback) {
auto r = std::make_shared<Request<EitherError<IItem>>>(shared_from_this());
r->set(
[=](Request<EitherError<IItem>>::Pointer r) {
auto data = AmazonS3::extract(destination->id());
rename<EitherError<IItem>>(
r, http(), region(), {data.first, data.second + source->filename()},
AmazonS3::extract(source->id()), [=](EitherError<void> e) {
if (e.left()) return r->done(e.left());
auto path =
data.second + source->filename() +
(source->type() == IItem::FileType::Directory ? "/" : "");
auto nitem = std::make_shared<Item>(
source->filename(), to_string({data.first, path}),
source->size(), source->timestamp(), source->type());
nitem->set_url(getUrl(*nitem));
r->done(std::static_pointer_cast<IItem>(nitem));
});
},
callback);
return r->run();
return std::make_shared<Request<EitherError<IItem>>>(
shared_from_this(), callback,
[=](Request<EitherError<IItem>>::Pointer r) {
auto data = AmazonS3::extract(destination->id());
rename<EitherError<IItem>>(
r, http(), region(),
{data.first, data.second + source->filename()},
AmazonS3::extract(source->id()), [=](EitherError<void> e) {
if (e.left()) return r->done(e.left());
auto path =
data.second + source->filename() +
(source->type() == IItem::FileType::Directory ? "/"
: "");
auto nitem = std::make_shared<Item>(
source->filename(), to_string({data.first, path}),
source->size(), source->timestamp(), source->type());
nitem->set_url(getUrl(*nitem));
r->done(std::static_pointer_cast<IItem>(nitem));
});
})
->run();
}
ICloudProvider::RenameItemRequest::Pointer AmazonS3::renameItemAsync(
IItem::Pointer item, const std::string& name, RenameItemCallback callback) {
auto r = std::make_shared<Request<EitherError<IItem>>>(shared_from_this());
r->set(
[=](Request<EitherError<IItem>>::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<EitherError<IItem>>(
r, http(), region(), {data.first, path + name}, extract(item->id()),
[=](EitherError<void> e) {
if (e.left()) return r->done(e.left());
auto npath =
path + name +
(item->type() == IItem::FileType::Directory ? "/" : "");
auto nitem = std::make_shared<Item>(
name, to_string({data.first, npath}), item->size(),
item->timestamp(), item->type());
nitem->set_url(getUrl(*nitem));
r->done(std::static_pointer_cast<IItem>(nitem));
});
},
callback);
return r->run();
return std::make_shared<Request<EitherError<IItem>>>(
shared_from_this(), callback,
[=](Request<EitherError<IItem>>::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<EitherError<IItem>>(
r, http(), region(), {data.first, path + name},
extract(item->id()), [=](EitherError<void> e) {
if (e.left()) return r->done(e.left());
auto npath =
path + name +
(item->type() == IItem::FileType::Directory ? "/"
: "");
auto nitem = std::make_shared<Item>(
name, to_string({data.first, npath}), item->size(),
item->timestamp(), item->type());
nitem->set_url(getUrl(*nitem));
r->done(std::static_pointer_cast<IItem>(nitem));
});
})
->run();
}
IHttpRequest::Pointer AmazonS3::createDirectoryRequest(const IItem& parent,
......@@ -282,94 +283,99 @@ IItem::Pointer AmazonS3::createDirectoryResponse(const IItem& parent,
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 = [=] {
r->request(
[=](util::Output) {
auto data = extract(item->id());
return http()->create("https://" + data.first + ".s3." +
region() + ".amazonaws.com/" +
escapePath(data.second),
"DELETE");
},
[=](EitherError<Response> e) {
r->done(e.left() ? e.left() : nullptr);
});
};
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<EitherError<void>>(r, e.right(),
[=](EitherError<void> e) {
if (e.left())
return r->done(e.left());
release();
});
}));
} else
release();
},
callback);
return r->run();
return std::make_shared<Request<EitherError<void>>>(
shared_from_this(), callback,
[=](Request<EitherError<void>>::Pointer r) {
auto release = [=] {
r->request(
[=](util::Output) {
auto data = extract(item->id());
return http()->create("https://" + data.first + ".s3." +
region() + ".amazonaws.com/" +
escapePath(data.second),
"DELETE");
},
[=](EitherError<Response> e) {
r->done(e.left() ? e.left() : nullptr);
});
};
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<EitherError<void>>(r, e.right(),
[=](EitherError<void> e) {
if (e.left())
return r->done(e.left());
release();
});
}));
} else
release();
})
->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()) 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;
};
r->request(factory, [=](EitherError<Response> e) {
if (e.left()) return r->done(e.left());
std::stringstream sstream;
sstream << e.right()->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));
});
},
callback);
return r->run();
return std::make_shared<Request<EitherError<IItem>>>(
shared_from_this(), callback,
[=](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;
};
r->request(factory, [=](EitherError<Response> e) {
if (e.left()) return r->done(e.left());
std::stringstream sstream;
sstream << e.right()->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));
});
})
->run();
}
IHttpRequest::Pointer AmazonS3::listDirectoryRequest(
......
......@@ -34,7 +34,6 @@
#include <condition_variable>
#include <cstring>
#include <fstream>
#include <iostream>
#include <queue>
using namespace mega;
......@@ -284,41 +283,46 @@ class HttpData : public IHttpServer::IResponse::ICallback {
static constexpr int AuthFailed = 2;
HttpData(Buffer::Pointer d, std::shared_ptr<MegaNz> mega,
std::string filename, Range range)
: 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();
});
},
[=](EitherError<void> e) {
if (e.left()) status_ = AuthFailed;
buffer_->resume();
});
request_ = r->run();
}
const std::string& filename, Range range)
: status_(AuthInProgress),
buffer_(d),
mega_(mega),
request_(request(mega, filename, range)) {}
~HttpData() { mega_->removeStreamRequest(request_); }
std::shared_ptr<ICloudProvider::DownloadFileRequest> request(
std::shared_ptr<MegaNz> mega, const std::string& filename, Range range) {
auto resolver = [=](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();
});
};
return std::make_shared<Request<EitherError<void>>>(
mega,
[=](EitherError<void> e) {
if (e.left()) status_ = AuthFailed;
buffer_->resume();
},
resolver)
->run();
}
int putData(char* buf, size_t max) override {
if (status_ == AuthFailed)
return Abort;
......@@ -331,8 +335,8 @@ class HttpData : public IHttpServer::IResponse::ICallback {
std::atomic_int status_;
Buffer::Pointer buffer_;
std::shared_ptr<MegaNz> mega_;
std::shared_ptr<ICloudProvider::DownloadFileRequest> request_;
std::unique_ptr<HttpDataCallback> callback_;
std::shared_ptr<ICloudProvider::DownloadFileRequest> request_;
};
} // namespace
......@@ -481,18 +485,18 @@ ICloudProvider::Hints MegaNz::hints() const {
ICloudProvider::ExchangeCodeRequest::Pointer MegaNz::exchangeCodeAsync(
const std::string& code, ExchangeCodeCallback callback) {
auto r = std::make_shared<Request<EitherError<Token>>>(shared_from_this());
r->set(
[=](Request<EitherError<Token>>::Pointer r) {
auto token = authorizationCodeToToken(code);
auto ret = token->token_.empty()
return std::make_shared<Request<EitherError<Token>>>(
shared_from_this(), callback,
[=](Request<EitherError<Token>>::Pointer r) {
auto token = authorizationCodeToToken(code);
auto ret =
token->token_.empty()
? EitherError<Token>(Error{IHttpRequest::Failure,
"invalid authorization code"})
: EitherError<Token>({token->token_, ""});
r->done(ret);
},
callback);
return r->run();
r->done(ret);
})
->run();
}
AuthorizeRequest::Pointer MegaNz::authorizeAsync() {
......@@ -537,276 +541,267 @@ AuthorizeRequest::Pointer MegaNz::authorizeAsync() {
ICloudProvider::GetItemDataRequest::Pointer MegaNz::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) {
ensureAuthorized<EitherError<IItem>>(r, [=] {
std::unique_ptr<mega::MegaNode> node(
mega_->getNodeByPath(id.c_str()));
if (!node) return r->done(Error{IHttpRequest::NotFound, "not found"});
return r->done(toItem(node.get()));
});
},
callback);
return r->run();
return std::make_shared<Request<EitherError<IItem>>>(
shared_from_this(), callback,
[=](Request<EitherError<IItem>>::Pointer r) {
ensureAuthorized<EitherError<IItem>>(r, [=] {
std::unique_ptr<mega::MegaNode> node(
mega_->getNodeByPath(id.c_str()));
if (!node)
return r->done(Error{IHttpRequest::NotFound, "not found"});
return r->done(toItem(node.get()));
});
})
->run();
}
ICloudProvider::ListDirectoryRequest::Pointer MegaNz::listDirectoryAsync(
IItem::Pointer item, IListDirectoryCallback::Pointer cb) {
using ItemList = EitherError<std::vector<IItem::Pointer>>;
auto r = std::make_shared<Request<ItemList>>(shared_from_this());
auto callback = cb.get();
r->set(
[=](Request<ItemList>::Pointer r) {
ensureAuthorized<ItemList>(r, [=] {
std::unique_ptr<mega::MegaNode> node(
mega_->getNodeByPath(item->id().c_str()));
if (node) {
std::vector<IItem::Pointer> result;
std::unique_ptr<mega::MegaNodeList> lst(
mega_->getChildren(node.get()));
if (lst) {
for (int i = 0; i < lst->size(); i++) {
auto item = toItem(lst->get(i));
result.push_back(item);
callback->receivedItem(item);
}
}
r->done(result);
} else {
r->done(Error{IHttpRequest::NotFound, "node not found"});
auto resolver = [=](Request<ItemList>::Pointer r) {
ensureAuthorized<ItemList>(r, [=] {
std::unique_ptr<mega::MegaNode> node(
mega_->getNodeByPath(item->id().c_str()));
if (node) {
std::vector<IItem::Pointer> result;
std::unique_ptr<mega::MegaNodeList> lst(mega_->getChildren(node.get()));
if (lst) {
for (int i = 0; i < lst->size(); i++) {
auto item = toItem(lst->get(i));
result.push_back(item);
callback->receivedItem(item);
}
});
},
[=](ItemList e) { cb->done(e); });
return r->run();
}
r->done(result);
} else {
r->done(Error{IHttpRequest::NotFound, "node not found"});
}
});
};
return std::make_shared<Request<ItemList>>(
shared_from_this(), [=](ItemList e) { cb->done(e); }, resolver)
->run();
}
ICloudProvider::DownloadFileRequest::Pointer MegaNz::downloadFileAsync(
IItem::Pointer item, IDownloadFileCallback::Pointer callback, Range range) {
auto r = std::make_shared<Request<EitherError<void>>>(shared_from_this());
r->set(downloadResolver(item, callback.get(), range),
[=](EitherError<void> e) { callback->done(e); });
return r->run();
return std::make_shared<Request<EitherError<void>>>(
shared_from_this(),
[=](EitherError<void> e) { callback->done(e); },
downloadResolver(item, callback.get(), range))
->run();
}
ICloudProvider::UploadFileRequest::Pointer MegaNz::uploadFileAsync(
IItem::Pointer item, const std::string& filename,
IUploadFileCallback::Pointer cb) {
auto r = std::make_shared<Request<EitherError<IItem>>>(shared_from_this());
auto callback = cb.get();
r->set(
[=](Request<EitherError<IItem>>::Pointer r) {
ensureAuthorized<EitherError<IItem>>(r, [=] {
std::string cache = temporaryFileName();
{
std::fstream mega_cache(cache.c_str(),
std::fstream::out | std::fstream::binary);
if (!mega_cache)
return r->done(Error{IHttpRequest::Forbidden,
"couldn't open cache file" + cache});
std::array<char, BUFFER_SIZE> buffer;
while (auto length =
callback->putData(buffer.data(), BUFFER_SIZE)) {
if (r->is_cancelled()) {
std::remove(cache.c_str());
return r->done(Error{IHttpRequest::Aborted, ""});
}
mega_cache.write(buffer.data(), length);
}
auto resolver = [=](Request<EitherError<IItem>>::Pointer r) {
ensureAuthorized<EitherError<IItem>>(r, [=] {
std::string cache = temporaryFileName();
{
std::fstream mega_cache(cache.c_str(),
std::fstream::out | std::fstream::binary);
if (!mega_cache)
return r->done(Error{IHttpRequest::Forbidden,
"couldn't open cache file" + cache});
std::array<char, BUFFER_SIZE> buffer;
while (auto length = callback->putData(buffer.data(), BUFFER_SIZE)) {
if (r->is_cancelled()) {
std::remove(cache.c_str());
return r->done(Error{IHttpRequest::Aborted, ""});
}
auto listener = Listener::make<TransferListener>(
[=](EitherError<void> e, Listener* listener) {
std::remove(cache.c_str());
if (e.left()) return r->done(e.left());
std::unique_ptr<MegaNode> node(mega_->getNodeByHandle(
static_cast<TransferListener*>(listener)->node_));
r->done(toItem(node.get()));
},
this);
listener->upload_callback_ = callback;
r->subrequest(listener);
std::unique_ptr<mega::MegaNode> node(
mega_->getNodeByPath(item->id().c_str()));
mega_->startUpload(cache.c_str(), node.get(), filename.c_str(),
listener.get());
mega_cache.write(buffer.data(), length);
}
}
auto listener = Listener::make<TransferListener>(
[=](EitherError<void> e, Listener* listener) {
std::remove(cache.c_str());
if (e.left()) return r->done(e.left());
std::unique_ptr<MegaNode> node(mega_->getNodeByHandle(
static_cast<TransferListener*>(listener)->node_));
r->done(toItem(node.get()));
},
this);
listener->upload_callback_ = callback;
r->subrequest(listener);
std::unique_ptr<mega::MegaNode> node(
mega_->getNodeByPath(item->id().c_str()));
mega_->startUpload(cache.c_str(), node.get(), filename.c_str(),
listener.get());
});
},
[=](EitherError<IItem> e) { cb->done(e); });
return r->run();
});
};
return std::make_shared<Request<EitherError<IItem>>>(
shared_from_this(), [=](EitherError<IItem> e) { cb->done(e); },
resolver)
->run();
}
ICloudProvider::DownloadFileRequest::Pointer MegaNz::getThumbnailAsync(
IItem::Pointer item, IDownloadFileCallback::Pointer callback) {
auto r = std::make_shared<Request<EitherError<void>>>(shared_from_this());
r->set(
[=](Request<EitherError<void>>::Pointer r) {
ensureAuthorized<EitherError<void>>(r, [=] {
std::string cache = temporaryFileName();
auto listener = Listener::make<RequestListener>(
[=](EitherError<void> e, Listener*) {
if (e.left()) return r->done(e.left());
std::fstream cache_file(
cache.c_str(), std::fstream::in | std::fstream::binary);
if (!cache_file)
return r->done(Error{IHttpRequest::Failure,
"couldn't open cache file " + cache});
std::array<char, BUFFER_SIZE> buffer;