34#include <condition_variable>
41#include <winpr/file.h>
42#include <winpr/handle.h>
43#include <winpr/json.h>
45#include "redirect_watcher.hpp"
50 constexpr uint32_t kDefaultTimeoutMs = 180000;
55 std::condition_variable cv;
58 std::string redirectUrl;
59 std::string errorMessage;
72 Session() : w(false, nullptr)
74 w.add_navigation_listener(&Session::onNavigateStatic,
this);
82 bool navigate(
const std::string& title,
const std::string& url,
83 const std::string& redirectUri, uint32_t timeoutMs,
84 const std::string& userAgent, std::string& redirectUrl, std::string& error)
86 PendingResult pending;
88 std::lock_guard<std::mutex> lock(stateMtx);
91 error =
"navigate_already_in_progress";
99 [
this, title, url, userAgent]()
101#if defined(__linux__)
104 auto handle = w.window();
106 gtk_widget_show(GTK_WIDGET(handle.value()));
109 w.set_size(800, 600, WEBVIEW_HINT_NONE);
110#if defined(__linux__)
112 auto widget = w.widget();
115 WebKitSettings* settings =
116 webkit_web_view_get_settings(WEBKIT_WEB_VIEW(widget.value()));
121 webkit_settings_set_user_agent(
122 settings, userAgent.empty() ?
nullptr : userAgent.c_str());
129 const uint32_t timeout = timeoutMs ? timeoutMs : kDefaultTimeoutMs;
130 bool signalled =
false;
132 std::unique_lock<std::mutex> lock(pending.mtx);
133 signalled = pending.cv.wait_for(lock, std::chrono::milliseconds(timeout),
134 [&] {
return pending.done; });
138 std::lock_guard<std::mutex> lock(stateMtx);
141 if (current == &pending)
153#if defined(__linux__)
154 auto handle = w.window();
156 gtk_widget_hide(GTK_WIDGET(handle.value()));
158 w.navigate(
"about:blank");
160 w.navigate(
"about:blank");
172 error = pending.errorMessage.empty() ?
"user_cancelled" : pending.errorMessage;
176 redirectUrl = pending.redirectUrl;
182 std::lock_guard<std::mutex> lock(stateMtx);
183 finishCurrentLocked(
false,
"",
"user_cancelled");
189 std::lock_guard<std::mutex> lock(stateMtx);
190 finishCurrentLocked(
false,
"",
"shutting_down");
192 w.dispatch([
this]() { w.terminate(); });
196 static void onNavigateStatic(webview_t wv,
const char* uri, webview_navigation_event_t type,
200 if (type != WEBVIEW_LOAD_FINISHED)
202 static_cast<Session*
>(arg)->onNavigate(uri);
205 void onNavigate(
const std::string& uri)
207 std::lock_guard<std::mutex> lock(stateMtx);
208 if (!current || !watcher.matches(uri))
212 if (watcher.hasError(uri, err))
213 finishCurrentLocked(
false,
"", err);
215 finishCurrentLocked(
true, uri,
"");
219 void finishCurrentLocked(
bool ok,
const std::string& redirectUrl,
const std::string& err)
224 std::lock_guard<std::mutex> lock(current->mtx);
225 current->done =
true;
227 current->redirectUrl = redirectUrl;
228 current->errorMessage = err;
230 current->cv.notify_all();
236 PendingResult* current =
nullptr;
243 HANDLE g_cmdIn =
nullptr;
244 HANDLE g_cmdOut =
nullptr;
246 bool writeLine(
const std::string& line)
248 std::string data = line;
252 while (written < data.size())
255 if (!WriteFile(g_cmdOut, data.data() + written,
256 static_cast<DWORD
>(data.size() - written), &dwWritten,
nullptr) ||
259 written += dwWritten;
265 bool extractLine(std::string& buf, std::string& line)
267 const auto pos = buf.find(
'\n');
268 if (pos == std::string::npos)
270 line = buf.substr(0, pos);
271 buf.erase(0, pos + 1);
275 bool readLine(std::string& buf, std::string& line)
277 if (extractLine(buf, line))
284 if (!ReadFile(g_cmdIn, chunk,
sizeof(chunk), &dwRead,
nullptr) || (dwRead == 0))
286 buf.append(chunk, dwRead);
287 if (extractLine(buf, line))
292 void sendLine(
const std::string& line)
294 (void)writeLine(line);
297 void sendJson(WINPR_JSON* obj)
308 void sendHelloResult(int64_t
id)
324 void sendNavigateResult(int64_t
id,
const std::string& redirectUrl)
339 void sendNullResult(int64_t
id)
349 void sendError(int64_t
id,
int code,
const std::string& message)
364 std::string getStringField(WINPR_JSON* obj,
const char* name)
375 uint32_t getUintField(WINPR_JSON* obj,
const char* name, uint32_t def)
388 void readerLoop(Session& session)
392 while (readLine(buf, line))
404 const std::string method = getStringField(msg,
"method");
407 if (method ==
"hello")
411 else if (method ==
"navigate")
413 const std::string title = getStringField(params,
"title");
414 const std::string url = getStringField(params,
"url");
415 const std::string redirectUri = getStringField(params,
"redirect_uri");
416 const std::string userAgent = getStringField(params,
"user_agent");
417 const uint32_t timeoutMs = getUintField(params,
"timeout_ms", kDefaultTimeoutMs);
419 std::string redirectUrl;
421 if (session.navigate(title, url, redirectUri, timeoutMs, userAgent, redirectUrl,
423 sendNavigateResult(
id, redirectUrl);
425 sendError(
id, 1, error);
427 else if (method ==
"cancel")
431 else if (method ==
"shutdown")
435 else if (method ==
"exit")
449int main(
int argc,
char* argv[])
451 std::string cmdInArg;
452 std::string cmdOutArg;
453 for (
int i = 1; i < argc; i++)
455 const std::string arg = argv[i];
456 if (arg.rfind(
"--cmdInFd=", 0) == 0)
458 else if (arg.rfind(
"--cmdOutFd=", 0) == 0)
462 if (cmdInArg.empty() || cmdOutArg.empty())
464 std::cerr <<
"usage: " << (argc > 0 ? argv[0] :
"freerdp-webview-aad-helper")
465 <<
" --cmdInFd=<handle> --cmdOutFd=<handle>" << std::endl;
469 g_cmdIn = winpr_importHandleFromString(cmdInArg.c_str(),
"--cmdInFd={}");
470 g_cmdOut = winpr_importHandleFromString(cmdOutArg.c_str(),
"--cmdOutFd={}");
471 if (!g_cmdIn || (g_cmdIn == INVALID_HANDLE_VALUE) || !g_cmdOut ||
472 (g_cmdOut == INVALID_HANDLE_VALUE))
474 std::cerr <<
"failed to import the cmdIn/cmdOut channel handles" << std::endl;
479 std::thread reader(readerLoop, std::ref(session));
484 (void)CloseHandle(g_cmdIn);
485 (void)CloseHandle(g_cmdOut);
WINPR_ATTR_NODISCARD WINPR_API WINPR_JSON * WINPR_JSON_AddObjectToObject(WINPR_JSON *object, const char *name)
WINPR_JSON_AddObjectToObject.
WINPR_ATTR_NODISCARD WINPR_API WINPR_JSON * WINPR_JSON_CreateObject(void)
WINPR_JSON_CreateObject.
WINPR_ATTR_NODISCARD WINPR_API BOOL WINPR_JSON_IsNumber(const WINPR_JSON *item)
Check if JSON item is of type Number.
WINPR_ATTR_NODISCARD WINPR_API WINPR_JSON * WINPR_JSON_AddIntegerToObject(WINPR_JSON *object, const char *name, int64_t number)
WINPR_JSON_AddIntegerToObject.
WINPR_ATTR_NODISCARD WINPR_API WINPR_JSON * WINPR_JSON_GetObjectItemCaseSensitive(const WINPR_JSON *object, const char *string)
Same as WINPR_JSON_GetObjectItem but with case sensitive matching.
WINPR_ATTR_NODISCARD WINPR_API WINPR_JSON * WINPR_JSON_AddNumberToObject(WINPR_JSON *object, const char *name, double number)
WINPR_JSON_AddNumberToObject.
WINPR_ATTR_NODISCARD WINPR_API BOOL WINPR_JSON_IsString(const WINPR_JSON *item)
Check if JSON item is of type String.
WINPR_API char * WINPR_JSON_PrintUnformatted(WINPR_JSON *item)
Serialize a JSON instance to string without formatting for human readable formatted output see WINPR_...
WINPR_ATTR_NODISCARD WINPR_API double WINPR_JSON_GetNumberValue(const WINPR_JSON *item)
Return the Number value of a JSON item.
WINPR_ATTR_NODISCARD WINPR_API WINPR_JSON * WINPR_JSON_AddNullToObject(WINPR_JSON *object, const char *name)
WINPR_JSON_AddNullToObject.
WINPR_ATTR_NODISCARD WINPR_API WINPR_JSON * WINPR_JSON_AddStringToObject(WINPR_JSON *object, const char *name, const char *string)
WINPR_JSON_AddStringToObject.
WINPR_API void WINPR_JSON_Delete(WINPR_JSON *item)
Delete a WinPR JSON wrapper object.
WINPR_ATTR_NODISCARD WINPR_API const char * WINPR_JSON_GetStringValue(WINPR_JSON *item)
Return the String value of a JSON item.
WINPR_API WINPR_JSON * WINPR_JSON_Parse(const char *value)
Parse a '\0' terminated JSON string.