2 * FreeRDP: A Remote Desktop Protocol Implementation
4 * Copyright 2014 Marc-Andre Moreau <marcandre.moreau@gmail.com>
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
23 #include <winpr/crt.h>
24 #include <winpr/ssl.h>
25 #include <winpr/wnd.h>
26 #include <winpr/path.h>
27 #include <winpr/cmdline.h>
28 #include <winpr/winsock.h>
30 #include <freerdp/log.h>
31 #include <freerdp/version.h>
33 #include <winpr/tools/makecert.h>
36 #include <sys/select.h>
37 #include <sys/signal.h>
42 #define TAG SERVER_TAG("shadow")
44 rdpShadowFont* g_Font = NULL;
46 static COMMAND_LINE_ARGUMENT_A shadow_args[] =
48 { "port", COMMAND_LINE_VALUE_REQUIRED, "<number>", NULL, NULL, -1, NULL, "Server port" },
49 { "ipc-socket", COMMAND_LINE_VALUE_REQUIRED, "<ipc-socket>", NULL, NULL, -1, NULL, "Server IPC socket" },
50 { "monitors", COMMAND_LINE_VALUE_OPTIONAL, "<0,1,2...>", NULL, NULL, -1, NULL, "Select or list monitors" },
51 { "rect", COMMAND_LINE_VALUE_REQUIRED, "<x,y,w,h>", NULL, NULL, -1, NULL, "Select rectangle within monitor to share" },
52 { "auth", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueFalse, NULL, -1, NULL, "Clients must authenticate" },
53 { "may-view", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueTrue, NULL, -1, NULL, "Clients may view without prompt" },
54 { "may-interact", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueTrue, NULL, -1, NULL, "Clients may interact without prompt" },
55 { "version", COMMAND_LINE_VALUE_FLAG | COMMAND_LINE_PRINT_VERSION, NULL, NULL, NULL, -1, NULL, "Print version" },
56 { "help", COMMAND_LINE_VALUE_FLAG | COMMAND_LINE_PRINT_HELP, NULL, NULL, NULL, -1, "?", "Print help" },
57 { NULL, 0, NULL, NULL, NULL, -1, NULL, NULL }
60 int shadow_server_print_command_line_help(int argc, char** argv)
64 COMMAND_LINE_ARGUMENT_A* arg;
66 WLog_INFO(TAG, "Usage: %s [options]", argv[0]);
69 WLog_INFO(TAG, "Syntax:");
70 WLog_INFO(TAG, " /flag (enables flag)");
71 WLog_INFO(TAG, " /option:<value> (specifies option with value)");
72 WLog_INFO(TAG, " +toggle -toggle (enables or disables toggle, where '/' is a synonym of '+')");
79 if (arg->Flags & COMMAND_LINE_VALUE_FLAG)
81 WLog_INFO(TAG, " %s", "/");
82 WLog_INFO(TAG, "%-20s", arg->Name);
83 WLog_INFO(TAG, "\t%s", arg->Text);
85 else if ((arg->Flags & COMMAND_LINE_VALUE_REQUIRED) || (arg->Flags & COMMAND_LINE_VALUE_OPTIONAL))
87 WLog_INFO(TAG, " %s", "/");
91 length = (int) (strlen(arg->Name) + strlen(arg->Format) + 2);
92 str = (char*) malloc(length + 1);
93 sprintf_s(str, length + 1, "%s:%s", arg->Name, arg->Format);
94 WLog_INFO(TAG, "%-20s", str);
99 WLog_INFO(TAG, "%-20s", arg->Name);
102 WLog_INFO(TAG, "\t%s", arg->Text);
104 else if (arg->Flags & COMMAND_LINE_VALUE_BOOL)
106 length = (int) strlen(arg->Name) + 32;
107 str = (char*) malloc(length + 1);
108 sprintf_s(str, length + 1, "%s (default:%s)", arg->Name,
109 arg->Default ? "on" : "off");
111 WLog_INFO(TAG, " %s", arg->Default ? "-" : "+");
113 WLog_INFO(TAG, "%-20s", str);
116 WLog_INFO(TAG, "\t%s", arg->Text);
119 while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
124 int shadow_server_command_line_status_print(rdpShadowServer* server, int argc, char** argv, int status)
126 if (status == COMMAND_LINE_STATUS_PRINT_VERSION)
128 WLog_INFO(TAG, "FreeRDP version %s (git %s)", FREERDP_VERSION_FULL, GIT_REVISION);
129 return COMMAND_LINE_STATUS_PRINT_VERSION;
131 else if (status == COMMAND_LINE_STATUS_PRINT)
133 return COMMAND_LINE_STATUS_PRINT;
137 shadow_server_print_command_line_help(argc, argv);
138 return COMMAND_LINE_STATUS_PRINT_HELP;
144 int shadow_server_parse_command_line(rdpShadowServer* server, int argc, char** argv)
148 COMMAND_LINE_ARGUMENT_A* arg;
153 CommandLineClearArgumentsA(shadow_args);
155 flags = COMMAND_LINE_SEPARATOR_COLON;
156 flags |= COMMAND_LINE_SIGIL_SLASH | COMMAND_LINE_SIGIL_PLUS_MINUS;
158 status = CommandLineParseArgumentsA(argc, (const char**) argv, shadow_args, flags, server, NULL, NULL);
167 if (!(arg->Flags & COMMAND_LINE_ARGUMENT_PRESENT))
170 CommandLineSwitchStart(arg)
172 CommandLineSwitchCase(arg, "port")
174 server->port = (DWORD) atoi(arg->Value);
176 CommandLineSwitchCase(arg, "ipc-socket")
178 server->ipcSocket = _strdup(arg->Value);
180 CommandLineSwitchCase(arg, "may-view")
182 server->mayView = arg->Value ? TRUE : FALSE;
184 CommandLineSwitchCase(arg, "may-interact")
186 server->mayInteract = arg->Value ? TRUE : FALSE;
188 CommandLineSwitchCase(arg, "rect")
193 char* str = _strdup(arg->Value);
200 p = strchr(p + 1, ',');
208 p = strchr(p + 1, ',');
216 p = strchr(p + 1, ',');
229 if ((x < 0) || (y < 0) || (w < 1) || (h < 1))
232 server->subRect.left = x;
233 server->subRect.top = y;
234 server->subRect.right = x + w;
235 server->subRect.bottom = y + h;
236 server->shareSubRect = TRUE;
238 CommandLineSwitchCase(arg, "auth")
240 server->authentication = arg->Value ? TRUE : FALSE;
242 CommandLineSwitchDefault(arg)
247 CommandLineSwitchEnd(arg)
249 while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
251 arg = CommandLineFindArgumentA(shadow_args, "monitors");
253 if (arg && (arg->Flags & COMMAND_LINE_ARGUMENT_PRESENT))
257 MONITOR_DEF monitors[16];
259 numMonitors = shadow_enum_monitors(monitors, 16, 0);
261 if (arg->Flags & COMMAND_LINE_VALUE_PRESENT)
263 /* Select monitors */
265 index = atoi(arg->Value);
270 if (index >= numMonitors)
273 server->selectedMonitor = index;
278 MONITOR_DEF* monitor;
282 for (index = 0; index < numMonitors; index++)
284 monitor = &monitors[index];
286 width = monitor->right - monitor->left;
287 height = monitor->bottom - monitor->top;
289 WLog_INFO(TAG, " %s [%d] %dx%d\t+%d+%d",
290 (monitor->flags == 1) ? "*" : " ", index,
291 width, height, monitor->left, monitor->top);
294 status = COMMAND_LINE_STATUS_PRINT;
301 void* shadow_server_thread(rdpShadowServer* server)
307 freerdp_listener* listener;
308 rdpShadowSubsystem* subsystem;
310 listener = server->listener;
311 StopEvent = server->StopEvent;
312 subsystem = server->subsystem;
314 shadow_subsystem_start(server->subsystem);
320 if (listener->GetEventHandles(listener, events, &nCount) < 0)
322 WLog_ERR(TAG, "Failed to get FreeRDP file descriptor");
326 events[nCount++] = server->StopEvent;
328 status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE);
330 if (WaitForSingleObject(server->StopEvent, 0) == WAIT_OBJECT_0)
335 if (!listener->CheckFileDescriptor(listener))
337 WLog_ERR(TAG, "Failed to check FreeRDP file descriptor");
342 Sleep(100); /* FIXME: listener event handles */
346 listener->Close(listener);
348 shadow_subsystem_stop(server->subsystem);
355 int shadow_server_start(rdpShadowServer* server)
360 if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
364 signal(SIGPIPE, SIG_IGN);
367 server->screen = shadow_screen_new(server);
372 server->capture = shadow_capture_new(server);
374 if (!server->capture)
377 if (!server->ipcSocket)
378 status = server->listener->Open(server->listener, NULL, (UINT16) server->port);
380 status = server->listener->OpenLocal(server->listener, server->ipcSocket);
384 server->thread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)
385 shadow_server_thread, (void*) server, 0, NULL);
391 int shadow_server_stop(rdpShadowServer* server)
395 SetEvent(server->StopEvent);
396 WaitForSingleObject(server->thread, INFINITE);
397 CloseHandle(server->thread);
398 server->thread = NULL;
400 server->listener->Close(server->listener);
405 shadow_screen_free(server->screen);
406 server->screen = NULL;
411 shadow_capture_free(server->capture);
412 server->capture = NULL;
418 int shadow_server_init_config_path(rdpShadowServer* server)
421 if (!server->ConfigPath)
423 server->ConfigPath = GetEnvironmentSubPath("LOCALAPPDATA", "freerdp");
428 if (!server->ConfigPath)
430 char* userLibraryPath;
431 char* userApplicationSupportPath;
433 userLibraryPath = GetKnownSubPath(KNOWN_PATH_HOME, "Library");
437 if (!PathFileExistsA(userLibraryPath))
438 CreateDirectoryA(userLibraryPath, 0);
440 userApplicationSupportPath = GetCombinedPath(userLibraryPath, "Application Support");
442 if (userApplicationSupportPath)
444 if (!PathFileExistsA(userApplicationSupportPath))
445 CreateDirectoryA(userApplicationSupportPath, 0);
447 server->ConfigPath = GetCombinedPath(userApplicationSupportPath, "freerdp");
450 free(userLibraryPath);
451 free(userApplicationSupportPath);
456 if (!server->ConfigPath)
460 configHome = GetKnownPath(KNOWN_PATH_XDG_CONFIG_HOME);
464 if (!PathFileExistsA(configHome))
465 CreateDirectoryA(configHome, 0);
467 server->ConfigPath = GetKnownSubPath(KNOWN_PATH_XDG_CONFIG_HOME, "freerdp");
473 if (!server->ConfigPath)
474 return -1; /* no usable config path */
479 int shadow_server_init_certificate(rdpShadowServer* server)
482 MAKECERT_CONTEXT* makecert;
484 const char* makecert_argv[6] =
493 int makecert_argc = (sizeof(makecert_argv) / sizeof(char*));
495 if (!PathFileExistsA(server->ConfigPath))
496 CreateDirectoryA(server->ConfigPath, 0);
498 filepath = GetCombinedPath(server->ConfigPath, "shadow");
503 if (!PathFileExistsA(filepath))
504 CreateDirectoryA(filepath, 0);
506 server->CertificateFile = GetCombinedPath(filepath, "shadow.crt");
507 server->PrivateKeyFile = GetCombinedPath(filepath, "shadow.key");
509 if ((!PathFileExistsA(server->CertificateFile)) ||
510 (!PathFileExistsA(server->PrivateKeyFile)))
512 makecert = makecert_context_new();
514 makecert_context_process(makecert, makecert_argc, (char**) makecert_argv);
516 makecert_context_set_output_file_name(makecert, "shadow");
518 if (!PathFileExistsA(server->CertificateFile))
519 makecert_context_output_certificate_file(makecert, filepath);
521 if (!PathFileExistsA(server->PrivateKeyFile))
522 makecert_context_output_private_key_file(makecert, filepath);
524 makecert_context_free(makecert);
532 int shadow_server_init_fonts(rdpShadowServer* server)
537 fontPath = GetCombinedPath(server->ConfigPath, "shadow/fonts");
539 font = shadow_font_new(fontPath, "source_serif_pro_regular_12");
548 int shadow_server_init(rdpShadowServer* server)
552 winpr_InitializeSSL(WINPR_SSL_INIT_DEFAULT);
554 WTSRegisterWtsApiFunctionTable(FreeRDP_InitWtsApi());
556 server->clients = ArrayList_New(TRUE);
558 server->StopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
560 InitializeCriticalSectionAndSpinCount(&(server->lock), 4000);
562 status = shadow_server_init_config_path(server);
567 status = shadow_server_init_certificate(server);
572 shadow_server_init_fonts(server);
574 server->listener = freerdp_listener_new();
576 if (!server->listener)
579 server->listener->info = (void*) server;
580 server->listener->PeerAccepted = shadow_client_accepted;
582 server->subsystem = shadow_subsystem_new(NULL);
584 if (!server->subsystem)
587 status = shadow_subsystem_init(server->subsystem, server);
592 int shadow_server_uninit(rdpShadowServer* server)
594 shadow_server_stop(server);
596 if (server->listener)
598 freerdp_listener_free(server->listener);
599 server->listener = NULL;
602 if (server->CertificateFile)
604 free(server->CertificateFile);
605 server->CertificateFile = NULL;
608 if (server->PrivateKeyFile)
610 free(server->PrivateKeyFile);
611 server->PrivateKeyFile = NULL;
614 if (server->ipcSocket)
616 free(server->ipcSocket);
617 server->ipcSocket = NULL;
620 shadow_subsystem_uninit(server->subsystem);
625 rdpShadowServer* shadow_server_new()
627 rdpShadowServer* server;
629 server = (rdpShadowServer*) calloc(1, sizeof(rdpShadowServer));
635 server->mayView = TRUE;
636 server->mayInteract = TRUE;
638 #ifdef WITH_SHADOW_X11
639 server->authentication = TRUE;
641 server->authentication = FALSE;
647 void shadow_server_free(rdpShadowServer* server)
652 DeleteCriticalSection(&(server->lock));
656 ArrayList_Free(server->clients);
657 server->clients = NULL;
660 shadow_subsystem_free(server->subsystem);