You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

250 lines
9.6 KiB

#ifndef _TOREST_RESOURCE_SESSION_HPP_
#define _TOREST_RESOURCE_SESSION_HPP_
#include <resource.hpp>
#include <util.hpp>
#include <libtorrent/session.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;
// virtual std::shared_ptr<libtorrent::session> get_session() = 0;
};
class translate {
public:
static nlohmann::json to_json(const libtorrent::session_handle &handle){
nlohmann::json session_json;
return session_json;
}
};
class manager : public basic_manager {
public:
// std::shared_ptr<libtorrent::session> get_session() override { return handle; }
void set_session(std::shared_ptr<libtorrent::session> session) override { handle=session; }
nlohmann::json get_json() const override {
nlohmann::json session_json;
session_json["peer_id"] = handle->id().to_string();
session_json["is_paused"] = handle->is_paused();
session_json["is_listening"] = handle->is_listening();
session_json["listen_port"] = handle->listen_port();
session_json["ssl_listen_port"] = handle->ssl_listen_port();
return session_json;
};
bool is_valid() const override { return handle && handle->is_valid(); }
bool patch(const nlohmann::json &data) override {
int is_paused=data["is_paued"];
int listen_port=data["listen_port"];
auto pause = [this](int is_paused){
if(is_paused!=-1){
if(is_paused==handle->is_paused()){
return true;
}else if(is_paused==true){
handle->resume();
return true;
}else if(is_paused==false){
handle->pause();
return true;
}
}
return false;
};
auto listen = [this](int listen_port){
if(listen_port!=-1&&listen_port>6880){
libtorrent::settings_pack sp;
sp.set_str(libtorrent::settings_pack::listen_interfaces,"0.0.0.0:"+std::to_string(listen_port));
handle->apply_settings(sp);
return true;
}
return false;
};
return pause(is_paused) && listen(listen_port);
};
private:
std::shared_ptr<libtorrent::session> handle;
};
class basic_resource : public resource_base {
public:
virtual ~basic_resource() {}
virtual void set_session(std::shared_ptr<basic_manager>)=0;
};
class resource : public basic_resource {
public:
resource(){} //TODO remove?
void set_session(std::shared_ptr<basic_manager> session_manager) override { mgr=session_manager; }
virtual void get(nlohmann::json arg=nullptr) override {
if(mgr && mgr->is_valid()){
get_response().set_status(http::ok);
get_response().set_body(mgr->get_json());
}else{
get_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>`
*/
virtual void patch(const nlohmann::json data=nlohmann::json::object()) override {
if(mgr && mgr->is_valid()){
if(data.is_object() && !data.is_null()){
int is_paused=util::json::get<int>("is_paused",data,-1),
listen_port=util::json::get<int>("listen_port",data,-1);
bool at_at_least_one_option_is_set=is_paused!=-1||listen_port!=-1;
if(at_at_least_one_option_is_set){
nlohmann::json patch={{"is_paused",is_paused},{"listen_port",listen_port}};
if(mgr->patch(patch)){
return get_response().set_status(http::ok);
}else{
return get_response().set_status(http::internal_server_error);
}
}else{
return get_response().set_status(http::bad_request);
}
}
return get_response().set_status(http::bad_request);
// auto data=data.array();
}else{
return get_response().set_status(http::service_unavailable);
}
}
// @todo should we return service unavailable or just method not allowed?
virtual void del(nlohmann::json arg=nullptr) override { get_response().set_status(http::method_not_allowed); }
virtual void put(nlohmann::json arg=nullptr) override { get_response().set_status(http::method_not_allowed); }
virtual void post(nlohmann::json arg=nullptr) override { get_response().set_status(http::method_not_allowed); }
protected:
std::shared_ptr<basic_manager> mgr;
};
}
#endif // _TOREST_RESOURCE_SESSION_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);
};
}*/