|
|
|
|
#ifndef _TOREST_SESSION_HPP_
|
|
|
|
|
#define _TOREST_SESSION_HPP_
|
|
|
|
|
|
|
|
|
|
#include <resource.hpp>
|
|
|
|
|
#include <util.hpp>
|
|
|
|
|
#include <libtorrent/session.hpp>
|
|
|
|
|
#include <http.hpp>
|
|
|
|
|
|
|
|
|
|
namespace session {
|
|
|
|
|
class basic_manager {
|
|
|
|
|
public:
|
|
|
|
|
virtual ~basic_manager()=0;
|
|
|
|
|
virtual nlohmann::json get_json() const = 0;
|
|
|
|
|
virtual bool patch(const nlohmann::json &data) = 0;
|
|
|
|
|
virtual bool is_valid() const = 0;
|
|
|
|
|
virtual int listen_port() const = 0;
|
|
|
|
|
virtual void set_session(std::shared_ptr<libtorrent::session>) = 0;
|
|
|
|
|
virtual void set_listen_port() = 0;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
class manager : public basic_manager {
|
|
|
|
|
public:
|
|
|
|
|
void set_session(std::shared_ptr<libtorrent::session> session) override { handle=session; }
|
|
|
|
|
nlohmann::json get_json() const override ;
|
|
|
|
|
bool is_valid() const override { return handle && handle->is_valid(); }
|
|
|
|
|
bool patch(const nlohmann::json &data) override ;
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
std::shared_ptr<libtorrent::session> handle;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
class basic_resource {
|
|
|
|
|
public:
|
|
|
|
|
virtual ~basic_resource() {}
|
|
|
|
|
virtual void set_session(std::shared_ptr<basic_manager>)=0;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
class resource : public basic_resource {
|
|
|
|
|
public:
|
|
|
|
|
void set_session(std::shared_ptr<basic_manager> session_manager) override { mgr=session_manager; }
|
|
|
|
|
|
|
|
|
|
virtual void get(nlohmann::json arg=nullptr) {
|
|
|
|
|
if(mgr && mgr->is_valid()){
|
|
|
|
|
response.set_status(http::ok);
|
|
|
|
|
response.set_body(mgr->get_json());
|
|
|
|
|
}else{
|
|
|
|
|
response.set_status(http::service_unavailable);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
/**
|
|
|
|
|
* PATCH /session
|
|
|
|
|
* --------------
|
|
|
|
|
* \brief Change values on the session.
|
|
|
|
|
* @param[in] data takes an JSON object with at least one `action` set.
|
|
|
|
|
* available `action`s are `is_paused<bool>` `listen_port<bool>`
|
|
|
|
|
*/
|
|
|
|
|
void patch(const nlohmann::json&data=nlohmann::json::object());
|
|
|
|
|
|
|
|
|
|
// @todo should we return service unavailable or just method not allowed?
|
|
|
|
|
void del(nlohmann::json arg=nullptr) { response.set_status(http::method_not_allowed); }
|
|
|
|
|
void put(nlohmann::json arg=nullptr) { response.set_status(http::method_not_allowed); }
|
|
|
|
|
virtual void post(nlohmann::json arg=nullptr) { response.set_status(http::method_not_allowed); }
|
|
|
|
|
protected:
|
|
|
|
|
std::shared_ptr<basic_manager> mgr;
|
|
|
|
|
http::response response;
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
#endif // _TOREST_RESOURCE_HPP_
|
|
|
|
|
|
|
|
|
|
/*! Resource: torrents
|
|
|
|
|
Other:
|
|
|
|
|
- If multiple headers with the same key is posted, the server uses only the last.
|
|
|
|
|
- Server can return 503 Service Unavailable if for some reason a service is down */
|
|
|
|
|
/*
|
|
|
|
|
class torrents : public Resource<std::shared_ptr<HttpServer::Response>,std::shared_ptr<HttpServer::Request>> {
|
|
|
|
|
public:
|
|
|
|
|
torrents(libtorrent::session &session);
|
|
|
|
|
};
|
|
|
|
|
*/
|
|
|
|
|
// std::function<void(StreamType,StreamType)>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*! API-End-point: [POST] /v1/session/torrents
|
|
|
|
|
Requirements:
|
|
|
|
|
- Content-Type: application/json
|
|
|
|
|
* This header must be set, otherwise toREST will return 403 Forbidden.
|
|
|
|
|
- Data:
|
|
|
|
|
* A valid JSON-object is required.
|
|
|
|
|
* A valid format:
|
|
|
|
|
```json
|
|
|
|
|
{
|
|
|
|
|
"torrent": "<torrent_url>",
|
|
|
|
|
"save_path": "<path>"
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
* Fields `torrent` and `save_path` can not be empty. toREST will return 400 Bad Request
|
|
|
|
|
* The `torrent` field can be a `local torrent file`, a `magnet-uri` or a HTTP-resource.
|
|
|
|
|
toREST will Accept every file with a valid url, uri or path, but will not download
|
|
|
|
|
or create torrents if the path is a invalid torrent file.
|
|
|
|
|
* The `save_path` field can be any location on the server machine, in which the invoking
|
|
|
|
|
user has write permissions. If the save_path is a directory, the torrent will download
|
|
|
|
|
into the directory. If the save_path is a file, toREST will return 400 Bad Request.
|
|
|
|
|
|
|
|
|
|
Side-Effects:
|
|
|
|
|
- 202 Accepted, if the request is valid.
|
|
|
|
|
* The `Location` header is set if the torrent info_hash is available
|
|
|
|
|
* If a WebSocket is open a TORRENT_ADDED action is posted when the torrent resource is available.
|
|
|
|
|
*
|
|
|
|
|
POST=[&session](std::shared_ptr<HttpServer::Response> resp,std::shared_ptr<HttpServer::Request> req){
|
|
|
|
|
http::request_handler handler(req,resp);
|
|
|
|
|
|
|
|
|
|
// If session is invalid there is no point hanging around.
|
|
|
|
|
if(!session.is_valid())
|
|
|
|
|
return handler.respond(i18N::session_unavailable,http::service_unavailable);
|
|
|
|
|
|
|
|
|
|
// At least one header named with Content-Type, then check if the last one set is application/json
|
|
|
|
|
if(handler.header_equals("Content-Type","application/json"))
|
|
|
|
|
return handler.respond(i18N::content_type_not_set,http::forbidden);
|
|
|
|
|
|
|
|
|
|
// If the supplied JSON is invalid, bad request
|
|
|
|
|
auto request_json=util::json::parse(req->content);
|
|
|
|
|
if(!request_json.is_object() || request_json.is_null())
|
|
|
|
|
return handler.respond(nlohmann::json(i18N::unable_to_parse_json),http::bad_request);
|
|
|
|
|
|
|
|
|
|
// handle required fields
|
|
|
|
|
std::vector<std::string> required_fields={
|
|
|
|
|
request_json.value("torrent",""),
|
|
|
|
|
request_json.value("save_path","")
|
|
|
|
|
};
|
|
|
|
|
std::vector<std::string> empty_fields;
|
|
|
|
|
std::copy_if(required_fields.begin(),required_fields.end(),empty_fields.begin(),[](const std::string &field){return field.empty();});
|
|
|
|
|
if(!empty_fields.empty()){
|
|
|
|
|
std::string message;
|
|
|
|
|
for(auto &field:empty_fields)
|
|
|
|
|
message+=" The required field `"+field+"` is empty";
|
|
|
|
|
return handler.respond(nlohmann::json(i18N::wrong_format + message),http::bad_request);
|
|
|
|
|
}
|
|
|
|
|
auto torrent_field=required_fields.front();
|
|
|
|
|
libtorrent::add_torrent_params torrent_options;
|
|
|
|
|
boost::system::error_code ec;
|
|
|
|
|
if(torrent_field.substr(0,6)=="magnet"){
|
|
|
|
|
libtorrent::parse_magnet_uri(torrent_field,torrent_options,ec);
|
|
|
|
|
ECERROR(ec);
|
|
|
|
|
}
|
|
|
|
|
else if(boost::filesystem::exists(torrent_field,ec) && !boost::filesystem::is_directory(torrent_field,ec))
|
|
|
|
|
torrent_options.ti=boost::make_shared<libtorrent::torrent_info>(libtorrent::torrent_info(torrent_field,ec));
|
|
|
|
|
else if(boost::regex_match(torrent_field,util::regex))
|
|
|
|
|
torrent_options.url=torrent_field;
|
|
|
|
|
else {
|
|
|
|
|
ECERROR(ec);
|
|
|
|
|
return handler.respond(nlohmann::json(i18N::unable_to_parse_torrent_uri),http::bad_request);
|
|
|
|
|
}
|
|
|
|
|
boost::filesystem::path save_path(required_fields.back());
|
|
|
|
|
auto status=boost::filesystem::status(save_path,ec);
|
|
|
|
|
if(!ec){
|
|
|
|
|
if(boost::filesystem::exists(status) && !boost::filesystem::is_directory(status))
|
|
|
|
|
return handler.respond(nlohmann::json(i18N::wrong_format + " The save path is invalid"),http::bad_request);
|
|
|
|
|
else {
|
|
|
|
|
// TODO chroot
|
|
|
|
|
try {
|
|
|
|
|
boost::filesystem::create_directories(save_path);
|
|
|
|
|
} catch(const std::exception &ex){
|
|
|
|
|
return handler.respond(nlohmann::json(i18N::write_error),http::forbidden);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// invalid file status
|
|
|
|
|
ECERROR(ec);
|
|
|
|
|
}
|
|
|
|
|
torrent_options.save_path=save_path.generic_string();
|
|
|
|
|
session.async_add_torrent(torrent_options);
|
|
|
|
|
// TODO add websocket event
|
|
|
|
|
std::string info_hash(torrent_options.info_hash.data());
|
|
|
|
|
if(info_hash!="undefined"){
|
|
|
|
|
std::stringstream ss;
|
|
|
|
|
ss << torrent_options.info_hash;
|
|
|
|
|
info_hash=ss.str();
|
|
|
|
|
handler.headers["Location:"]= "/torrents/" + info_hash;
|
|
|
|
|
}
|
|
|
|
|
return handler.respond(nlohmann::json(http::http_code(http::accepted).second), http::accepted);
|
|
|
|
|
};
|
|
|
|
|
}*/
|