diff --git a/assets/sunshine.conf b/assets/sunshine.conf
index 7a67c87b..9042beb4 100644
--- a/assets/sunshine.conf
+++ b/assets/sunshine.conf
@@ -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*
diff --git a/assets/web/apps.html b/assets/web/apps.html
index d1784608..6a5056d8 100644
--- a/assets/web/apps.html
+++ b/assets/web/apps.html
@@ -15,7 +15,8 @@
{{app.name}}
Edit
- Delete
+ Delete
+
@@ -41,7 +42,7 @@
Command Preparations
A list of commands to be run before/after the application. If any of the
prep-commands fail, starting the application is aborted
-
+
Do
Undo
@@ -73,7 +74,8 @@
@@ -84,7 +86,7 @@
-
@@ -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;
}
-
\ No newline at end of file
diff --git a/sunshine/config.cpp b/sunshine/config.cpp
index f2cf391e..00a43dd2 100644
--- a/sunshine/config.cpp
+++ b/sunshine/config.cpp
@@ -7,6 +7,7 @@
#include
#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 parse_config(std::string_view file_content) {
+std::unordered_map parse_config(const std::string_view &file_content) {
std::unordered_map vars;
auto pos = std::begin(file_content);
@@ -567,6 +568,11 @@ void apply_config(std::unordered_map &&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(in),
- std::istreambuf_iterator() });
+ auto vars = parse_config(read_file(config_file));
for(auto &[name, value] : cmd_vars) {
vars.insert_or_assign(std::move(name), std::move(value));
diff --git a/sunshine/config.h b/sunshine/config.h
index 99f50408..45640b8d 100644
--- a/sunshine/config.h
+++ b/sunshine/config.h
@@ -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 parse_config(std::string_view file_content);
+std::unordered_map parse_config(const std::string_view &file_content);
} // namespace config
#endif
diff --git a/sunshine/confighttp.cpp b/sunshine/confighttp.cpp
index d5db6fd9..7234cb3f 100644
--- a/sunshine/confighttp.cpp
+++ b/sunshine/confighttp.cpp
@@ -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("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(in),
- std::istreambuf_iterator() });
+ 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("currentUsername");
- std::string newUsername = inputTree.get("newUsername");
- std::string password = inputTree.get("currentPassword");
- std::string newPassword = inputTree.get("newPassword");
- std::string confirmPassword = inputTree.get("confirmNewPassword");
+ auto username = inputTree.get("currentUsername");
+ auto newUsername = inputTree.get("newUsername");
+ auto password = inputTree.get("currentPassword");
+ auto newPassword = inputTree.get("newPassword");
+ auto confirmPassword = inputTree.get("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 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;
-}
\ No newline at end of file
+} // namespace confighttp
\ No newline at end of file
diff --git a/sunshine/httpcommon.cpp b/sunshine/httpcommon.cpp
index a68b7eb3..05beb899 100644
--- a/sunshine/httpcommon.cpp
+++ b/sunshine/httpcommon.cpp
@@ -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 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 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("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;
}
diff --git a/sunshine/httpcommon.h b/sunshine/httpcommon.h
index 49923290..ba2d587f 100644
--- a/sunshine/httpcommon.h
+++ b/sunshine/httpcommon.h
@@ -5,6 +5,7 @@ namespace http {
void init(std::shared_ptr 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;
diff --git a/sunshine/nvhttp.cpp b/sunshine/nvhttp.cpp
index 07e25422..b425ea4e 100644
--- a/sunshine/nvhttp.cpp
+++ b/sunshine/nvhttp.cpp
@@ -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("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("root.uniqueid");
+ }
+
auto device_nodes = root.get_child("root.devices");
for(auto &[_, device_node] : device_nodes) {
diff --git a/sunshine/nvhttp.h b/sunshine/nvhttp.h
index f6853b60..7ee9f2c7 100644
--- a/sunshine/nvhttp.h
+++ b/sunshine/nvhttp.h
@@ -5,15 +5,11 @@
#ifndef SUNSHINE_NVHTTP_H
#define SUNSHINE_NVHTTP_H
-#include
-#include
+#include "thread_safe.h"
#include
#include
-#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
+#include
namespace nvhttp {
void start(std::shared_ptr shutdown_event);