fix adding and editing apps json

This commit is contained in:
loki 2021-06-09 19:12:00 +02:00
parent cf77b301bc
commit 17d3fcf0d0
9 changed files with 234 additions and 144 deletions

View File

@ -37,6 +37,10 @@
# The file where current state of Sunshine is stored
# file_state = sunshine_state.json
# The file where user credentials for the UI are stored
# By default, credentials are stored in `file_state`
# credentials_file = sunshine_state.json
# The display modes advertised by Sunshine
#
# Some versions of Moonlight, such as Moonlight-nx (Switch),
@ -228,7 +232,7 @@
#################################### VAAPI ###################################
####### adapter ##########
# Unlike with `amfvce` and `nvenc`, it doesn't matter if video encoding is done
# Unlike with `amdvce` and `nvenc`, it doesn't matter if video encoding is done
# on a different GPU.
# Run the following commands:
# 1. ls /dev/dri/renderD*

View File

@ -15,7 +15,8 @@
<tr v-for="(app,i) in apps" :key="i">
<td>{{app.name}}</td>
<td><button class="btn btn-primary" @click="editApp(i)">Edit</button>
<button class="btn btn-danger" @click="showDeleteForm(i)">Delete</button></td>
<button class="btn btn-danger" @click="showDeleteForm(i)">Delete</button>
</td>
</tr>
</tbody>
</table>
@ -41,7 +42,7 @@
<label for="appName" class="form-label">Command Preparations</label>
<div class="form-text">A list of commands to be run before/after the application. <br> If any of the
prep-commands fail, starting the application is aborted</div>
<table v-if="editForm['prep-cmd'].length > 0">
<table v-if="editForm['prep-cmd'].length > 0">
<thead>
<th class="precmd-head">Do</th>
<th class="precmd-head">Undo</th>
@ -73,7 +74,8 @@
<!--command-->
<div class="mb-3">
<label for="appCmd" class="form-label">Command</label>
<input type="text" class="form-control monospace" id="appCmd" aria-describedby="appCmdHelp" v-model="editForm.cmd">
<input type="text" class="form-control monospace" id="appCmd" aria-describedby="appCmdHelp"
v-model="editForm.cmd">
<div id="appCmdHelp" class="form-text">The main application, if it is not specified, a processs is started that
sleeps indefinitely</div>
</div>
@ -84,7 +86,7 @@
</div>
</div>
</div>
<div class="mt-2" v-else >
<div class="mt-2" v-else>
<button class="btn btn-primary" @click="newApp">+ Add New</button>
</div>
</div>
@ -113,7 +115,8 @@
output: '',
cmd: [],
index: -1,
"prep-cmd": []
"prep-cmd": [],
"detached": []
};
this.editForm.index = -1;
this.showEditForm = true;
@ -156,5 +159,4 @@
.monospace {
font-family: monospace;
}
</style>

View File

@ -7,6 +7,7 @@
#include <boost/asio.hpp>
#include "config.h"
#include "main.h"
#include "utility.h"
#define CA_DIR "credentials"
@ -209,12 +210,12 @@ input_t input {
};
sunshine_t sunshine {
2, // min_log_level
0, // flags
"user_credentials.json"s, //User file
""s, //Username
""s, //Password
""s //Password Salt
2, // min_log_level
0, // flags
{}, //User file
{}, //Username
{}, //Password
{} //Password Salt
};
bool endline(char ch) {
@ -297,7 +298,7 @@ parse_option(std::string_view::const_iterator begin, std::string_view::const_ite
std::make_pair(to_string(begin, end_name), to_string(begin_val, endl)));
}
std::unordered_map<std::string, std::string> parse_config(std::string_view file_content) {
std::unordered_map<std::string, std::string> parse_config(const std::string_view &file_content) {
std::unordered_map<std::string, std::string> vars;
auto pos = std::begin(file_content);
@ -567,6 +568,11 @@ void apply_config(std::unordered_map<std::string, std::string> &&vars) {
list_string_f(vars, "resolutions"s, nvhttp.resolutions);
list_int_f(vars, "fps"s, nvhttp.fps);
string_f(vars, "credentials_file", config::sunshine.credentials_file);
if(config::sunshine.credentials_file.empty()) {
config::sunshine.credentials_file = config::nvhttp.file_state;
}
string_f(vars, "audio_sink", audio.sink);
string_f(vars, "virtual_sink", audio.virtual_sink);
@ -681,18 +687,9 @@ int parse(int argc, char *argv[]) {
}
}
std::ifstream in { config_file };
sunshine.config_file = config_file;
if(!in.is_open()) {
std::cout << "Error: Couldn't open "sv << config_file << std::endl;
return -1;
}
auto vars = parse_config(std::string {
// Quick and dirty
std::istreambuf_iterator<char>(in),
std::istreambuf_iterator<char>() });
auto vars = parse_config(read_file(config_file));
for(auto &[name, value] : cmd_vars) {
vars.insert_or_assign(std::move(name), std::move(value));

View File

@ -82,7 +82,7 @@ namespace flag {
enum flag_e : std::size_t {
PIN_STDIN = 0, // Read PIN from stdin instead of http
FRESH_STATE, // Do not load or save state
CONST_PIN = 4, // Use "universal" pin
CONST_PIN, // Use "universal" pin
FLAG_SIZE
};
}
@ -94,6 +94,8 @@ struct sunshine_t {
std::string username;
std::string password;
std::string salt;
std::string config_file;
};
extern video_t video;
@ -104,6 +106,6 @@ extern input_t input;
extern sunshine_t sunshine;
int parse(int argc, char *argv[]);
std::unordered_map<std::string, std::string> parse_config(std::string_view file_content);
std::unordered_map<std::string, std::string> parse_config(const std::string_view &file_content);
} // namespace config
#endif

View File

@ -30,8 +30,6 @@
#include "utility.h"
#include "uuid.h"
std::string read_file(std::string path);
namespace confighttp {
using namespace std::literals;
constexpr auto PORT_HTTPS = 47990;
@ -50,6 +48,23 @@ enum class op_e {
REMOVE
};
void print_req(const req_https_t &request) {
BOOST_LOG(debug) << "METHOD :: "sv << request->method;
BOOST_LOG(debug) << "DESTINATION :: "sv << request->path;
for(auto &[name, val] : request->header) {
BOOST_LOG(debug) << name << " -- " << val;
}
BOOST_LOG(debug) << " [--] "sv;
for(auto &[name, val] : request->parse_query_string()) {
BOOST_LOG(debug) << name << " -- " << val;
}
BOOST_LOG(debug) << " [--] "sv;
}
void send_unauthorized(resp_https_t response, req_https_t request) {
auto address = request->remote_endpoint_address();
BOOST_LOG(info) << '[' << address << "] -- denied"sv;
@ -62,27 +77,40 @@ void send_unauthorized(resp_https_t response, req_https_t request) {
bool authenticate(resp_https_t response, req_https_t request) {
auto address = request->remote_endpoint_address();
auto ip_type = net::from_address(address);
if(ip_type > http::origin_pin_allowed) {
BOOST_LOG(info) << '[' << address << "] -- denied"sv;
response->write(SimpleWeb::StatusCode::client_error_forbidden);
return false;
}
auto fg = util::fail_guard([&]() {
send_unauthorized(response, request);
});
auto auth = request->header.find("authorization");
if(auth == request->header.end()) {
send_unauthorized(response, request);
return false;
}
std::string rawAuth = auth->second;
std::string authData = rawAuth.substr("Basic "sv.length());
authData = SimpleWeb::Crypto::Base64::decode(authData);
int index = authData.find(':');
std::string username = authData.substr(0, index);
std::string password = authData.substr(index + 1);
std::string hash = util::hex(crypto::hash(password + config::sunshine.salt)).to_string();
if(username == config::sunshine.username && hash == config::sunshine.password) return true;
send_unauthorized(response, request);
return false;
auto &rawAuth = auth->second;
auto authData = SimpleWeb::Crypto::Base64::decode(rawAuth.substr("Basic "sv.length()));
int index = authData.find(':');
if(index >= authData.size() - 1) {
return false;
}
auto username = authData.substr(0, index);
auto password = authData.substr(index + 1);
auto hash = util::hex(crypto::hash(password + config::sunshine.salt)).to_string();
if(username != config::sunshine.username || hash != config::sunshine.password) {
return false;
}
fg.disable();
return true;
}
void not_found(resp_https_t response, req_https_t request) {
@ -101,6 +129,8 @@ void not_found(resp_https_t response, req_https_t request) {
void getIndexPage(resp_https_t response, req_https_t request) {
if(!authenticate(response, request)) return;
print_req(request);
std::string header = read_file(WEB_DIR "header.html");
std::string content = read_file(WEB_DIR "index.html");
response->write(header + content);
@ -109,6 +139,8 @@ void getIndexPage(resp_https_t response, req_https_t request) {
void getPinPage(resp_https_t response, req_https_t request) {
if(!authenticate(response, request)) return;
print_req(request);
std::string header = read_file(WEB_DIR "header.html");
std::string content = read_file(WEB_DIR "pin.html");
response->write(header + content);
@ -117,6 +149,8 @@ void getPinPage(resp_https_t response, req_https_t request) {
void getAppsPage(resp_https_t response, req_https_t request) {
if(!authenticate(response, request)) return;
print_req(request);
std::string header = read_file(WEB_DIR "header.html");
std::string content = read_file(WEB_DIR "apps.html");
response->write(header + content);
@ -125,6 +159,8 @@ void getAppsPage(resp_https_t response, req_https_t request) {
void getClientsPage(resp_https_t response, req_https_t request) {
if(!authenticate(response, request)) return;
print_req(request);
std::string header = read_file(WEB_DIR "header.html");
std::string content = read_file(WEB_DIR "clients.html");
response->write(header + content);
@ -133,6 +169,8 @@ void getClientsPage(resp_https_t response, req_https_t request) {
void getConfigPage(resp_https_t response, req_https_t request) {
if(!authenticate(response, request)) return;
print_req(request);
std::string header = read_file(WEB_DIR "header.html");
std::string content = read_file(WEB_DIR "config.html");
response->write(header + content);
@ -141,6 +179,8 @@ void getConfigPage(resp_https_t response, req_https_t request) {
void getPasswordPage(resp_https_t response, req_https_t request) {
if(!authenticate(response, request)) return;
print_req(request);
std::string header = read_file(WEB_DIR "header.html");
std::string content = read_file(WEB_DIR "password.html");
response->write(header + content);
@ -149,15 +189,20 @@ void getPasswordPage(resp_https_t response, req_https_t request) {
void getApps(resp_https_t response, req_https_t request) {
if(!authenticate(response, request)) return;
std::string content = read_file(SUNSHINE_ASSETS_DIR "/" APPS_JSON);
print_req(request);
std::string content = read_file(config::stream.file_apps.c_str());
response->write(content);
}
void saveApp(resp_https_t response, req_https_t request) {
if(!authenticate(response, request)) return;
print_req(request);
std::stringstream ss;
ss << request->content.rdbuf();
pt::ptree outputTree;
auto g = util::fail_guard([&]() {
std::ostringstream data;
@ -165,20 +210,28 @@ void saveApp(resp_https_t response, req_https_t request) {
pt::write_json(data, outputTree);
response->write(data.str());
});
pt::ptree inputTree, fileTree;
BOOST_LOG(fatal) << config::stream.file_apps;
try {
//TODO: Input Validation
pt::read_json(ss, inputTree);
pt::read_json(SUNSHINE_ASSETS_DIR "/" APPS_JSON, fileTree);
pt::read_json(config::stream.file_apps, fileTree);
if(inputTree.get_child("prep-cmd").empty()) {
inputTree.erase("prep-cmd");
}
if(inputTree.get_child("detached").empty()) {
inputTree.erase("detached");
}
auto &apps_node = fileTree.get_child("apps"s);
int index = inputTree.get<int>("index");
if(inputTree.get_child("prep-cmd").empty())
inputTree.erase("prep-cmd");
if(inputTree.get_child("detached").empty())
inputTree.erase("detached");
inputTree.erase("index");
if(index == -1) {
apps_node.push_back(std::make_pair("", inputTree));
}
@ -198,21 +251,25 @@ void saveApp(resp_https_t response, req_https_t request) {
fileTree.erase("apps");
fileTree.push_back(std::make_pair("apps", newApps));
}
pt::write_json(SUNSHINE_ASSETS_DIR "/" APPS_JSON, fileTree);
outputTree.put("status", "true");
proc::refresh(SUNSHINE_ASSETS_DIR "/" APPS_JSON);
pt::write_json(config::stream.file_apps, fileTree);
}
catch(std::exception &e) {
BOOST_LOG(warning) << e.what();
BOOST_LOG(warning) << "SaveApp: "sv << e.what();
outputTree.put("status", "false");
outputTree.put("error", "Invalid Input JSON");
return;
}
outputTree.put("status", "true");
proc::refresh(config::stream.file_apps);
}
void deleteApp(resp_https_t response, req_https_t request) {
if(!authenticate(response, request)) return;
print_req(request);
pt::ptree outputTree;
auto g = util::fail_guard([&]() {
std::ostringstream data;
@ -225,8 +282,8 @@ void deleteApp(resp_https_t response, req_https_t request) {
pt::read_json(config::stream.file_apps, fileTree);
auto &apps_node = fileTree.get_child("apps"s);
int index = stoi(request->path_match[1]);
BOOST_LOG(info) << index;
if(index <= 0) {
if(index < 0) {
outputTree.put("status", "false");
outputTree.put("error", "Invalid Index");
return;
@ -236,29 +293,31 @@ void deleteApp(resp_https_t response, req_https_t request) {
pt::ptree newApps;
int i = 0;
for(const auto &kv : apps_node) {
if(i != index) {
if(i++ != index) {
newApps.push_back(std::make_pair("", kv.second));
}
i++;
}
fileTree.erase("apps");
fileTree.push_back(std::make_pair("apps", newApps));
}
pt::write_json(SUNSHINE_ASSETS_DIR "/" APPS_JSON, fileTree);
outputTree.put("status", "true");
proc::refresh(SUNSHINE_ASSETS_DIR "/" APPS_JSON);
pt::write_json(config::stream.file_apps, fileTree);
}
catch(std::exception &e) {
BOOST_LOG(warning) << e.what();
BOOST_LOG(warning) << "DeleteApp: "sv << e.what();
outputTree.put("status", "false");
outputTree.put("error", "Invalid File JSON");
return;
}
outputTree.put("status", "true");
proc::refresh(config::stream.file_apps);
}
void getConfig(resp_https_t response, req_https_t request) {
if(!authenticate(response, request)) return;
print_req(request);
pt::ptree outputTree;
auto g = util::fail_guard([&]() {
std::ostringstream data;
@ -266,36 +325,22 @@ void getConfig(resp_https_t response, req_https_t request) {
pt::write_json(data, outputTree);
response->write(data.str());
});
try {
outputTree.put("status", "true");
outputTree.put("platform", SUNSHINE_PLATFORM);
const char *config_file = SUNSHINE_ASSETS_DIR "/sunshine.conf";
std::ifstream in { config_file };
if(!in.is_open()) {
std::cout << "Error: Couldn't open "sv << config_file << std::endl;
}
outputTree.put("status", "true");
outputTree.put("platform", SUNSHINE_PLATFORM);
auto vars = config::parse_config(std::string {
// Quick and dirty
std::istreambuf_iterator<char>(in),
std::istreambuf_iterator<char>() });
auto vars = config::parse_config(read_file(config::sunshine.config_file.c_str()));
for(auto &[name, value] : vars) {
outputTree.put(std::move(name), std::move(value));
}
}
catch(std::exception &e) {
BOOST_LOG(warning) << e.what();
outputTree.put("status", "false");
outputTree.put("error", "Invalid File JSON");
return;
for(auto &[name, value] : vars) {
outputTree.put(std::move(name), std::move(value));
}
}
void saveConfig(resp_https_t response, req_https_t request) {
if(!authenticate(response, request)) return;
print_req(request);
std::stringstream ss;
std::stringstream configStream;
ss << request->content.rdbuf();
@ -316,10 +361,10 @@ void saveConfig(resp_https_t response, req_https_t request) {
configStream << kv.first << " = " << value << std::endl;
}
write_file(SUNSHINE_ASSETS_DIR "/sunshine.conf", configStream.str());
write_file(config::sunshine.config_file.c_str(), configStream.str());
}
catch(std::exception &e) {
BOOST_LOG(warning) << e.what();
BOOST_LOG(warning) << "SaveConfig: "sv << e.what();
outputTree.put("status", "false");
outputTree.put("error", e.what());
return;
@ -329,11 +374,13 @@ void saveConfig(resp_https_t response, req_https_t request) {
void savePassword(resp_https_t response, req_https_t request) {
if(!authenticate(response, request)) return;
print_req(request);
std::stringstream ss;
std::stringstream configStream;
ss << request->content.rdbuf();
pt::ptree inputTree, outputTree, fileTree;
pt::ptree inputTree, outputTree;
auto g = util::fail_guard([&]() {
std::ostringstream data;
@ -344,23 +391,21 @@ void savePassword(resp_https_t response, req_https_t request) {
try {
//TODO: Input Validation
pt::read_json(ss, inputTree);
std::string username = inputTree.get<std::string>("currentUsername");
std::string newUsername = inputTree.get<std::string>("newUsername");
std::string password = inputTree.get<std::string>("currentPassword");
std::string newPassword = inputTree.get<std::string>("newPassword");
std::string confirmPassword = inputTree.get<std::string>("confirmNewPassword");
auto username = inputTree.get<std::string>("currentUsername");
auto newUsername = inputTree.get<std::string>("newUsername");
auto password = inputTree.get<std::string>("currentPassword");
auto newPassword = inputTree.get<std::string>("newPassword");
auto confirmPassword = inputTree.get<std::string>("confirmNewPassword");
if(newUsername.length() == 0) newUsername = username;
std::string hash = util::hex(crypto::hash(password + config::sunshine.salt)).to_string();
auto hash = util::hex(crypto::hash(password + config::sunshine.salt)).to_string();
if(username == config::sunshine.username && hash == config::sunshine.password) {
if(newPassword != confirmPassword) {
outputTree.put("status", false);
outputTree.put("error", "Password Mismatch");
}
fileTree.put("username", newUsername);
fileTree.put("password", util::hex(crypto::hash(newPassword + config::sunshine.salt)).to_string());
fileTree.put("salt", config::sunshine.salt);
pt::write_json(config::sunshine.credentials_file, fileTree);
http::save_user_creds(config::sunshine.credentials_file, newUsername, newPassword, crypto::rand_alphabet(16));
http::reload_user_creds(config::sunshine.credentials_file);
outputTree.put("status", true);
}
@ -370,7 +415,7 @@ void savePassword(resp_https_t response, req_https_t request) {
}
}
catch(std::exception &e) {
BOOST_LOG(warning) << e.what();
BOOST_LOG(warning) << "SavePassword: "sv << e.what();
outputTree.put("status", false);
outputTree.put("error", e.what());
return;
@ -380,6 +425,8 @@ void savePassword(resp_https_t response, req_https_t request) {
void savePin(resp_https_t response, req_https_t request) {
if(!authenticate(response, request)) return;
print_req(request);
std::stringstream ss;
ss << request->content.rdbuf();
@ -398,7 +445,7 @@ void savePin(resp_https_t response, req_https_t request) {
outputTree.put("status", nvhttp::pin(pin));
}
catch(std::exception &e) {
BOOST_LOG(warning) << e.what();
BOOST_LOG(warning) << "SavePin: "sv << e.what();
outputTree.put("status", false);
outputTree.put("error", e.what());
return;
@ -462,19 +509,4 @@ void start(std::shared_ptr<safe::signal_t> shutdown_event) {
tcp.join();
}
} // namespace confighttp
std::string read_file(std::string path) {
std::ifstream in(path);
std::string input;
std::string base64_cert;
//FIXME: Being unable to read file could result in infinite loop
while(!in.eof()) {
std::getline(in, input);
base64_cert += input + '\n';
}
return base64_cert;
}
} // namespace confighttp

View File

@ -30,9 +30,9 @@ using namespace std::literals;
namespace fs = std::filesystem;
namespace pt = boost::property_tree;
int create_creds(const std::string &pkey, const std::string &cert);
int generate_user_creds(const std::string &file);
int reload_user_creds(const std::string &file);
bool user_creds_exist(const std::string &file);
std::string unique_id;
net::net_e origin_pin_allowed;
@ -52,8 +52,8 @@ void init(std::shared_ptr<safe::signal_t> shutdown_event) {
return;
}
}
if(!fs::exists(config::sunshine.credentials_file)) {
if(generate_user_creds(config::sunshine.credentials_file)) {
if(!user_creds_exist(config::sunshine.credentials_file)) {
if(save_user_creds(config::sunshine.credentials_file, "sunshine"s, crypto::rand_alphabet(16), crypto::rand_alphabet(16))) {
shutdown_event->raise(true);
return;
}
@ -64,27 +64,56 @@ void init(std::shared_ptr<safe::signal_t> shutdown_event) {
}
}
int generate_user_creds(const std::string &file) {
int save_user_creds(const std::string &file, const std::string &username, const std::string &password, const std::string &salt) {
pt::ptree outputTree;
if(fs::exists(file)) {
try {
pt::read_json(file, outputTree);
}
catch(std::exception &e) {
BOOST_LOG(error) << "Couldn't read user credentials: "sv << e.what();
return -1;
}
}
outputTree.put("username", "sunshine");
outputTree.put("salt", salt);
outputTree.put("password", util::hex(crypto::hash(password + salt)).to_string());
try {
std::string username = "sunshine";
std::string plainPassword = crypto::rand_alphabet(16);
std::string salt = crypto::rand_alphabet(16);
outputTree.put("username", "sunshine");
outputTree.put("salt", salt);
outputTree.put("password", util::hex(crypto::hash(plainPassword + salt)).to_string());
BOOST_LOG(info) << "New credentials have been created";
BOOST_LOG(info) << "Username: " << username;
BOOST_LOG(info) << "Password: " << plainPassword;
pt::write_json(file, outputTree);
}
catch(std::exception &e) {
BOOST_LOG(fatal) << e.what();
return 1;
BOOST_LOG(error) << "generating user credentials: "sv << e.what();
return -1;
}
BOOST_LOG(info) << "New credentials have been created"sv;
BOOST_LOG(info) << "Username: "sv << username;
BOOST_LOG(info) << "Password: "sv << password;
return 0;
}
bool user_creds_exist(const std::string &file) {
if(!fs::exists(file)) {
return false;
}
pt::ptree inputTree;
try {
pt::read_json(file, inputTree);
return inputTree.find("username") != inputTree.not_found() &&
inputTree.find("password") != inputTree.not_found() &&
inputTree.find("salt") != inputTree.not_found();
}
catch(std::exception &e) {
BOOST_LOG(error) << "validating user credentials: "sv << e.what();
}
return false;
}
int reload_user_creds(const std::string &file) {
pt::ptree inputTree;
try {
@ -94,8 +123,8 @@ int reload_user_creds(const std::string &file) {
config::sunshine.salt = inputTree.get<std::string>("salt");
}
catch(std::exception &e) {
BOOST_LOG(fatal) << e.what();
return 1;
BOOST_LOG(error) << "loading user credentials: "sv << e.what();
return -1;
}
return 0;
}
@ -114,23 +143,23 @@ int create_creds(const std::string &pkey, const std::string &cert) {
std::error_code err_code {};
fs::create_directories(pkey_dir, err_code);
if(err_code) {
BOOST_LOG(fatal) << "Couldn't create directory ["sv << pkey_dir << "] :"sv << err_code.message();
BOOST_LOG(error) << "Couldn't create directory ["sv << pkey_dir << "] :"sv << err_code.message();
return -1;
}
fs::create_directories(cert_dir, err_code);
if(err_code) {
BOOST_LOG(fatal) << "Couldn't create directory ["sv << cert_dir << "] :"sv << err_code.message();
BOOST_LOG(error) << "Couldn't create directory ["sv << cert_dir << "] :"sv << err_code.message();
return -1;
}
if(write_file(pkey.c_str(), creds.pkey)) {
BOOST_LOG(fatal) << "Couldn't open ["sv << config::nvhttp.pkey << ']';
BOOST_LOG(error) << "Couldn't open ["sv << config::nvhttp.pkey << ']';
return -1;
}
if(write_file(cert.c_str(), creds.x509)) {
BOOST_LOG(fatal) << "Couldn't open ["sv << config::nvhttp.cert << ']';
BOOST_LOG(error) << "Couldn't open ["sv << config::nvhttp.cert << ']';
return -1;
}
@ -139,7 +168,7 @@ int create_creds(const std::string &pkey, const std::string &cert) {
fs::perm_options::replace, err_code);
if(err_code) {
BOOST_LOG(fatal) << "Couldn't change permissions of ["sv << config::nvhttp.pkey << "] :"sv << err_code.message();
BOOST_LOG(error) << "Couldn't change permissions of ["sv << config::nvhttp.pkey << "] :"sv << err_code.message();
return -1;
}
@ -148,7 +177,7 @@ int create_creds(const std::string &pkey, const std::string &cert) {
fs::perm_options::replace, err_code);
if(err_code) {
BOOST_LOG(fatal) << "Couldn't change permissions of ["sv << config::nvhttp.cert << "] :"sv << err_code.message();
BOOST_LOG(error) << "Couldn't change permissions of ["sv << config::nvhttp.cert << "] :"sv << err_code.message();
return -1;
}

View File

@ -5,6 +5,7 @@ namespace http {
void init(std::shared_ptr<safe::signal_t> shutdown_event);
int create_creds(const std::string &pkey, const std::string &cert);
int save_user_creds(const std::string &file, const std::string &username, const std::string &password, const std::string &salt);
int reload_user_creds(const std::string &file);
extern std::string unique_id;
extern net::net_e origin_pin_allowed;

View File

@ -93,6 +93,18 @@ enum class op_e {
void save_state() {
pt::ptree root;
if(fs::exists(config::nvhttp.file_state)) {
try {
pt::read_json(config::nvhttp.file_state, root);
}
catch(std::exception &e) {
BOOST_LOG(error) << e.what();
return;
}
}
root.erase("root"s);
root.put("root.uniqueid", http::unique_id);
auto &nodes = root.add_child("root.devices", pt::ptree {});
for(auto &[_, client] : map_id_client) {
@ -111,7 +123,13 @@ void save_state() {
nodes.push_back(std::make_pair(""s, node));
}
pt::write_json(config::nvhttp.file_state, root);
try {
pt::write_json(config::nvhttp.file_state, root);
}
catch(std::exception &e) {
BOOST_LOG(error) << e.what();
return;
}
}
void load_state() {
@ -131,7 +149,16 @@ void load_state() {
return;
}
http::unique_id = root.get<std::string>("root.uniqueid");
auto unique_id_p = root.find("root.uniqueid"s);
if(unique_id_p == root.not_found()) {
// This file doesn't contain moonlight credentials
http::unique_id = util::uuid_t::generate().string();
return;
}
else {
http::unique_id = root.get<std::string>("root.uniqueid");
}
auto device_nodes = root.get_child("root.devices");
for(auto &[_, device_node] : device_nodes) {

View File

@ -5,15 +5,11 @@
#ifndef SUNSHINE_NVHTTP_H
#define SUNSHINE_NVHTTP_H
#include <functional>
#include <string>
#include "thread_safe.h"
#include <Simple-Web-Server/server_http.hpp>
#include <Simple-Web-Server/server_https.hpp>
#include "thread_safe.h"
#define CA_DIR SUNSHINE_ASSETS_DIR "/demoCA"
#define PRIVATE_KEY_FILE CA_DIR "/cakey.pem"
#define CERTIFICATE_FILE CA_DIR "/cacert.pem"
#include <functional>
#include <string>
namespace nvhttp {
void start(std::shared_ptr<safe::signal_t> shutdown_event);