FreeRDP
ios_freerdp.m
1 /*
2  RDP run-loop
3 
4  Copyright 2013 Thincast Technologies GmbH, Authors: Martin Fleisz, Dorian Johnson
5 
6  This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
7  If a copy of the MPL was not distributed with this file, You can obtain one at
8  http://mozilla.org/MPL/2.0/.
9  */
10 
11 #include <winpr/assert.h>
12 #import <winpr/clipboard.h>
13 
14 #import <freerdp/gdi/gdi.h>
15 #import <freerdp/channels/channels.h>
16 #import <freerdp/client/channels.h>
17 #import <freerdp/client/cmdline.h>
18 #import <freerdp/freerdp.h>
19 #import <freerdp/gdi/gfx.h>
20 #import <freerdp/client/cliprdr.h>
21 
22 #import "ios_freerdp.h"
23 #import "ios_freerdp_ui.h"
24 #import "ios_freerdp_events.h"
25 #import "ios_cliprdr.h"
26 
27 #import "RDPSession.h"
28 #import "Utils.h"
29 
30 #include <errno.h>
31 
32 #define TAG FREERDP_TAG("iOS")
33 
34 #pragma mark Connection helpers
35 
36 static void ios_OnChannelConnectedEventHandler(void *context, const ChannelConnectedEventArgs *e)
37 {
38  WLog_INFO(TAG, "ios_OnChannelConnectedEventHandler, channel %s", e->name);
39  rdpSettings *settings;
40  mfContext *afc;
41 
42  if (!context || !e)
43  {
44  WLog_FATAL(TAG, "(context=%p, EventArgs=%p", context, (void *)e);
45  return;
46  }
47 
48  afc = (mfContext *)context;
49  settings = afc->_p.settings;
50 
51  if (strcmp(e->name, RDPGFX_DVC_CHANNEL_NAME) == 0)
52  {
53  if (freerdp_settings_get_bool(settings, FreeRDP_SoftwareGdi))
54  {
55  gdi_graphics_pipeline_init(afc->_p.gdi, (RdpgfxClientContext *)e->pInterface);
56  }
57  else
58  {
59  WLog_WARN(TAG, "GFX without software GDI requested. "
60  " This is not supported, add /gdi:sw");
61  }
62  }
63  else if (strcmp(e->name, CLIPRDR_SVC_CHANNEL_NAME) == 0)
64  {
65  ios_cliprdr_init(afc, (CliprdrClientContext *)e->pInterface);
66  }
67 }
68 
69 static void ios_OnChannelDisconnectedEventHandler(void *context,
70  const ChannelDisconnectedEventArgs *e)
71 {
72  WLog_INFO(TAG, "ios_OnChannelConnectedEventHandler, channel %s", e->name);
73  rdpSettings *settings;
74  mfContext *afc;
75 
76  if (!context || !e)
77  {
78  WLog_FATAL(TAG, "(context=%p, EventArgs=%p", context, (void *)e);
79  return;
80  }
81 
82  afc = (mfContext *)context;
83  settings = afc->_p.settings;
84 
85  if (strcmp(e->name, RDPGFX_DVC_CHANNEL_NAME) == 0)
86  {
87  if (freerdp_settings_get_bool(settings, FreeRDP_SoftwareGdi))
88  {
89  gdi_graphics_pipeline_uninit(afc->_p.gdi, (RdpgfxClientContext *)e->pInterface);
90  }
91  else
92  {
93  WLog_WARN(TAG, "GFX without software GDI requested. "
94  " This is not supported, add /gdi:sw");
95  }
96  }
97  else if (strcmp(e->name, CLIPRDR_SVC_CHANNEL_NAME) == 0)
98  {
99  ios_cliprdr_uninit(afc, (CliprdrClientContext *)e->pInterface);
100  }
101 }
102 
103 static BOOL ios_pre_connect(freerdp *instance)
104 {
105  int rc;
106  rdpSettings *settings;
107 
108  if (!instance || !instance->context)
109  return FALSE;
110 
111  settings = instance->context->settings;
112  WINPR_ASSERT(settings);
113 
114  const char *Password = freerdp_settings_get_string(settings, FreeRDP_Password);
115  if (!freerdp_settings_set_bool(settings, FreeRDP_AutoLogonEnabled,
116  Password && (Password && (strlen(Password) > 0))))
117  return FALSE;
118 
119  // Verify screen width/height are sane
120  if ((freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth) < 64) ||
121  (freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight) < 64) ||
122  (freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth) > 4096) ||
123  (freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight) > 4096))
124  {
125  NSLog(@"%s: invalid dimensions %d %d", __func__,
126  freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth),
127  freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight));
128  return FALSE;
129  }
130 
131  rc = PubSub_SubscribeChannelConnected(instance->context->pubSub,
132  ios_OnChannelConnectedEventHandler);
133 
134  if (rc != CHANNEL_RC_OK)
135  {
136  WLog_ERR(TAG, "Could not subscribe to connect event handler [%l08X]", rc);
137  return FALSE;
138  }
139 
140  rc = PubSub_SubscribeChannelDisconnected(instance->context->pubSub,
141  ios_OnChannelDisconnectedEventHandler);
142 
143  if (rc != CHANNEL_RC_OK)
144  {
145  WLog_ERR(TAG, "Could not subscribe to disconnect event handler [%l08X]", rc);
146  return FALSE;
147  }
148 
149  if (!freerdp_client_load_addins(instance->context->channels, settings))
150  {
151  WLog_ERR(TAG, "Failed to load addins [%l08X]", GetLastError());
152  return FALSE;
153  }
154 
155  return TRUE;
156 }
157 
158 static BOOL ios_Pointer_New(rdpContext *context, rdpPointer *pointer)
159 {
160  if (!context || !pointer || !context->gdi)
161  return FALSE;
162 
163  return TRUE;
164 }
165 
166 static void ios_Pointer_Free(rdpContext *context, rdpPointer *pointer)
167 {
168  if (!context || !pointer)
169  return;
170 }
171 
172 static BOOL ios_Pointer_Set(rdpContext *context, rdpPointer *pointer)
173 {
174  if (!context)
175  return FALSE;
176 
177  return TRUE;
178 }
179 
180 static BOOL ios_Pointer_SetPosition(rdpContext *context, UINT32 x, UINT32 y)
181 {
182  if (!context)
183  return FALSE;
184 
185  return TRUE;
186 }
187 
188 static BOOL ios_Pointer_SetNull(rdpContext *context)
189 {
190  if (!context)
191  return FALSE;
192 
193  return TRUE;
194 }
195 
196 static BOOL ios_Pointer_SetDefault(rdpContext *context)
197 {
198  if (!context)
199  return FALSE;
200 
201  return TRUE;
202 }
203 
204 static BOOL ios_register_pointer(rdpGraphics *graphics)
205 {
206  rdpPointer pointer = { 0 };
207 
208  if (!graphics)
209  return FALSE;
210 
211  pointer.size = sizeof(pointer);
212  pointer.New = ios_Pointer_New;
213  pointer.Free = ios_Pointer_Free;
214  pointer.Set = ios_Pointer_Set;
215  pointer.SetNull = ios_Pointer_SetNull;
216  pointer.SetDefault = ios_Pointer_SetDefault;
217  pointer.SetPosition = ios_Pointer_SetPosition;
218  graphics_register_pointer(graphics, &pointer);
219  return TRUE;
220 }
221 
222 static BOOL ios_post_connect(freerdp *instance)
223 {
224  mfInfo *mfi;
225 
226  if (!instance)
227  return FALSE;
228 
229  mfi = MFI_FROM_INSTANCE(instance);
230 
231  if (!mfi)
232  return FALSE;
233 
234  if (!gdi_init(instance, PIXEL_FORMAT_BGRA32))
235  return FALSE;
236 
237  if (!ios_register_pointer(instance->context->graphics))
238  return FALSE;
239 
240  ios_allocate_display_buffer(mfi);
241  instance->context->update->BeginPaint = ios_ui_begin_paint;
242  instance->context->update->EndPaint = ios_ui_end_paint;
243  instance->context->update->DesktopResize = ios_ui_resize_window;
244  [mfi->session performSelectorOnMainThread:@selector(sessionDidConnect)
245  withObject:nil
246  waitUntilDone:YES];
247  return TRUE;
248 }
249 
250 static void ios_post_disconnect(freerdp *instance)
251 {
252  gdi_free(instance);
253 }
254 
255 #pragma mark -
256 #pragma mark Running the connection
257 
258 int ios_run_freerdp(freerdp *instance)
259 {
260  mfContext *context = (mfContext *)instance->context;
261  mfInfo *mfi = context->mfi;
262  rdpChannels *channels = instance->context->channels;
263  mfi->connection_state = TSXConnectionConnecting;
264 
265  if (!freerdp_connect(instance))
266  {
267  NSLog(@"%s: inst->rdp_connect failed", __func__);
268  return mfi->unwanted ? MF_EXIT_CONN_CANCELED : MF_EXIT_CONN_FAILED;
269  }
270 
271  if (mfi->unwanted)
272  return MF_EXIT_CONN_CANCELED;
273 
274  mfi->connection_state = TSXConnectionConnected;
275  // Connection main loop
276  NSAutoreleasePool *pool;
277 
278  while (!freerdp_shall_disconnect_context(instance->context))
279  {
280  DWORD status;
281  DWORD nCount = 0;
282  HANDLE handles[MAXIMUM_WAIT_OBJECTS] = { 0 };
283  pool = [[NSAutoreleasePool alloc] init];
284 
285  nCount = freerdp_get_event_handles(instance->context, handles, ARRAYSIZE(handles));
286  if (nCount == 0)
287  {
288  NSLog(@"%s: freerdp_get_event_handles failed", __func__);
289  break;
290  }
291 
292  handles[nCount++] = ios_events_get_handle(mfi);
293 
294  status = WaitForMultipleObjects(nCount, handles, FALSE, INFINITE);
295 
296  if (WAIT_FAILED == status)
297  {
298  NSLog(@"%s: WaitForMultipleObjects failed!", __func__);
299  break;
300  }
301 
302  // Check the libfreerdp fds
303  if (!freerdp_check_event_handles(instance->context))
304  {
305  NSLog(@"%s: freerdp_check_event_handles failed.", __func__);
306  break;
307  }
308 
309  // Check input event fds
310  if (ios_events_check_handle(mfi) != TRUE)
311  {
312  // This event will fail when the app asks for a disconnect.
313  // NSLog(@"%s: ios_events_check_fds failed: terminating connection.", __func__);
314  break;
315  }
316 
317  [pool release];
318  pool = nil;
319  }
320 
321  CGContextRelease(mfi->bitmap_context);
322  mfi->bitmap_context = NULL;
323  mfi->connection_state = TSXConnectionDisconnected;
324  // Cleanup
325  freerdp_disconnect(instance);
326  gdi_free(instance);
327  cache_free(instance->context->cache);
328  [pool release];
329  pool = nil;
330  return MF_EXIT_SUCCESS;
331 }
332 
333 #pragma mark -
334 #pragma mark Context callbacks
335 
336 static BOOL ios_client_new(freerdp *instance, rdpContext *context)
337 {
338  mfContext *ctx = (mfContext *)context;
339 
340  if (!instance || !context)
341  return FALSE;
342 
343  if ((ctx->mfi = calloc(1, sizeof(mfInfo))) == NULL)
344  return FALSE;
345 
346  ctx->mfi->context = (mfContext *)context;
347  ctx->mfi->_context = context;
348  ctx->mfi->instance = instance;
349 
350  if (!ios_events_create_pipe(ctx->mfi))
351  return FALSE;
352 
353  instance->PreConnect = ios_pre_connect;
354  instance->PostConnect = ios_post_connect;
355  instance->PostDisconnect = ios_post_disconnect;
356  instance->Authenticate = ios_ui_authenticate;
357  instance->GatewayAuthenticate = ios_ui_gw_authenticate;
358  instance->VerifyCertificateEx = ios_ui_verify_certificate_ex;
359  instance->VerifyChangedCertificateEx = ios_ui_verify_changed_certificate_ex;
360  instance->LogonErrorInfo = NULL;
361  return TRUE;
362 }
363 
364 static void ios_client_free(freerdp *instance, rdpContext *context)
365 {
366  mfInfo *mfi;
367 
368  if (!context)
369  return;
370 
371  mfi = ((mfContext *)context)->mfi;
372  ios_events_free_pipe(mfi);
373  free(mfi);
374 }
375 
376 static int RdpClientEntry(RDP_CLIENT_ENTRY_POINTS *pEntryPoints)
377 {
378  WINPR_ASSERT(pEntryPoints);
379 
380  ZeroMemory(pEntryPoints, sizeof(RDP_CLIENT_ENTRY_POINTS));
381  pEntryPoints->Version = RDP_CLIENT_INTERFACE_VERSION;
382  pEntryPoints->Size = sizeof(RDP_CLIENT_ENTRY_POINTS_V1);
383  pEntryPoints->GlobalInit = NULL;
384  pEntryPoints->GlobalUninit = NULL;
385  pEntryPoints->ContextSize = sizeof(mfContext);
386  pEntryPoints->ClientNew = ios_client_new;
387  pEntryPoints->ClientFree = ios_client_free;
388  pEntryPoints->ClientStart = NULL;
389  pEntryPoints->ClientStop = NULL;
390  return 0;
391 }
392 
393 #pragma mark -
394 #pragma mark Initialization and cleanup
395 
396 freerdp *ios_freerdp_new()
397 {
398  rdpContext *context;
399  RDP_CLIENT_ENTRY_POINTS clientEntryPoints;
400  RdpClientEntry(&clientEntryPoints);
401  context = freerdp_client_context_new(&clientEntryPoints);
402 
403  if (!context)
404  return NULL;
405 
406  return context->instance;
407 }
408 
409 void ios_freerdp_free(freerdp *instance)
410 {
411  if (!instance || !instance->context)
412  return;
413 
414  freerdp_client_context_free(instance->context);
415 }
416 
417 void ios_init_freerdp()
418 {
419  signal(SIGPIPE, SIG_IGN);
420 }
421 
422 void ios_uninit_freerdp()
423 {
424 }
425 
426 /* compatibilty functions */
427 size_t fwrite$UNIX2003(const void *ptr, size_t size, size_t nmemb, FILE *stream)
428 {
429  return fwrite(ptr, size, nmemb, stream);
430 }
431 
432 void ios_send_clipboard_data(void *context, const void *data, UINT32 size)
433 {
434  mfContext *afc = (mfContext *)context;
435  ClipboardLock(afc->clipboard);
436  UINT32 formatId = ClipboardRegisterFormat(afc->clipboard, "UTF8_STRING");
437  if (size)
438  ClipboardSetData(afc->clipboard, formatId, data, size);
439  else
440  ClipboardEmpty(afc->clipboard);
441  ClipboardUnlock(afc->clipboard);
442  ios_cliprdr_send_client_format_list(afc->cliprdr);
443 }
FREERDP_API UINT32 freerdp_settings_get_uint32(const rdpSettings *settings, FreeRDP_Settings_Keys_UInt32 id)
Returns a UINT32 settings value.
FREERDP_API BOOL freerdp_settings_get_bool(const rdpSettings *settings, FreeRDP_Settings_Keys_Bool id)
Returns a boolean settings value.
FREERDP_API const char * freerdp_settings_get_string(const rdpSettings *settings, FreeRDP_Settings_Keys_String id)
Returns a immutable string settings value.
FREERDP_API BOOL freerdp_settings_set_bool(rdpSettings *settings, FreeRDP_Settings_Keys_Bool id, BOOL param)
Sets a BOOL settings value.