shadow: initial font rendering
[platform/upstream/freerdp.git] / server / shadow / shadow_server.c
1 /**
2  * FreeRDP: A Remote Desktop Protocol Implementation
3  *
4  * Copyright 2014 Marc-Andre Moreau <marcandre.moreau@gmail.com>
5  *
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
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
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.
17  */
18
19 #ifdef HAVE_CONFIG_H
20 #include "config.h"
21 #endif
22
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>
29
30 #include <freerdp/log.h>
31 #include <freerdp/version.h>
32
33 #include <winpr/tools/makecert.h>
34
35 #ifndef _WIN32
36 #include <sys/select.h>
37 #include <sys/signal.h>
38 #endif
39
40 #include "shadow.h"
41
42 #define TAG SERVER_TAG("shadow")
43
44 rdpShadowFont* g_Font = NULL;
45
46 static COMMAND_LINE_ARGUMENT_A shadow_args[] =
47 {
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 }
58 };
59
60 int shadow_server_print_command_line_help(int argc, char** argv)
61 {
62         char* str;
63         int length;
64         COMMAND_LINE_ARGUMENT_A* arg;
65
66         WLog_INFO(TAG, "Usage: %s [options]", argv[0]);
67         WLog_INFO(TAG, "");
68
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 '+')");
73         WLog_INFO(TAG, "");
74
75         arg = shadow_args;
76
77         do
78         {
79                 if (arg->Flags & COMMAND_LINE_VALUE_FLAG)
80                 {
81                         WLog_INFO(TAG, "    %s", "/");
82                         WLog_INFO(TAG, "%-20s", arg->Name);
83                         WLog_INFO(TAG, "\t%s", arg->Text);
84                 }
85                 else if ((arg->Flags & COMMAND_LINE_VALUE_REQUIRED) || (arg->Flags & COMMAND_LINE_VALUE_OPTIONAL))
86                 {
87                         WLog_INFO(TAG, "    %s", "/");
88
89                         if (arg->Format)
90                         {
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);
95                                 free(str);
96                         }
97                         else
98                         {
99                                 WLog_INFO(TAG, "%-20s", arg->Name);
100                         }
101
102                         WLog_INFO(TAG, "\t%s", arg->Text);
103                 }
104                 else if (arg->Flags & COMMAND_LINE_VALUE_BOOL)
105                 {
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");
110
111                         WLog_INFO(TAG, "    %s", arg->Default ? "-" : "+");
112
113                         WLog_INFO(TAG, "%-20s", str);
114                         free(str);
115
116                         WLog_INFO(TAG, "\t%s", arg->Text);
117                 }
118         }
119         while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
120
121         return 1;
122 }
123
124 int shadow_server_command_line_status_print(rdpShadowServer* server, int argc, char** argv, int status)
125 {
126         if (status == COMMAND_LINE_STATUS_PRINT_VERSION)
127         {
128                 WLog_INFO(TAG, "FreeRDP version %s (git %s)", FREERDP_VERSION_FULL, GIT_REVISION);
129                 return COMMAND_LINE_STATUS_PRINT_VERSION;
130         }
131         else if (status == COMMAND_LINE_STATUS_PRINT)
132         {
133                 return COMMAND_LINE_STATUS_PRINT;
134         }
135         else if (status < 0)
136         {
137                 shadow_server_print_command_line_help(argc, argv);
138                 return COMMAND_LINE_STATUS_PRINT_HELP;
139         }
140
141         return 1;
142 }
143
144 int shadow_server_parse_command_line(rdpShadowServer* server, int argc, char** argv)
145 {
146         int status;
147         DWORD flags;
148         COMMAND_LINE_ARGUMENT_A* arg;
149
150         if (argc < 2)
151                 return 1;
152
153         CommandLineClearArgumentsA(shadow_args);
154
155         flags = COMMAND_LINE_SEPARATOR_COLON;
156         flags |= COMMAND_LINE_SIGIL_SLASH | COMMAND_LINE_SIGIL_PLUS_MINUS;
157
158         status = CommandLineParseArgumentsA(argc, (const char**) argv, shadow_args, flags, server, NULL, NULL);
159
160         if (status < 0)
161                 return status;
162
163         arg = shadow_args;
164
165         do
166         {
167                 if (!(arg->Flags & COMMAND_LINE_ARGUMENT_PRESENT))
168                         continue;
169
170                 CommandLineSwitchStart(arg)
171
172                 CommandLineSwitchCase(arg, "port")
173                 {
174                         server->port = (DWORD) atoi(arg->Value);
175                 }
176                 CommandLineSwitchCase(arg, "ipc-socket")
177                 {
178                         server->ipcSocket = _strdup(arg->Value);
179                 }
180                 CommandLineSwitchCase(arg, "may-view")
181                 {
182                         server->mayView = arg->Value ? TRUE : FALSE;
183                 }
184                 CommandLineSwitchCase(arg, "may-interact")
185                 {
186                         server->mayInteract = arg->Value ? TRUE : FALSE;
187                 }
188                 CommandLineSwitchCase(arg, "rect")
189                 {
190                         char* p;
191                         char* tok[4];
192                         int x, y, w, h;
193                         char* str = _strdup(arg->Value);
194
195                         if (!str)
196                                 return -1;
197
198                         tok[0] = p = str;
199
200                         p = strchr(p + 1, ',');
201
202                         if (!p)
203                                 return -1;
204
205                         *p++ = '\0';
206                         tok[1] = p;
207
208                         p = strchr(p + 1, ',');
209
210                         if (!p)
211                                 return -1;
212
213                         *p++ = '\0';
214                         tok[2] = p;
215
216                         p = strchr(p + 1, ',');
217
218                         if (!p)
219                                 return -1;
220
221                         *p++ = '\0';
222                         tok[3] = p;
223
224                         x = atoi(tok[0]);
225                         y = atoi(tok[1]);
226                         w = atoi(tok[2]);
227                         h = atoi(tok[3]);
228
229                         if ((x < 0) || (y < 0) || (w < 1) || (h < 1))
230                                 return -1;
231
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;
237                 }
238                 CommandLineSwitchCase(arg, "auth")
239                 {
240                         server->authentication = arg->Value ? TRUE : FALSE;
241                 }
242                 CommandLineSwitchDefault(arg)
243                 {
244
245                 }
246
247                 CommandLineSwitchEnd(arg)
248         }
249         while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
250
251         arg = CommandLineFindArgumentA(shadow_args, "monitors");
252
253         if (arg && (arg->Flags & COMMAND_LINE_ARGUMENT_PRESENT))
254         {
255                 int index;
256                 int numMonitors;
257                 MONITOR_DEF monitors[16];
258
259                 numMonitors = shadow_enum_monitors(monitors, 16, 0);
260
261                 if (arg->Flags & COMMAND_LINE_VALUE_PRESENT)
262                 {
263                         /* Select monitors */
264
265                         index = atoi(arg->Value);
266
267                         if (index < 0)
268                                 index = 0;
269
270                         if (index >= numMonitors)
271                                 index = 0;
272
273                         server->selectedMonitor = index;
274                 }
275                 else
276                 {
277                         int width, height;
278                         MONITOR_DEF* monitor;
279
280                         /* List monitors */
281
282                         for (index = 0; index < numMonitors; index++)
283                         {
284                                 monitor = &monitors[index];
285
286                                 width = monitor->right - monitor->left;
287                                 height = monitor->bottom - monitor->top;
288
289                                 WLog_INFO(TAG, "      %s [%d] %dx%d\t+%d+%d",
290                                         (monitor->flags == 1) ? "*" : " ", index,
291                                         width, height, monitor->left, monitor->top);
292                         }
293
294                         status = COMMAND_LINE_STATUS_PRINT;
295                 }
296         }
297
298         return status;
299 }
300
301 void* shadow_server_thread(rdpShadowServer* server)
302 {
303         DWORD status;
304         DWORD nCount;
305         HANDLE events[32];
306         HANDLE StopEvent;
307         freerdp_listener* listener;
308         rdpShadowSubsystem* subsystem;
309
310         listener = server->listener;
311         StopEvent = server->StopEvent;
312         subsystem = server->subsystem;
313
314         shadow_subsystem_start(server->subsystem);
315
316         while (1)
317         {
318                 nCount = 0;
319
320                 if (listener->GetEventHandles(listener, events, &nCount) < 0)
321                 {
322                         WLog_ERR(TAG, "Failed to get FreeRDP file descriptor");
323                         break;
324                 }
325
326                 events[nCount++] = server->StopEvent;
327
328                 status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE);
329
330                 if (WaitForSingleObject(server->StopEvent, 0) == WAIT_OBJECT_0)
331                 {
332                         break;
333                 }
334
335                 if (!listener->CheckFileDescriptor(listener))
336                 {
337                         WLog_ERR(TAG, "Failed to check FreeRDP file descriptor");
338                         break;
339                 }
340
341 #ifdef _WIN32
342                 Sleep(100); /* FIXME: listener event handles */
343 #endif
344         }
345
346         listener->Close(listener);
347
348         shadow_subsystem_stop(server->subsystem);
349
350         ExitThread(0);
351
352         return NULL;
353 }
354
355 int shadow_server_start(rdpShadowServer* server)
356 {
357         BOOL status;
358         WSADATA wsaData;
359
360         if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
361                 return -1;
362
363 #ifndef _WIN32
364         signal(SIGPIPE, SIG_IGN);
365 #endif
366
367         server->screen = shadow_screen_new(server);
368
369         if (!server->screen)
370                 return -1;
371
372         server->capture = shadow_capture_new(server);
373
374         if (!server->capture)
375                 return -1;
376
377         if (!server->ipcSocket)
378                 status = server->listener->Open(server->listener, NULL, (UINT16) server->port);
379         else
380                 status = server->listener->OpenLocal(server->listener, server->ipcSocket);
381
382         if (status)
383         {
384                 server->thread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)
385                                 shadow_server_thread, (void*) server, 0, NULL);
386         }
387
388         return 0;
389 }
390
391 int shadow_server_stop(rdpShadowServer* server)
392 {
393         if (server->thread)
394         {
395                 SetEvent(server->StopEvent);
396                 WaitForSingleObject(server->thread, INFINITE);
397                 CloseHandle(server->thread);
398                 server->thread = NULL;
399
400                 server->listener->Close(server->listener);
401         }
402
403         if (server->screen)
404         {
405                 shadow_screen_free(server->screen);
406                 server->screen = NULL;
407         }
408
409         if (server->capture)
410         {
411                 shadow_capture_free(server->capture);
412                 server->capture = NULL;
413         }
414
415         return 0;
416 }
417
418 int shadow_server_init_config_path(rdpShadowServer* server)
419 {
420 #ifdef _WIN32
421         if (!server->ConfigPath)
422         {
423                 server->ConfigPath = GetEnvironmentSubPath("LOCALAPPDATA", "freerdp");
424         }
425 #endif
426
427 #ifdef __APPLE__
428         if (!server->ConfigPath)
429         {
430                 char* userLibraryPath;
431                 char* userApplicationSupportPath;
432
433                 userLibraryPath = GetKnownSubPath(KNOWN_PATH_HOME, "Library");
434
435                 if (userLibraryPath)
436                 {
437                         if (!PathFileExistsA(userLibraryPath))
438                                 CreateDirectoryA(userLibraryPath, 0);
439
440                         userApplicationSupportPath = GetCombinedPath(userLibraryPath, "Application Support");
441
442                         if (userApplicationSupportPath)
443                         {
444                                 if (!PathFileExistsA(userApplicationSupportPath))
445                                         CreateDirectoryA(userApplicationSupportPath, 0);
446
447                                 server->ConfigPath = GetCombinedPath(userApplicationSupportPath, "freerdp");
448                         }
449
450                         free(userLibraryPath);
451                         free(userApplicationSupportPath);
452                 }
453         }
454 #endif
455
456         if (!server->ConfigPath)
457         {
458                 char* configHome;
459
460                 configHome = GetKnownPath(KNOWN_PATH_XDG_CONFIG_HOME);
461
462                 if (configHome)
463                 {
464                         if (!PathFileExistsA(configHome))
465                                 CreateDirectoryA(configHome, 0);
466
467                         server->ConfigPath = GetKnownSubPath(KNOWN_PATH_XDG_CONFIG_HOME, "freerdp");
468
469                         free(configHome);
470                 }
471         }
472
473         if (!server->ConfigPath)
474                 return -1; /* no usable config path */
475
476         return 1;
477 }
478
479 int shadow_server_init_certificate(rdpShadowServer* server)
480 {
481         char* filepath;
482         MAKECERT_CONTEXT* makecert;
483
484         const char* makecert_argv[6] =
485         {
486                 "makecert",
487                 "-rdp",
488                 "-live",
489                 "-silent",
490                 "-y", "5"
491         };
492
493         int makecert_argc = (sizeof(makecert_argv) / sizeof(char*));
494
495         if (!PathFileExistsA(server->ConfigPath))
496                 CreateDirectoryA(server->ConfigPath, 0);
497
498         filepath = GetCombinedPath(server->ConfigPath, "shadow");
499
500         if (!filepath)
501                 return -1;
502
503         if (!PathFileExistsA(filepath))
504                 CreateDirectoryA(filepath, 0);
505
506         server->CertificateFile = GetCombinedPath(filepath, "shadow.crt");
507         server->PrivateKeyFile = GetCombinedPath(filepath, "shadow.key");
508
509         if ((!PathFileExistsA(server->CertificateFile)) ||
510                         (!PathFileExistsA(server->PrivateKeyFile)))
511         {
512                 makecert = makecert_context_new();
513
514                 makecert_context_process(makecert, makecert_argc, (char**) makecert_argv);
515
516                 makecert_context_set_output_file_name(makecert, "shadow");
517
518                 if (!PathFileExistsA(server->CertificateFile))
519                         makecert_context_output_certificate_file(makecert, filepath);
520
521                 if (!PathFileExistsA(server->PrivateKeyFile))
522                         makecert_context_output_private_key_file(makecert, filepath);
523
524                 makecert_context_free(makecert);
525         }
526
527         free(filepath);
528
529         return 1;
530 }
531
532 int shadow_server_init_fonts(rdpShadowServer* server)
533 {
534         char* fontPath;
535         rdpShadowFont* font;
536
537         fontPath = GetCombinedPath(server->ConfigPath, "shadow/fonts");
538
539         font = shadow_font_new(fontPath, "source_serif_pro_regular_12");
540
541         g_Font = font;
542
543         free(fontPath);
544
545         return 1;
546 }
547
548 int shadow_server_init(rdpShadowServer* server)
549 {
550         int status;
551
552         winpr_InitializeSSL(WINPR_SSL_INIT_DEFAULT);
553
554         WTSRegisterWtsApiFunctionTable(FreeRDP_InitWtsApi());
555
556         server->clients = ArrayList_New(TRUE);
557
558         server->StopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
559
560         InitializeCriticalSectionAndSpinCount(&(server->lock), 4000);
561
562         status = shadow_server_init_config_path(server);
563
564         if (status < 0)
565                 return -1;
566
567         status = shadow_server_init_certificate(server);
568
569         if (status < 0)
570                 return -1;
571
572         shadow_server_init_fonts(server);
573
574         server->listener = freerdp_listener_new();
575
576         if (!server->listener)
577                 return -1;
578
579         server->listener->info = (void*) server;
580         server->listener->PeerAccepted = shadow_client_accepted;
581
582         server->subsystem = shadow_subsystem_new(NULL);
583
584         if (!server->subsystem)
585                 return -1;
586
587         status = shadow_subsystem_init(server->subsystem, server);
588
589         return status;
590 }
591
592 int shadow_server_uninit(rdpShadowServer* server)
593 {
594         shadow_server_stop(server);
595
596         if (server->listener)
597         {
598                 freerdp_listener_free(server->listener);
599                 server->listener = NULL;
600         }
601
602         if (server->CertificateFile)
603         {
604                 free(server->CertificateFile);
605                 server->CertificateFile = NULL;
606         }
607         
608         if (server->PrivateKeyFile)
609         {
610                 free(server->PrivateKeyFile);
611                 server->PrivateKeyFile = NULL;
612         }
613
614         if (server->ipcSocket)
615         {
616                 free(server->ipcSocket);
617                 server->ipcSocket = NULL;
618         }
619
620         shadow_subsystem_uninit(server->subsystem);
621
622         return 1;
623 }
624
625 rdpShadowServer* shadow_server_new()
626 {
627         rdpShadowServer* server;
628
629         server = (rdpShadowServer*) calloc(1, sizeof(rdpShadowServer));
630
631         if (!server)
632                 return NULL;
633
634         server->port = 3389;
635         server->mayView = TRUE;
636         server->mayInteract = TRUE;
637
638 #ifdef WITH_SHADOW_X11
639         server->authentication = TRUE;
640 #else
641         server->authentication = FALSE;
642 #endif
643
644         return server;
645 }
646
647 void shadow_server_free(rdpShadowServer* server)
648 {
649         if (!server)
650                 return;
651
652         DeleteCriticalSection(&(server->lock));
653
654         if (server->clients)
655         {
656                 ArrayList_Free(server->clients);
657                 server->clients = NULL;
658         }
659
660         shadow_subsystem_free(server->subsystem);
661
662         free(server);
663 }
664