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