FreeRDP
RDPSession.m
1 /*
2  RDP Session object
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 #import "ios_freerdp.h"
12 #import "ios_freerdp_ui.h"
13 #import "ios_freerdp_events.h"
14 
15 #import "RDPSession.h"
16 #import "TSXTypes.h"
17 #import "Bookmark.h"
18 #import "ConnectionParams.h"
19 
20 NSString *TSXSessionDidDisconnectNotification = @"TSXSessionDidDisconnect";
21 NSString *TSXSessionDidFailToConnectNotification = @"TSXSessionDidFailToConnect";
22 
23 @interface RDPSession (Private)
24 - (void)runSession;
25 - (void)runSessionFinished:(NSNumber *)result;
26 - (mfInfo *)mfi;
27 
28 // The connection thread calls these on the main thread.
29 - (void)sessionWillConnect;
30 - (void)sessionDidConnect;
31 - (void)sessionDidDisconnect;
32 - (void)sessionDidFailToConnect:(int)reason;
33 - (void)sessionBitmapContextWillChange;
34 - (void)sessionBitmapContextDidChange;
35 @end
36 
37 @implementation RDPSession
38 
39 @synthesize delegate = _delegate, params = _params, toolbarVisible = _toolbar_visible,
40  uiRequestCompleted = _ui_request_completed, bookmark = _bookmark;
41 
42 + (void)initialize
43 {
44  ios_init_freerdp();
45 }
46 
47 static BOOL addArgument(int *argc, char ***argv, const char *fmt, ...)
48 {
49  va_list ap;
50  char *arg = NULL;
51  char **tmp = realloc(*argv, (*argc + 1) * sizeof(char *));
52 
53  if (!tmp)
54  return FALSE;
55 
56  *argv = tmp;
57  *argc = *argc + 1;
58  va_start(ap, fmt);
59  vasprintf(&arg, fmt, ap);
60  va_end(ap);
61  (*argv)[*argc - 1] = arg;
62  return TRUE;
63 }
64 
65 static BOOL addFlag(int *argc, char ***argv, const char *str, BOOL flag)
66 {
67  return addArgument(argc, argv, "%s%s", flag ? "+" : "-", str);
68 }
69 
70 static void freeArguments(int argc, char **argv)
71 {
72  for (int i = 0; i < argc; i++)
73  free(argv[i]);
74 
75  free(argv);
76 }
77 
78 // Designated initializer.
79 - (id)initWithBookmark:(ComputerBookmark *)bookmark
80 {
81  int status;
82  char **argv = NULL;
83  int argc = 0;
84 
85  if (!(self = [super init]))
86  return nil;
87 
88  if (!bookmark)
89  [NSException raise:NSInvalidArgumentException
90  format:@"%s: params may not be nil.", __func__];
91 
92  _bookmark = [bookmark retain];
93  _params = [[bookmark params] copy];
94  _name = [[bookmark label] retain];
95  _delegate = nil;
96  _toolbar_visible = YES;
97  _freerdp = ios_freerdp_new();
98  _ui_request_completed = [[NSCondition alloc] init];
99  BOOL connected_via_3g = ![bookmark conntectedViaWLAN];
100 
101  if (!addArgument(&argc, &argv, "iFreeRDP"))
102  goto out_free;
103 
104  if (!addArgument(&argc, &argv, "/gdi:sw"))
105  goto out_free;
106 
107  // Screen Size is set on connect (we need a valid delegate in case the user choose an automatic
108  // screen size)
109 
110  // Other simple numeric settings
111  if ([_params hasValueForKey:@"colors"])
112  if (!addArgument(&argc, &argv, "/bpp:%d",
113  [_params intForKey:@"colors" with3GEnabled:connected_via_3g]))
114  goto out_free;
115 
116  if ([_params hasValueForKey:@"port"])
117  if (!addArgument(&argc, &argv, "/port:%d", [_params intForKey:@"port"]))
118  goto out_free;
119 
120  if ([_params boolForKey:@"console"])
121  if (!addArgument(&argc, &argv, "/admin"))
122  goto out_free;
123 
124  if (!addArgument(&argc, &argv, "/v:%s", [_params UTF8StringForKey:@"hostname"]))
125  goto out_free;
126 
127  // String settings
128  if ([[_params StringForKey:@"username"] length])
129  {
130  if (!addArgument(&argc, &argv, "/u:%s", [_params UTF8StringForKey:@"username"]))
131  goto out_free;
132  }
133 
134  if ([[_params StringForKey:@"password"] length])
135  {
136  if (!addArgument(&argc, &argv, "/p:%s", [_params UTF8StringForKey:@"password"]))
137  goto out_free;
138  }
139 
140  if ([[_params StringForKey:@"domain"] length])
141  {
142  if (!addArgument(&argc, &argv, "/d:%s", [_params UTF8StringForKey:@"domain"]))
143  goto out_free;
144  }
145 
146  if ([[_params StringForKey:@"working_directory"] length])
147  {
148  if (!addArgument(&argc, &argv, "/shell-dir:%s",
149  [_params UTF8StringForKey:@"working_directory"]))
150  goto out_free;
151  }
152 
153  if ([[_params StringForKey:@"remote_program"] length])
154  {
155  if (!addArgument(&argc, &argv, "/shell:%s", [_params UTF8StringForKey:@"remote_program"]))
156  goto out_free;
157  }
158 
159  // RemoteFX
160  if ([_params boolForKey:@"perf_remotefx" with3GEnabled:connected_via_3g])
161  if (!addArgument(&argc, &argv, "/rfx"))
162  goto out_free;
163 
164  if ([_params boolForKey:@"perf_gfx" with3GEnabled:connected_via_3g])
165  if (!addArgument(&argc, &argv, "/gfx"))
166  goto out_free;
167 
168  if ([_params boolForKey:@"perf_h264" with3GEnabled:connected_via_3g])
169  if (!addArgument(&argc, &argv, "/gfx-h264"))
170  goto out_free;
171 
172  if (![_params boolForKey:@"perf_remotefx" with3GEnabled:connected_via_3g] &&
173  ![_params boolForKey:@"perf_gfx" with3GEnabled:connected_via_3g] &&
174  ![_params boolForKey:@"perf_h264" with3GEnabled:connected_via_3g])
175  if (!addArgument(&argc, &argv, "/nsc"))
176  goto out_free;
177 
178  if (!addFlag(&argc, &argv, "bitmap-cache", TRUE))
179  goto out_free;
180 
181  if (!addFlag(&argc, &argv, "wallpaper",
182  [_params boolForKey:@"perf_show_desktop" with3GEnabled:connected_via_3g]))
183  goto out_free;
184 
185  if (!addFlag(&argc, &argv, "window-drag",
186  [_params boolForKey:@"perf_window_dragging" with3GEnabled:connected_via_3g]))
187  goto out_free;
188 
189  if (!addFlag(&argc, &argv, "menu-anims",
190  [_params boolForKey:@"perf_menu_animation" with3GEnabled:connected_via_3g]))
191  goto out_free;
192 
193  if (!addFlag(&argc, &argv, "themes",
194  [_params boolForKey:@"perf_windows_themes" with3GEnabled:connected_via_3g]))
195  goto out_free;
196 
197  if (!addFlag(&argc, &argv, "fonts",
198  [_params boolForKey:@"perf_font_smoothing" with3GEnabled:connected_via_3g]))
199  goto out_free;
200 
201  if (!addFlag(&argc, &argv, "aero",
202  [_params boolForKey:@"perf_desktop_composition" with3GEnabled:connected_via_3g]))
203  goto out_free;
204 
205  if ([_params hasValueForKey:@"width"])
206  if (!addArgument(&argc, &argv, "/w:%d", [_params intForKey:@"width"]))
207  goto out_free;
208 
209  if ([_params hasValueForKey:@"height"])
210  if (!addArgument(&argc, &argv, "/h:%d", [_params intForKey:@"height"]))
211  goto out_free;
212 
213  // security
214  switch ([_params intForKey:@"security"])
215  {
216  case TSXProtocolSecurityNLA:
217  if (!addArgument(&argc, &argv, "/sec:NLA"))
218  goto out_free;
219 
220  break;
221 
222  case TSXProtocolSecurityTLS:
223  if (!addArgument(&argc, &argv, "/sec:TLS"))
224  goto out_free;
225 
226  break;
227 
228  case TSXProtocolSecurityRDP:
229  if (!addArgument(&argc, &argv, "/sec:RDP"))
230  goto out_free;
231 
232  break;
233 
234  default:
235  break;
236  }
237 
238  // ts gateway settings
239  if ([_params boolForKey:@"enable_tsg_settings"])
240  {
241  if (!addArgument(&argc, &argv, "/g:%s", [_params UTF8StringForKey:@"tsg_hostname"]))
242  goto out_free;
243 
244  if (!addArgument(&argc, &argv, "/gp:%d", [_params intForKey:@"tsg_port"]))
245  goto out_free;
246 
247  if (!addArgument(&argc, &argv, "/gu:%s", [_params intForKey:@"tsg_username"]))
248  goto out_free;
249 
250  if (!addArgument(&argc, &argv, "/gp:%s", [_params intForKey:@"tsg_password"]))
251  goto out_free;
252 
253  if (!addArgument(&argc, &argv, "/gd:%s", [_params intForKey:@"tsg_domain"]))
254  goto out_free;
255  }
256 
257  // Remote keyboard layout
258  if (!addArgument(&argc, &argv, "/kbd:%d", 0x409))
259  goto out_free;
260 
261  status =
262  freerdp_client_settings_parse_command_line(_freerdp->context->settings, argc, argv, FALSE);
263 
264  if (0 != status)
265  goto out_free;
266 
267  freeArguments(argc, argv);
268  [self mfi]->session = self;
269  return self;
270 out_free:
271  freeArguments(argc, argv);
272  [self release];
273  return nil;
274 }
275 
276 - (void)dealloc
277 {
278  [self setDelegate:nil];
279  [_bookmark release];
280  [_name release];
281  [_params release];
282  [_ui_request_completed release];
283  ios_freerdp_free(_freerdp);
284  [super dealloc];
285 }
286 
287 - (CGContextRef)bitmapContext
288 {
289  return [self mfi]->bitmap_context;
290 }
291 
292 #pragma mark -
293 #pragma mark Connecting and disconnecting
294 
295 - (void)connect
296 {
297  // Set Screen Size to automatic if widht or height are still 0
298  rdpSettings *settings = _freerdp->context->settings;
299 
300  if (freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth) == 0 ||
301  freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight) == 0)
302  {
303  CGSize size = CGSizeZero;
304 
305  if ([[self delegate] respondsToSelector:@selector(sizeForFitScreenForSession:)])
306  size = [[self delegate] sizeForFitScreenForSession:self];
307 
308  if (!CGSizeEqualToSize(CGSizeZero, size))
309  {
310  [_params setInt:size.width forKey:@"width"];
311  [_params setInt:size.height forKey:@"height"];
312  (void)freerdp_settings_set_uint32(settings, FreeRDP_DesktopWidth, size.width);
313  (void)freerdp_settings_set_uint32(settings, FreeRDP_DesktopHeight, size.height);
314  }
315  }
316 
317  // TODO: This is a hack to ensure connections to RDVH with 16bpp don't have an odd screen
318  // resolution width
319  // Otherwise this could result in screen corruption ..
320  if (freerdp_settings_get_uint32(settings, FreeRDP_ColorDepth) <= 16)
321  {
322  const UINT32 w = freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth) & (~1);
323  (void)freerdp_settings_set_uint32(settings, FreeRDP_DesktopWidth, w);
324  }
325 
326  [self performSelectorInBackground:@selector(runSession) withObject:nil];
327 }
328 
329 - (void)disconnect
330 {
331  mfInfo *mfi = [self mfi];
332  ios_events_send(mfi, [NSDictionary dictionaryWithObject:@"disconnect" forKey:@"type"]);
333 
334  if (mfi->connection_state == TSXConnectionConnecting)
335  {
336  mfi->unwanted = YES;
337  [self sessionDidDisconnect];
338  return;
339  }
340 }
341 
342 - (TSXConnectionState)connectionState
343 {
344  return [self mfi]->connection_state;
345 }
346 
347 // suspends the session
348 - (void)suspend
349 {
350  if (!_suspended)
351  {
352  _suspended = YES;
353  // instance->update->SuppressOutput(instance->context, 0, NULL);
354  }
355 }
356 
357 // resumes a previously suspended session
358 - (void)resume
359 {
360  if (_suspended)
361  {
362  /* RECTANGLE_16 rec;
363  rec.left = 0;
364  rec.top = 0;
365  rec.right = freerdp_settings_get_uint32(instance->settings, FreeRDP_DesktopWidth);
366  rec.bottom = freerdp_settings_get_uint32(instance->settings, FreeRDP_DesktopHeight);
367  */
368  _suspended = NO;
369  // instance->update->SuppressOutput(instance->context, 1, &rec);
370  // [delegate sessionScreenSettingsChanged:self];
371  }
372 }
373 
374 // returns YES if the session is started
375 - (BOOL)isSuspended
376 {
377  return _suspended;
378 }
379 
380 #pragma mark -
381 #pragma mark Input events
382 
383 - (void)sendInputEvent:(NSDictionary *)eventDescriptor
384 {
385  if ([self mfi]->connection_state == TSXConnectionConnected)
386  ios_events_send([self mfi], eventDescriptor);
387 }
388 
389 #pragma mark -
390 #pragma mark Server events (main thread)
391 
392 - (void)setNeedsDisplayInRectAsValue:(NSValue *)rect_value
393 {
394  if ([[self delegate] respondsToSelector:@selector(session:needsRedrawInRect:)])
395  [[self delegate] session:self needsRedrawInRect:[rect_value CGRectValue]];
396 }
397 
398 #pragma mark -
399 #pragma mark interface functions
400 
401 - (UIImage *)getScreenshotWithSize:(CGSize)size
402 {
403  NSAssert([self mfi]->bitmap_context != nil,
404  @"Screenshot requested while having no valid RDP drawing context");
405  CGImageRef cgImage = CGBitmapContextCreateImage([self mfi]->bitmap_context);
406  UIGraphicsBeginImageContext(size);
407  CGContextTranslateCTM(UIGraphicsGetCurrentContext(), 0, size.height);
408  CGContextScaleCTM(UIGraphicsGetCurrentContext(), 1.0, -1.0);
409  CGContextDrawImage(UIGraphicsGetCurrentContext(), CGRectMake(0, 0, size.width, size.height),
410  cgImage);
411  UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
412  UIGraphicsEndImageContext();
413  CGImageRelease(cgImage);
414  return viewImage;
415 }
416 
417 - (rdpSettings *)getSessionParams
418 {
419  return _freerdp->context->settings;
420 }
421 
422 - (NSString *)sessionName
423 {
424  return _name;
425 }
426 
427 @end
428 
429 #pragma mark -
430 @implementation RDPSession (Private)
431 
432 - (mfInfo *)mfi
433 {
434  return MFI_FROM_INSTANCE(_freerdp);
435 }
436 
437 // Blocks until rdp session finishes.
438 - (void)runSession
439 {
440  NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
441  // Run the session
442  [self performSelectorOnMainThread:@selector(sessionWillConnect)
443  withObject:nil
444  waitUntilDone:YES];
445  int result_code = ios_run_freerdp(_freerdp);
446  [self mfi]->connection_state = TSXConnectionDisconnected;
447  [self performSelectorOnMainThread:@selector(runSessionFinished:)
448  withObject:[NSNumber numberWithInt:result_code]
449  waitUntilDone:YES];
450  [pool release];
451 }
452 
453 // Main thread.
454 - (void)runSessionFinished:(NSNumber *)result
455 {
456  int result_code = [result intValue];
457 
458  switch (result_code)
459  {
460  case MF_EXIT_CONN_CANCELED:
461  [self sessionDidDisconnect];
462  break;
463 
464  case MF_EXIT_LOGON_TIMEOUT:
465  case MF_EXIT_CONN_FAILED:
466  [self sessionDidFailToConnect:result_code];
467  break;
468 
469  case MF_EXIT_SUCCESS:
470  default:
471  [self sessionDidDisconnect];
472  break;
473  }
474 }
475 
476 #pragma mark -
477 #pragma mark Session management (main thread)
478 
479 - (void)sessionWillConnect
480 {
481  if ([[self delegate] respondsToSelector:@selector(sessionWillConnect:)])
482  [[self delegate] sessionWillConnect:self];
483 }
484 
485 - (void)sessionDidConnect
486 {
487  if ([[self delegate] respondsToSelector:@selector(sessionDidConnect:)])
488  [[self delegate] sessionDidConnect:self];
489 }
490 
491 - (void)sessionDidFailToConnect:(int)reason
492 {
493  [[NSNotificationCenter defaultCenter]
494  postNotificationName:TSXSessionDidFailToConnectNotification
495  object:self];
496 
497  if ([[self delegate] respondsToSelector:@selector(session:didFailToConnect:)])
498  [[self delegate] session:self didFailToConnect:reason];
499 }
500 
501 - (void)sessionDidDisconnect
502 {
503  [[NSNotificationCenter defaultCenter] postNotificationName:TSXSessionDidDisconnectNotification
504  object:self];
505 
506  if ([[self delegate] respondsToSelector:@selector(sessionDidDisconnect:)])
507  [[self delegate] sessionDidDisconnect:self];
508 }
509 
510 - (void)sessionBitmapContextWillChange
511 {
512  if ([[self delegate] respondsToSelector:@selector(sessionBitmapContextWillChange:)])
513  [[self delegate] sessionBitmapContextWillChange:self];
514 }
515 
516 - (void)sessionBitmapContextDidChange
517 {
518  if ([[self delegate] respondsToSelector:@selector(sessionBitmapContextDidChange:)])
519  [[self delegate] sessionBitmapContextDidChange:self];
520 }
521 
522 - (void)sessionRequestsAuthenticationWithParams:(NSMutableDictionary *)params
523 {
524  if ([[self delegate] respondsToSelector:@selector(session:requestsAuthenticationWithParams:)])
525  [[self delegate] session:self requestsAuthenticationWithParams:params];
526 }
527 
528 - (void)sessionVerifyCertificateWithParams:(NSMutableDictionary *)params
529 {
530  if ([[self delegate] respondsToSelector:@selector(session:verifyCertificateWithParams:)])
531  [[self delegate] session:self verifyCertificateWithParams:params];
532 }
533 
534 @end
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_set_uint32(rdpSettings *settings, FreeRDP_Settings_Keys_UInt32 id, UINT32 param)
Sets a UINT32 settings value.