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, std::string& redirectUrl,
86 PendingResult pending;
88 std::lock_guard<std::mutex> lock(stateMtx);
91 error =
"navigate_already_in_progress";
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);
113 const uint32_t timeout = timeoutMs ? timeoutMs : kDefaultTimeoutMs;
114 bool signalled =
false;
116 std::unique_lock<std::mutex> lock(pending.mtx);
117 signalled = pending.cv.wait_for(lock, std::chrono::milliseconds(timeout),
118 [&] {
return pending.done; });
122 std::lock_guard<std::mutex> lock(stateMtx);
125 if (current == &pending)
137#if defined(__linux__)
138 auto handle = w.window();
140 gtk_widget_hide(GTK_WIDGET(handle.value()));
142 w.navigate(
"about:blank");
144 w.navigate(
"about:blank");
156 error = pending.errorMessage.empty() ?
"user_cancelled" : pending.errorMessage;
160 redirectUrl = pending.redirectUrl;
166 std::lock_guard<std::mutex> lock(stateMtx);
167 finishCurrentLocked(
false,
"",
"user_cancelled");
173 std::lock_guard<std::mutex> lock(stateMtx);
174 finishCurrentLocked(
false,
"",
"shutting_down");
176 w.dispatch([
this]() { w.terminate(); });
180 static void onNavigateStatic(webview_t wv,
const char* uri, webview_navigation_event_t type,
184 if (type != WEBVIEW_LOAD_FINISHED)
186 static_cast<Session*
>(arg)->onNavigate(uri);
189 void onNavigate(
const std::string& uri)
191 std::lock_guard<std::mutex> lock(stateMtx);
192 if (!current || !watcher.matches(uri))
196 if (watcher.hasError(uri, err))
197 finishCurrentLocked(
false,
"", err);
199 finishCurrentLocked(
true, uri,
"");
203 void finishCurrentLocked(
bool ok,
const std::string& redirectUrl,
const std::string& err)
208 std::lock_guard<std::mutex> lock(current->mtx);
209 current->done =
true;
211 current->redirectUrl = redirectUrl;
212 current->errorMessage = err;
214 current->cv.notify_all();
220 PendingResult* current =
nullptr;
227 HANDLE g_cmdIn =
nullptr;
228 HANDLE g_cmdOut =
nullptr;
230 bool writeLine(
const std::string& line)
232 std::string data = line;
236 while (written < data.size())
239 if (!WriteFile(g_cmdOut, data.data() + written,
240 static_cast<DWORD
>(data.size() - written), &dwWritten,
nullptr) ||
243 written += dwWritten;
249 bool extractLine(std::string& buf, std::string& line)
251 const auto pos = buf.find(
'\n');
252 if (pos == std::string::npos)
254 line = buf.substr(0, pos);
255 buf.erase(0, pos + 1);
259 bool readLine(std::string& buf, std::string& line)
261 if (extractLine(buf, line))
268 if (!ReadFile(g_cmdIn, chunk,
sizeof(chunk), &dwRead,
nullptr) || (dwRead == 0))
270 buf.append(chunk, dwRead);
271 if (extractLine(buf, line))
276 void sendLine(
const std::string& line)
278 (void)writeLine(line);
281 void sendJson(WINPR_JSON* obj)
292 void sendHelloResult(int64_t
id)
308 void sendNavigateResult(int64_t
id,
const std::string& redirectUrl)
323 void sendNullResult(int64_t
id)
333 void sendError(int64_t
id,
int code,
const std::string& message)
348 std::string getStringField(WINPR_JSON* obj,
const char* name)
359 uint32_t getUintField(WINPR_JSON* obj,
const char* name, uint32_t def)
372 void readerLoop(Session& session)
376 while (readLine(buf, line))
388 const std::string method = getStringField(msg,
"method");
391 if (method ==
"hello")
395 else if (method ==
"navigate")
397 const std::string title = getStringField(params,
"title");
398 const std::string url = getStringField(params,
"url");
399 const std::string redirectUri = getStringField(params,
"redirect_uri");
400 const uint32_t timeoutMs = getUintField(params,
"timeout_ms", kDefaultTimeoutMs);
402 std::string redirectUrl;
404 if (session.navigate(title, url, redirectUri, timeoutMs, redirectUrl, error))
405 sendNavigateResult(
id, redirectUrl);
407 sendError(
id, 1, error);
409 else if (method ==
"cancel")
413 else if (method ==
"shutdown")
417 else if (method ==
"exit")
431int main(
int argc,
char* argv[])
433 std::string cmdInArg;
434 std::string cmdOutArg;
435 for (
int i = 1; i < argc; i++)
437 const std::string arg = argv[i];
438 if (arg.rfind(
"--cmdInFd=", 0) == 0)
440 else if (arg.rfind(
"--cmdOutFd=", 0) == 0)
444 if (cmdInArg.empty() || cmdOutArg.empty())
446 std::cerr <<
"usage: " << (argc > 0 ? argv[0] :
"freerdp-webview-aad-helper")
447 <<
" --cmdInFd=<handle> --cmdOutFd=<handle>" << std::endl;
451 g_cmdIn = winpr_importHandleFromString(cmdInArg.c_str(),
"--cmdInFd={}");
452 g_cmdOut = winpr_importHandleFromString(cmdOutArg.c_str(),
"--cmdOutFd={}");
453 if (!g_cmdIn || (g_cmdIn == INVALID_HANDLE_VALUE) || !g_cmdOut ||
454 (g_cmdOut == INVALID_HANDLE_VALUE))
456 std::cerr <<
"failed to import the cmdIn/cmdOut channel handles" << std::endl;
461 std::thread reader(readerLoop, std::ref(session));
466 (void)CloseHandle(g_cmdIn);
467 (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.