#ifndef _TOREST_RESOURCE_SESSION_HPP_ #define _TOREST_RESOURCE_SESSION_HPP_ #include #include #include 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) = 0; virtual void set_listen_port() = 0; // virtual std::shared_ptr 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 get_session() override { return handle; } void set_session(std::shared_ptr 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 handle; }; class basic_resource : public resource_base { public: virtual ~basic_resource() {} virtual void set_session(std::shared_ptr)=0; }; class resource : public basic_resource { public: resource(){} //TODO remove? void set_session(std::shared_ptr 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` `listen_port` */ 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("is_paused",data,-1), listen_port=util::json::get("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 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> { public: torrents(libtorrent::session &session); }; */ // std::function /*! 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": "", "save_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 resp,std::shared_ptr 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 required_fields={ request_json.value("torrent",""), request_json.value("save_path","") }; std::vector 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(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); }; }*/