client/common: parse and use remote assistance file
[platform/upstream/freerdp.git] / channels / remdesk / client / remdesk_main.c
1 /**
2  * FreeRDP: A Remote Desktop Protocol Implementation
3  * Remote Assistance Virtual Channel
4  *
5  * Copyright 2014 Marc-Andre Moreau <marcandre.moreau@gmail.com>
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *     http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23
24 #include <winpr/crt.h>
25 #include <winpr/print.h>
26
27 #include <freerdp/client/remdesk.h>
28
29 #include "remdesk_main.h"
30
31 RemdeskClientContext* remdesk_get_client_interface(remdeskPlugin* remdesk)
32 {
33         RemdeskClientContext* pInterface;
34         pInterface = (RemdeskClientContext*) remdesk->channelEntryPoints.pInterface;
35         return pInterface;
36 }
37
38 static int remdesk_process_receive(remdeskPlugin* remdesk, wStream* s)
39 {
40         int status = 1;
41
42         printf("RemdeskReceive: %d\n", Stream_GetRemainingLength(s));
43         winpr_HexDump(Stream_Pointer(s), Stream_GetRemainingLength(s));
44
45         return status;
46 }
47
48 static void remdesk_process_connect(remdeskPlugin* remdesk)
49 {
50         printf("RemdeskProcessConnect\n");
51 }
52
53 /****************************************************************************************/
54
55 static wListDictionary* g_InitHandles;
56 static wListDictionary* g_OpenHandles;
57
58 void remdesk_add_init_handle_data(void* pInitHandle, void* pUserData)
59 {
60         if (!g_InitHandles)
61                 g_InitHandles = ListDictionary_New(TRUE);
62
63         ListDictionary_Add(g_InitHandles, pInitHandle, pUserData);
64 }
65
66 void* remdesk_get_init_handle_data(void* pInitHandle)
67 {
68         void* pUserData = NULL;
69         pUserData = ListDictionary_GetItemValue(g_InitHandles, pInitHandle);
70         return pUserData;
71 }
72
73 void remdesk_remove_init_handle_data(void* pInitHandle)
74 {
75         ListDictionary_Remove(g_InitHandles, pInitHandle);
76 }
77
78 void remdesk_add_open_handle_data(DWORD openHandle, void* pUserData)
79 {
80         void* pOpenHandle = (void*) (size_t) openHandle;
81
82         if (!g_OpenHandles)
83                 g_OpenHandles = ListDictionary_New(TRUE);
84
85         ListDictionary_Add(g_OpenHandles, pOpenHandle, pUserData);
86 }
87
88 void* remdesk_get_open_handle_data(DWORD openHandle)
89 {
90         void* pUserData = NULL;
91         void* pOpenHandle = (void*) (size_t) openHandle;
92         pUserData = ListDictionary_GetItemValue(g_OpenHandles, pOpenHandle);
93         return pUserData;
94 }
95
96 void remdesk_remove_open_handle_data(DWORD openHandle)
97 {
98         void* pOpenHandle = (void*) (size_t) openHandle;
99         ListDictionary_Remove(g_OpenHandles, pOpenHandle);
100 }
101
102 int remdesk_send(remdeskPlugin* remdesk, wStream* s)
103 {
104         UINT32 status = 0;
105         remdeskPlugin* plugin = (remdeskPlugin*) remdesk;
106
107         if (!plugin)
108         {
109                 status = CHANNEL_RC_BAD_INIT_HANDLE;
110         }
111         else
112         {
113                 status = plugin->channelEntryPoints.pVirtualChannelWrite(plugin->OpenHandle,
114                         Stream_Buffer(s), (UINT32) Stream_GetPosition(s), s);
115         }
116
117         if (status != CHANNEL_RC_OK)
118         {
119                 Stream_Free(s, TRUE);
120                 fprintf(stderr, "remdesk_send: VirtualChannelWrite failed %d\n", status);
121         }
122
123         return status;
124 }
125
126 static void remdesk_virtual_channel_event_data_received(remdeskPlugin* remdesk,
127                 void* pData, UINT32 dataLength, UINT32 totalLength, UINT32 dataFlags)
128 {
129         wStream* data_in;
130
131         if ((dataFlags & CHANNEL_FLAG_SUSPEND) || (dataFlags & CHANNEL_FLAG_RESUME))
132         {
133                 return;
134         }
135
136         if (dataFlags & CHANNEL_FLAG_FIRST)
137         {
138                 if (remdesk->data_in)
139                         Stream_Free(remdesk->data_in, TRUE);
140
141                 remdesk->data_in = Stream_New(NULL, totalLength);
142         }
143
144         data_in = remdesk->data_in;
145         Stream_EnsureRemainingCapacity(data_in, (int) dataLength);
146         Stream_Write(data_in, pData, dataLength);
147
148         if (dataFlags & CHANNEL_FLAG_LAST)
149         {
150                 if (Stream_Capacity(data_in) != Stream_GetPosition(data_in))
151                 {
152                         fprintf(stderr, "remdesk_plugin_process_received: read error\n");
153                 }
154
155                 remdesk->data_in = NULL;
156                 Stream_SealLength(data_in);
157                 Stream_SetPosition(data_in, 0);
158
159                 MessageQueue_Post(remdesk->MsgPipe->In, NULL, 0, (void*) data_in, NULL);
160         }
161 }
162
163 static VOID VCAPITYPE remdesk_virtual_channel_open_event(DWORD openHandle, UINT event,
164                 LPVOID pData, UINT32 dataLength, UINT32 totalLength, UINT32 dataFlags)
165 {
166         remdeskPlugin* remdesk;
167
168         remdesk = (remdeskPlugin*) remdesk_get_open_handle_data(openHandle);
169
170         if (!remdesk)
171         {
172                 fprintf(stderr, "remdesk_virtual_channel_open_event: error no match\n");
173                 return;
174         }
175
176         switch (event)
177         {
178                 case CHANNEL_EVENT_DATA_RECEIVED:
179                         remdesk_virtual_channel_event_data_received(remdesk, pData, dataLength, totalLength, dataFlags);
180                         break;
181
182                 case CHANNEL_EVENT_WRITE_COMPLETE:
183                         Stream_Free((wStream*) pData, TRUE);
184                         break;
185
186                 case CHANNEL_EVENT_USER:
187                         break;
188         }
189 }
190
191 static void* remdesk_virtual_channel_client_thread(void* arg)
192 {
193         wStream* data;
194         wMessage message;
195         remdeskPlugin* remdesk = (remdeskPlugin*) arg;
196
197         remdesk_process_connect(remdesk);
198
199         while (1)
200         {
201                 if (!MessageQueue_Wait(remdesk->MsgPipe->In))
202                         break;
203
204                 if (MessageQueue_Peek(remdesk->MsgPipe->In, &message, TRUE))
205                 {
206                         if (message.id == WMQ_QUIT)
207                                 break;
208
209                         if (message.id == 0)
210                         {
211                                 data = (wStream*) message.wParam;
212                                 remdesk_process_receive(remdesk, data);
213                         }
214                 }
215         }
216
217         ExitThread(0);
218         return NULL;
219 }
220
221 static void remdesk_virtual_channel_event_connected(remdeskPlugin* remdesk, LPVOID pData, UINT32 dataLength)
222 {
223         UINT32 status;
224
225         status = remdesk->channelEntryPoints.pVirtualChannelOpen(remdesk->InitHandle,
226                 &remdesk->OpenHandle, remdesk->channelDef.name, remdesk_virtual_channel_open_event);
227
228         remdesk_add_open_handle_data(remdesk->OpenHandle, remdesk);
229
230         if (status != CHANNEL_RC_OK)
231         {
232                 fprintf(stderr, "remdesk_virtual_channel_event_connected: open failed: status: %d\n", status);
233                 return;
234         }
235
236         remdesk->MsgPipe = MessagePipe_New();
237
238         remdesk->thread = CreateThread(NULL, 0,
239                         (LPTHREAD_START_ROUTINE) remdesk_virtual_channel_client_thread, (void*) remdesk, 0, NULL);
240 }
241
242 static void remdesk_virtual_channel_event_terminated(remdeskPlugin* remdesk)
243 {
244         MessagePipe_PostQuit(remdesk->MsgPipe, 0);
245         WaitForSingleObject(remdesk->thread, INFINITE);
246
247         MessagePipe_Free(remdesk->MsgPipe);
248         CloseHandle(remdesk->thread);
249
250         remdesk->channelEntryPoints.pVirtualChannelClose(remdesk->OpenHandle);
251
252         if (remdesk->data_in)
253         {
254                 Stream_Free(remdesk->data_in, TRUE);
255                 remdesk->data_in = NULL;
256         }
257
258         remdesk_remove_open_handle_data(remdesk->OpenHandle);
259         remdesk_remove_init_handle_data(remdesk->InitHandle);
260 }
261
262 static VOID VCAPITYPE remdesk_virtual_channel_init_event(LPVOID pInitHandle, UINT event, LPVOID pData, UINT dataLength)
263 {
264         remdeskPlugin* remdesk;
265
266         remdesk = (remdeskPlugin*) remdesk_get_init_handle_data(pInitHandle);
267
268         if (!remdesk)
269         {
270                 fprintf(stderr, "remdesk_virtual_channel_init_event: error no match\n");
271                 return;
272         }
273
274         switch (event)
275         {
276                 case CHANNEL_EVENT_CONNECTED:
277                         remdesk_virtual_channel_event_connected(remdesk, pData, dataLength);
278                         break;
279
280                 case CHANNEL_EVENT_DISCONNECTED:
281                         break;
282
283                 case CHANNEL_EVENT_TERMINATED:
284                         remdesk_virtual_channel_event_terminated(remdesk);
285                         break;
286         }
287 }
288
289 /* remdesk is always built-in */
290 #define VirtualChannelEntry     remdesk_VirtualChannelEntry
291
292 BOOL VCAPITYPE VirtualChannelEntry(PCHANNEL_ENTRY_POINTS pEntryPoints)
293 {
294         remdeskPlugin* remdesk;
295         RemdeskClientContext* context;
296         CHANNEL_ENTRY_POINTS_FREERDP* pEntryPointsEx;
297
298         remdesk = (remdeskPlugin*) calloc(1, sizeof(remdeskPlugin));
299
300         remdesk->channelDef.options =
301                         CHANNEL_OPTION_INITIALIZED |
302                         CHANNEL_OPTION_ENCRYPT_RDP |
303                         CHANNEL_OPTION_COMPRESS_RDP |
304                         CHANNEL_OPTION_SHOW_PROTOCOL;
305
306         strcpy(remdesk->channelDef.name, "remdesk");
307
308         pEntryPointsEx = (CHANNEL_ENTRY_POINTS_FREERDP*) pEntryPoints;
309
310         if ((pEntryPointsEx->cbSize >= sizeof(CHANNEL_ENTRY_POINTS_FREERDP)) &&
311                         (pEntryPointsEx->MagicNumber == FREERDP_CHANNEL_MAGIC_NUMBER))
312         {
313                 context = (RemdeskClientContext*) calloc(1, sizeof(RemdeskClientContext));
314
315                 context->handle = (void*) remdesk;
316
317                 *(pEntryPointsEx->ppInterface) = (void*) context;
318         }
319
320         CopyMemory(&(remdesk->channelEntryPoints), pEntryPoints, sizeof(CHANNEL_ENTRY_POINTS_FREERDP));
321
322         remdesk->channelEntryPoints.pVirtualChannelInit(&remdesk->InitHandle,
323                 &remdesk->channelDef, 1, VIRTUAL_CHANNEL_VERSION_WIN2000, remdesk_virtual_channel_init_event);
324
325         remdesk_add_init_handle_data(remdesk->InitHandle, (void*) remdesk);
326
327         return 1;
328 }