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.
251 lines
9.6 KiB
251 lines
9.6 KiB
|
10 years ago
|
#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);
|
||
|
|
};
|
||
|
|
}*/
|