From 589c1539ed5c16cce1d500d9d7709606d08d05a2 Mon Sep 17 00:00:00 2001 From: Xander Frangos <33106561+xanderfrangos@users.noreply.github.com> Date: Sun, 26 May 2024 09:30:17 -0400 Subject: [PATCH] Squashed commit of the following: commit d2e21f98bcf89d7e250d405c31cd1c72a817eda5 Merge: 52cb9961 7fb8c765 Author: Xander Frangos <33106561+xanderfrangos@users.noreply.github.com> Date: Wed May 1 21:39:04 2024 -0400 Merge branch 'nightly' into unpair-single-client commit 52cb99611012dabdebb8caae51fb211bd8fdc58b Author: Xander Frangos <33106561+xanderfrangos@users.noreply.github.com> Date: Thu Apr 18 22:19:53 2024 -0400 Rename unpair_all_desc to unpair_desc commit f5a0916e9a2c1c33afbe047b4d894aa50eb089e9 Author: Xander Frangos <33106561+xanderfrangos@users.noreply.github.com> Date: Thu Apr 18 22:01:50 2024 -0400 Update src_assets/common/assets/web/troubleshooting.html Co-authored-by: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com> commit e30ccb4cdac94678dbfddc1b9e24276a5fd073b2 Author: Xander Frangos <33106561+xanderfrangos@users.noreply.github.com> Date: Thu Apr 18 22:01:02 2024 -0400 Update src_assets/common/assets/web/public/assets/locale/en.json Co-authored-by: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com> commit a64bfc42c7d47b467ee27ffe64bf0415587e131a Author: Xander Frangos <33106561+xanderfrangos@users.noreply.github.com> Date: Thu Apr 18 09:31:38 2024 -0400 Re-use navbar.pin string commit a3909774f788cdb0d673422fbeed242a85a3cfcb Author: Xander Frangos <33106561+xanderfrangos@users.noreply.github.com> Date: Wed Apr 17 21:35:07 2024 -0400 Additional variable reordering commit 63d58758a3c8edeaaed0c38a2eeeecb534d22f8a Author: Xander Frangos <33106561+xanderfrangos@users.noreply.github.com> Date: Wed Apr 17 21:32:27 2024 -0400 Reordered Vue page vars https://github.com/LizardByte/Sunshine/pull/2042#pullrequestreview-2007515774 commit 31c1f214406ee7fda1eba073d34eb2912b93b11d Author: Xander Frangos <33106561+xanderfrangos@users.noreply.github.com> Date: Wed Apr 17 21:29:34 2024 -0400 Specify that only individual unpair requires force close commit bb3c0ba03326aa8e1991a51dc6eeee94131c3112 Author: Xander Frangos <33106561+xanderfrangos@users.noreply.github.com> Date: Wed Apr 17 21:21:35 2024 -0400 Sort client list alphabetically https://github.com/LizardByte/Sunshine/pull/2042#pullrequestreview-2007515774 commit 2e825370b93bf3c4667684905c3aad7b87c93e9c Author: Xander Frangos <33106561+xanderfrangos@users.noreply.github.com> Date: Wed Apr 17 21:14:05 2024 -0400 Localize "PIN" input placeholder https://github.com/LizardByte/Sunshine/pull/2042#pullrequestreview-2007515774 commit 1b6a828b1cdcda2854097b2d5788ee1d48f9dff9 Author: Xander Frangos <33106561+xanderfrangos@users.noreply.github.com> Date: Wed Apr 17 21:13:02 2024 -0400 Close session when unpairing all clients https://github.com/LizardByte/Sunshine/pull/2042#pullrequestreview-2007534088 commit 270015244e8801ca59baab2ebe93649cd5f414c2 Author: Xander Frangos <33106561+xanderfrangos@users.noreply.github.com> Date: Wed Apr 17 13:04:20 2024 -0400 Formatting commit 4e7785480e3026a10db334bb78cd50d9d118ff51 Author: Xander Frangos <33106561+xanderfrangos@users.noreply.github.com> Date: Wed Apr 17 12:45:22 2024 -0400 Rename unpair all button commit cef0b19614f62acd286b632897840770bc22c2eb Author: Xander Frangos <33106561+xanderfrangos@users.noreply.github.com> Date: Wed Apr 17 12:42:45 2024 -0400 Refresh client certs after unpairing a single client commit 5ef7b03f1b8226d213f98063f375cc03934b153a Author: Xander Frangos <33106561+xanderfrangos@users.noreply.github.com> Date: Wed Apr 17 12:41:14 2024 -0400 Update web UI to use localization for single device pairing/unpairing commit da936b8d0fb88f30e799322725d06ee609a62859 Merge: 24526655 9c68a62a Author: Xander Frangos <33106561+xanderfrangos@users.noreply.github.com> Date: Wed Apr 17 11:01:42 2024 -0400 Merge branch 'nightly' into unpair-single-client commit 9c68a62abc02a8d5597a04ca4edc35a8a70de8d7 Merge: 2511bd03 ec8170cb Author: Xander Frangos <33106561+xanderfrangos@users.noreply.github.com> Date: Wed Apr 17 10:43:49 2024 -0400 Merge branch 'nightly' of https://github.com/xanderfrangos/Sunshine into nightly commit 24526655a56d45f1df2ee1932ae3bf9bc6777f8c Merge: 735d788f 65493d09 Author: Xander Frangos <33106561+xanderfrangos@users.noreply.github.com> Date: Sat Feb 10 18:22:42 2024 -0500 Merge remote-tracking branch 'upstream/nightly' into unpair-single-client commit 735d788f187f8d74db913203a4fcea7cb375e4bf Author: Xander Frangos <33106561+xanderfrangos@users.noreply.github.com> Date: Sat Feb 10 18:09:49 2024 -0500 Update named_cert_t to better match sunshine_state.json commit 5ff80ada0b93dd8527c0c7e20e42e9f75065b0f3 Author: Xander Frangos <33106561+xanderfrangos@users.noreply.github.com> Date: Sat Feb 10 17:53:05 2024 -0500 Remove uniqueID from client_t commit fbd26d0a386d197d8d7ba9472ddad151212ae599 Author: Xander Frangos <33106561+xanderfrangos@users.noreply.github.com> Date: Sun Jan 28 11:10:17 2024 -0500 Rename device name field and mark fields as required https://github.com/LizardByte/Sunshine/pull/2042#pullrequestreview-1845012932 commit b5d7da4590dca11e269b3490d7bfee71fec56929 Author: Xander Frangos <33106561+xanderfrangos@users.noreply.github.com> Date: Thu Jan 25 23:42:18 2024 -0500 Update src_assets/common/assets/web/troubleshooting.html Co-authored-by: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com> commit 60e2da7d5f95c2ce417cf3ecf416e6d18701013d Author: Xander Frangos <33106561+xanderfrangos@users.noreply.github.com> Date: Thu Jan 25 23:41:59 2024 -0500 Update src_assets/common/assets/web/troubleshooting.html Co-authored-by: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com> commit a4a4125d10e3992d5ce5e6c93f14fbceefc23799 Author: Xander Frangos <33106561+xanderfrangos@users.noreply.github.com> Date: Thu Jan 25 23:41:46 2024 -0500 Update src/confighttp.cpp Co-authored-by: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com> commit 805311bd672692bea36e7a90fa71556610855a86 Author: Xander Frangos <33106561+xanderfrangos@users.noreply.github.com> Date: Thu Jan 25 23:41:36 2024 -0500 Update src/confighttp.cpp Co-authored-by: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com> commit cb6846eaa9e88e5b87da4df72923d92aaba7e42b Author: Xander Frangos <33106561+xanderfrangos@users.noreply.github.com> Date: Thu Jan 25 23:41:25 2024 -0500 Update src/confighttp.cpp Co-authored-by: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com> commit 3a1c109ad870a7f1bddf916c93211862cca07e9c Author: Xander Frangos <33106561+xanderfrangos@users.noreply.github.com> Date: Thu Jan 25 23:41:03 2024 -0500 Update src/confighttp.cpp Co-authored-by: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com> commit dd6b59739764a9dbd47f4a0def0d70ea25f43c55 Author: Xander Frangos <33106561+xanderfrangos@users.noreply.github.com> Date: Thu Jan 25 23:18:51 2024 -0500 Update src_assets/common/assets/web/troubleshooting.html Co-authored-by: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com> commit 36b8bab42109a386bae8242889004834330a9c69 Author: Xander Frangos <33106561+xanderfrangos@users.noreply.github.com> Date: Thu Jan 25 23:18:38 2024 -0500 Update src_assets/common/assets/web/pin.html Co-authored-by: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com> commit 3f4fc1c4f5eaa57b5f860b37dcf3d5523fb03c6e Author: Xander Frangos <33106561+xanderfrangos@users.noreply.github.com> Date: Thu Jan 25 21:36:25 2024 -0500 Move Paired Client UI to Troubleshooting page commit 0e1c02b069707df4a3fd9a855b9e86afe4343a64 Author: Xander Frangos <33106561+xanderfrangos@users.noreply.github.com> Date: Thu Jan 25 21:20:11 2024 -0500 Update API to use uuid instead of uniqueid for clients commit 5cc7fc0d1376497787c76dff4420e0ab7b593d86 Author: Xander Frangos <33106561+xanderfrangos@users.noreply.github.com> Date: Wed Jan 24 17:17:10 2024 -0500 Rename named_certs to named_devices commit 541e341ccd34990e8ec7064a2fc6d209ea4d09bb Author: Xander Frangos <33106561+xanderfrangos@users.noreply.github.com> Date: Wed Jan 24 17:11:26 2024 -0500 Refactor map_id_client into a single client_t commit d842e97cd9469c92e30a1eb0f02f901673f21700 Author: Xander Frangos <33106561+xanderfrangos@users.noreply.github.com> Date: Wed Jan 24 15:46:39 2024 -0500 Formatting commit 6c639920b6f09f2a997ae4e7f20bf6a6d39641e0 Author: Xander Frangos <33106561+xanderfrangos@users.noreply.github.com> Date: Wed Jan 24 15:37:47 2024 -0500 Formatting commit b6010a6c8e1f65309e2d64124d6a7727667099fb Author: Xander Frangos <33106561+xanderfrangos@users.noreply.github.com> Date: Wed Jan 24 15:33:44 2024 -0500 Update sunshine_state.json format to no longer use root.devices sunshine_state.json now follows the format outlined in https://github.com/LizardByte/Sunshine/pull/2042#issuecomment-1904634424 commit 8c8dfcff9aa0963565e0944105ed0c828718df09 Author: Xander Frangos <33106561+xanderfrangos@users.noreply.github.com> Date: Wed Jan 24 15:23:40 2024 -0500 Fixed "restart Sunshine" banner not hiding after clicking "apply" commit ced01221297f5f419bc20219b35d88b373b20000 Merge: 67988be1 2511bd03 Author: Xander Frangos <33106561+xanderfrangos@users.noreply.github.com> Date: Sat Jan 20 04:55:46 2024 -0500 Merge branch 'nightly' into unpair-single-client commit 2511bd032ade1c773638dc2fa0586b45df7845c0 Merge: b8f8b46b a10ec3a9 Author: Xander Frangos <33106561+xanderfrangos@users.noreply.github.com> Date: Sat Jan 20 04:54:59 2024 -0500 Merge branch 'LizardByte:nightly' into nightly commit 67988be12f00f1b6747c95521034bfc44583d7d5 Author: Xander Frangos <33106561+xanderfrangos@users.noreply.github.com> Date: Sat Jan 20 04:43:14 2024 -0500 Use lowercase property name for consistency commit bb3fd9a45eee3bae3f12e9dbdbd703f9e8d77297 Author: Xander Frangos <33106561+xanderfrangos@users.noreply.github.com> Date: Sat Jan 20 04:16:48 2024 -0500 Comment typo commit 73bda364113ec3080abe91b631a0d37a19b9c788 Author: Xander Frangos <33106561+xanderfrangos@users.noreply.github.com> Date: Sat Jan 20 04:05:45 2024 -0500 Formatting commit b8f8b46b67460a4a16c42693cbdb05d59f9af58a Author: Xander Frangos <33106561+xanderfrangos@users.noreply.github.com> Date: Sat Jan 20 02:59:37 2024 -0500 Added UI for unpairing individual clients commit 2e7655bfff677e9427b7b90cab0a237b4ee7e747 Author: Xander Frangos <33106561+xanderfrangos@users.noreply.github.com> Date: Sat Jan 20 02:58:15 2024 -0500 During unpairing, also remove cert from client.certs commit c8d74f21605edacaec8e2c015d185fcd3db3146a Author: Xander Frangos <33106561+xanderfrangos@users.noreply.github.com> Date: Fri Jan 19 21:59:35 2024 -0500 API to unpair clients commit 4b9f9d0c4eaed20d806743ca5c968fa42f300e65 Author: Xander Frangos <33106561+xanderfrangos@users.noreply.github.com> Date: Fri Jan 19 21:57:41 2024 -0500 API to list paired clients commit 223fdfa6592b46674ba2f6668b841e8870ece65b Author: Xander Frangos <33106561+xanderfrangos@users.noreply.github.com> Date: Fri Jan 19 12:17:55 2024 -0500 Generate and track uniqueID for named certs commit 567725d2d62ebc533a8b72022a0cc5ae08f9127d Author: Xander Frangos <33106561+xanderfrangos@users.noreply.github.com> Date: Fri Jan 19 12:17:22 2024 -0500 Only read "certs" and "named_certs" if available commit ca6d1a28300a09598b3f02b457d84a242ddfbcc4 Author: Xander Frangos <33106561+xanderfrangos@users.noreply.github.com> Date: Fri Jan 19 12:16:25 2024 -0500 Don't prepare nodes for old format commit 9f6a4856fefd5f7f721eb848ec9102462a2f6092 Author: Xander Frangos <33106561+xanderfrangos@users.noreply.github.com> Date: Fri Jan 19 09:41:09 2024 -0500 Assign name to certs when confirming PIN --- src/confighttp.cpp | 61 +++++- src/nvhttp.cpp | 176 +++++++++++++----- src/nvhttp.h | 9 +- src_assets/common/assets/web/pin.html | 9 +- .../assets/web/public/assets/locale/en.json | 10 +- .../common/assets/web/troubleshooting.html | 80 ++++++-- 6 files changed, 274 insertions(+), 71 deletions(-) diff --git a/src/confighttp.cpp b/src/confighttp.cpp index de25bf0e7cf..a237c1fc3a7 100644 --- a/src/confighttp.cpp +++ b/src/confighttp.cpp @@ -693,7 +693,8 @@ namespace confighttp { // TODO: Input Validation pt::read_json(ss, inputTree); std::string pin = inputTree.get("pin"); - outputTree.put("status", nvhttp::pin(pin)); + std::string name = inputTree.get("name"); + outputTree.put("status", nvhttp::pin(pin, name)); } catch (std::exception &e) { BOOST_LOG(warning) << "SavePin: "sv << e.what(); @@ -717,6 +718,60 @@ namespace confighttp { response->write(data.str()); }); nvhttp::erase_all_clients(); + proc::proc.terminate(); + outputTree.put("status", true); + } + + void + unpair(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 inputTree, outputTree; + + auto g = util::fail_guard([&]() { + std::ostringstream data; + pt::write_json(data, outputTree); + response->write(data.str()); + }); + + try { + // TODO: Input Validation + pt::read_json(ss, inputTree); + std::string uuid = inputTree.get("uuid"); + outputTree.put("status", nvhttp::unpair_client(uuid)); + } + catch (std::exception &e) { + BOOST_LOG(warning) << "Unpair: "sv << e.what(); + outputTree.put("status", false); + outputTree.put("error", e.what()); + return; + } + } + + void + listClients(resp_https_t response, req_https_t request) { + if (!authenticate(response, request)) return; + + print_req(request); + + pt::ptree named_certs = nvhttp::get_all_clients(); + + pt::ptree outputTree; + + outputTree.put("status", false); + + auto g = util::fail_guard([&]() { + std::ostringstream data; + pt::write_json(data, outputTree); + response->write(data.str()); + }); + + outputTree.add_child("named_certs", named_certs); outputTree.put("status", true); } @@ -765,7 +820,9 @@ namespace confighttp { server.resource["^/api/restart$"]["POST"] = restart; server.resource["^/api/password$"]["POST"] = savePassword; server.resource["^/api/apps/([0-9]+)$"]["DELETE"] = deleteApp; - server.resource["^/api/clients/unpair$"]["POST"] = unpairAll; + server.resource["^/api/clients/unpair-all$"]["POST"] = unpairAll; + server.resource["^/api/clients/list$"]["GET"] = listClients; + server.resource["^/api/clients/unpair$"]["POST"] = unpair; server.resource["^/api/apps/close$"]["POST"] = closeApp; server.resource["^/api/covers/upload$"]["POST"] = uploadCover; server.resource["^/images/sunshine.ico$"]["GET"] = getFaviconImage; diff --git a/src/nvhttp.cpp b/src/nvhttp.cpp index 70ca9bc79be..f03be3c76b9 100644 --- a/src/nvhttp.cpp +++ b/src/nvhttp.cpp @@ -117,9 +117,15 @@ namespace nvhttp { std::string pkey; } conf_intern; + struct named_cert_t { + std::string name; + std::string uuid; + std::string cert; + }; + struct client_t { - std::string uniqueID; std::vector certs; + std::vector named_devices; }; struct pair_session_t { @@ -145,7 +151,7 @@ namespace nvhttp { // uniqueID, session std::unordered_map map_id_sess; - std::unordered_map map_id_client; + client_t client_root; std::atomic session_id_counter; using args_t = SimpleWeb::CaseInsensitiveMultimap; @@ -189,22 +195,18 @@ namespace nvhttp { 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) { - pt::ptree node; - - node.put("uniqueid"s, client.uniqueID); - - pt::ptree cert_nodes; - for (auto &cert : client.certs) { - pt::ptree cert_node; - cert_node.put_value(cert); - cert_nodes.push_back(std::make_pair(""s, cert_node)); - } - node.add_child("certs"s, cert_nodes); - - nodes.push_back(std::make_pair(""s, node)); + client_t &client = client_root; + pt::ptree node; + + pt::ptree named_cert_nodes; + for (auto &named_cert : client.named_devices) { + pt::ptree named_cert_node; + named_cert_node.put("name"s, named_cert.name); + named_cert_node.put("cert"s, named_cert.cert); + named_cert_node.put("uuid"s, named_cert.uuid); + named_cert_nodes.push_back(std::make_pair(""s, named_cert_node)); } + root.add_child("root.named_devices"s, named_cert_nodes); try { pt::write_json(config::nvhttp.file_state, root); @@ -223,9 +225,9 @@ namespace nvhttp { return; } - pt::ptree root; + pt::ptree tree; try { - pt::read_json(config::nvhttp.file_state, root); + pt::read_json(config::nvhttp.file_state, tree); } catch (std::exception &e) { BOOST_LOG(error) << "Couldn't read "sv << config::nvhttp.file_state << ": "sv << e.what(); @@ -233,7 +235,7 @@ namespace nvhttp { return; } - auto unique_id_p = root.get_optional("root.uniqueid"); + auto unique_id_p = tree.get_optional("root.uniqueid"); if (!unique_id_p) { // This file doesn't contain moonlight credentials http::unique_id = uuid_util::uuid_t::generate().string(); @@ -241,30 +243,61 @@ namespace nvhttp { } http::unique_id = std::move(*unique_id_p); - auto device_nodes = root.get_child("root.devices"); - - for (auto &[_, device_node] : device_nodes) { - auto uniqID = device_node.get("uniqueid"); - auto &client = map_id_client.emplace(uniqID, client_t {}).first->second; - - client.uniqueID = uniqID; + auto root = tree.get_child("root"); + client_t client; + + // Import from old format + if (root.get_child_optional("devices")) { + auto device_nodes = root.get_child("devices"); + for (auto &[_, device_node] : device_nodes) { + auto uniqID = device_node.get("uniqueid"); + + if (device_node.count("certs")) { + for (auto &[_, el] : device_node.get_child("certs")) { + named_cert_t named_cert; + named_cert.name = ""s; + named_cert.cert = el.get_value(); + named_cert.uuid = uuid_util::uuid_t::generate().string(); + client.named_devices.emplace_back(named_cert); + client.certs.emplace_back(named_cert.cert); + } + } + } + } - for (auto &[_, el] : device_node.get_child("certs")) { - client.certs.emplace_back(el.get_value()); + if (root.count("named_devices")) { + for (auto &[_, el] : root.get_child("named_devices")) { + named_cert_t named_cert; + named_cert.name = el.get_child("name").get_value(); + named_cert.cert = el.get_child("cert").get_value(); + named_cert.uuid = el.get_child("uuid").get_value(); + client.named_devices.emplace_back(named_cert); + client.certs.emplace_back(named_cert.cert); } } + + // Empty certificate chain and import certs from file + cert_chain.clear(); + for (auto &cert : client.certs) { + cert_chain.add(crypto::x509(cert)); + } + for (auto &named_cert : client.named_devices) { + cert_chain.add(crypto::x509(named_cert.cert)); + } + + client_root = client; } void update_id_client(const std::string &uniqueID, std::string &&cert, op_e op) { switch (op) { case op_e::ADD: { - auto &client = map_id_client[uniqueID]; + client_t &client = client_root; client.certs.emplace_back(std::move(cert)); - client.uniqueID = uniqueID; } break; case op_e::REMOVE: - map_id_client.erase(uniqueID); + client_t client; + client_root = client; break; } @@ -579,15 +612,16 @@ namespace nvhttp { /** * @brief Compare the user supplied pin to the Moonlight pin. * @param pin The user supplied pin. + * @param name The user supplied name. * @return `true` if the pin is correct, `false` otherwise. * * EXAMPLES: * ```cpp - * bool pin_status = nvhttp::pin("1234"); + * bool pin_status = nvhttp::pin("1234", "laptop"); * ``` */ bool - pin(std::string pin) { + pin(std::string pin, std::string name) { pt::ptree tree; if (map_id_sess.empty()) { return false; @@ -613,6 +647,14 @@ namespace nvhttp { auto &sess = std::begin(map_id_sess)->second; getservercert(sess, tree, pin); + // set up named cert + client_t &client = client_root; + named_cert_t named_cert; + named_cert.name = name; + named_cert.cert = sess.client.cert; + named_cert.uuid = uuid_util::uuid_t::generate().string(); + client.named_devices.emplace_back(named_cert); + // response to the request for pin std::ostringstream data; pt::write_xml(data, tree); @@ -645,9 +687,7 @@ namespace nvhttp { auto clientID = args.find("uniqueid"s); if (clientID != std::end(args)) { - if (auto it = map_id_client.find(clientID->second); it != std::end(map_id_client)) { - pair_status = 1; - } + pair_status = 1; } } @@ -742,6 +782,20 @@ namespace nvhttp { response->close_connection_after_response = true; } + pt::ptree + get_all_clients() { + pt::ptree named_cert_nodes; + client_t &client = client_root; + for (auto &named_cert : client.named_devices) { + pt::ptree named_cert_node; + named_cert_node.put("name"s, named_cert.name); + named_cert_node.put("uuid"s, named_cert.uuid); + named_cert_nodes.push_back(std::make_pair(""s, named_cert_node)); + } + + return named_cert_nodes; + } + void applist(resp_https_t response, req_https_t request) { print_req(request); @@ -1020,12 +1074,6 @@ namespace nvhttp { conf_intern.pkey = file_handler::read_file(config::nvhttp.pkey.c_str()); conf_intern.servercert = file_handler::read_file(config::nvhttp.cert.c_str()); - for (auto &[_, client] : map_id_client) { - for (auto &cert : client.certs) { - cert_chain.add(crypto::x509(cert)); - } - } - auto add_cert = std::make_shared>(30); // resume doesn't always get the parameter "localAudioPlayMode" @@ -1149,8 +1197,48 @@ namespace nvhttp { */ void erase_all_clients() { - map_id_client.clear(); + client_t client; + client_root = client; cert_chain.clear(); save_state(); } + + /** + * @brief Remove single client. + * + * EXAMPLES: + * ```cpp + * nvhttp::unpair_client("4D7BB2DD-5704-A405-B41C-891A022932E1"); + * ``` + */ + int + unpair_client(std::string uuid) { + int removed = 0; + client_t &client = client_root; + for (auto it = client.named_devices.begin(); it != client.named_devices.end();) { + if ((*it).uuid == uuid) { + // Find matching cert and remove it + for (auto cert = client.certs.begin(); cert != client.certs.end();) { + if ((*cert) == (*it).cert) { + cert = client.certs.erase(cert); + removed++; + } + else { + ++cert; + } + } + + // And then remove the named cert + it = client.named_devices.erase(it); + removed++; + } + else { + ++it; + } + } + + save_state(); + load_state(); + return removed; + } } // namespace nvhttp diff --git a/src/nvhttp.h b/src/nvhttp.h index 3be24b3de06..6fdf202ac29 100644 --- a/src/nvhttp.h +++ b/src/nvhttp.h @@ -9,6 +9,9 @@ // standard includes #include +// lib includes +#include + // local includes #include "thread_safe.h" @@ -43,7 +46,11 @@ namespace nvhttp { void start(); bool - pin(std::string pin); + pin(std::string pin, std::string name); + int + unpair_client(std::string uniqueid); + boost::property_tree::ptree + get_all_clients(); void erase_all_clients(); } // namespace nvhttp diff --git a/src_assets/common/assets/web/pin.html b/src_assets/common/assets/web/pin.html index f3139e123cc..82b45299e67 100644 --- a/src_assets/common/assets/web/pin.html +++ b/src_assets/common/assets/web/pin.html @@ -8,10 +8,11 @@
-

{{ $t('pin.pin_pairing') }}

+

{{ $t('pin.pin_pairing') }}

- + +
@@ -39,8 +40,9 @@

{{ $t('pin.pin_pairing') }}

document.querySelector("#form").addEventListener("submit", (e) => { e.preventDefault(); let pin = document.querySelector("#pin-input").value; + let name = document.querySelector("#name-input").value; document.querySelector("#status").innerHTML = ""; - let b = JSON.stringify({ pin: pin }); + let b = JSON.stringify({ pin: pin, name: name }); fetch("/api/pin", { method: "POST", body: b }) .then((response) => response.json()) .then((response) => { @@ -49,6 +51,7 @@

{{ $t('pin.pin_pairing') }}

"#status" ).innerHTML = ``; document.querySelector("#pin-input").value = ""; + document.querySelector("#name-input").value = ""; } else { document.querySelector( "#status" diff --git a/src_assets/common/assets/web/public/assets/locale/en.json b/src_assets/common/assets/web/public/assets/locale/en.json index ac69860c305..5b08ee5c7f2 100644 --- a/src_assets/common/assets/web/public/assets/locale/en.json +++ b/src_assets/common/assets/web/public/assets/locale/en.json @@ -6,6 +6,7 @@ "cancel": "Cancel", "disabled": "Disabled", "disabled_def": "Disabled (default)", + "dismiss": "Dismiss", "do_cmd": "Do Command", "elevated": "Elevated", "enabled": "Enabled", @@ -352,6 +353,7 @@ "success_msg": "Password has been changed successfully! This page will reload soon, your browser will ask you for the new credentials." }, "pin": { + "device_name": "Device Name", "pair_failure": "Pairing Failed: Check if the PIN is typed correctly", "pair_success": "Success! Please check Moonlight to continue", "pin_pairing": "PIN Pairing", @@ -381,9 +383,13 @@ "restart_sunshine_success": "Sunshine is restarting", "troubleshooting": "Troubleshooting", "unpair_all": "Unpair All", - "unpair_all_desc": "Remove all your paired devices", "unpair_all_error": "Error while unpairing", - "unpair_all_success": "Unpair Successful!" + "unpair_all_success": "All devices unpaired.", + "unpair_desc": "Remove your paired devices. Individually unpaired devices with an active session will remain connected, but cannot start or resume a session.", + "unpair_single_no_devices": "There are no paired devices.", + "unpair_single_success": "However, the device(s) may still be in an active session. Use the 'Force Close' button above to end any open sessions.", + "unpair_single_unknown": "Unknown Client", + "unpair_title": "Unpair Devices" }, "welcome": { "confirm_password": "Confirm password", diff --git a/src_assets/common/assets/web/troubleshooting.html b/src_assets/common/assets/web/troubleshooting.html index 0adc16542af..2a41666054a 100644 --- a/src_assets/common/assets/web/troubleshooting.html +++ b/src_assets/common/assets/web/troubleshooting.html @@ -75,24 +75,39 @@

{{ $t('troubleshooting.restart_sunshine') }}

- -
+ +
-

{{ $t('troubleshooting.unpair_all') }}

-
-

{{ $t('troubleshooting.unpair_all_desc') }}

-
- {{ $t('troubleshooting.unpair_all_success') }} -
-
- {{ $t('troubleshooting.unpair_all_error') }} -
-
- +
+
+

{{ $t('troubleshooting.unpair_title') }}

+ +
+
+

{{ $t('troubleshooting.unpair_desc') }}

+
+
{{ $t('_common.success') }} {{ $t('troubleshooting.unpair_single_success') }}
+ +
+
+ {{ $t('troubleshooting.unpair_all_success') }} +
+
+ {{ $t('troubleshooting.unpair_all_error') }} +
+
    +
    +
    {{client.name != "" ? client.name : $t('troubleshooting.unpair_single_unknown')}}
    +
    +
+
    +
    {{ $t('troubleshooting.unpair_single_no_devices') }}
    +
+
@@ -123,14 +138,16 @@

{{ $t('troubleshooting.logs') }}

}, data() { return { + clients: [], closeAppPressed: false, closeAppStatus: null, - unpairAllPressed: false, - unpairAllStatus: null, - restartPressed: false, logs: 'Loading...', logFilter: null, logInterval: null, + restartPressed: false, + showApplyMessage: false, + unpairAllPressed: false, + unpairAllStatus: null, }; }, computed: { @@ -146,6 +163,7 @@

{{ $t('troubleshooting.logs') }}

this.refreshLogs(); }, 5000); this.refreshLogs(); + this.refreshClients(); }, beforeDestroy() { clearInterval(this.logInterval); @@ -172,7 +190,7 @@

{{ $t('troubleshooting.logs') }}

}, unpairAll() { this.unpairAllPressed = true; - fetch("/api/clients/unpair", { method: "POST" }) + fetch("/api/clients/unpair-all", { method: "POST" }) .then((r) => r.json()) .then((r) => { this.unpairAllPressed = false; @@ -180,8 +198,32 @@

{{ $t('troubleshooting.logs') }}

setTimeout(() => { this.unpairAllStatus = null; }, 5000); + this.refreshClients(); }); }, + unpairSingle(uuid) { + fetch("/api/clients/unpair", { method: "POST", body: JSON.stringify({ uuid }) }).then(() => { + this.showApplyMessage = true; + this.refreshClients(); + }); + }, + refreshClients() { + fetch("/api/clients/list") + .then((response) => response.json()) + .then((response) => { + const clientList = document.querySelector("#client-list"); + if (response.status === 'true' && response.named_certs && response.named_certs.length) { + this.clients = response.named_certs.sort((a, b) => { + return (a.name.toLowerCase() > b.name.toLowerCase() || a.name == "" ? 1 : -1) + }); + } else { + this.clients = []; + } + }); + }, + clickedApplyBanner() { + this.showApplyMessage = false; + }, copyLogs() { navigator.clipboard.writeText(this.actualLogs); },