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)sessionBitmapContextWillChange;
35- (void)sessionBitmapContextDidChange;
36@end
37
38@implementation RDPSession
39
40@synthesize delegate = _delegate, params = _params, toolbarVisible = _toolbar_visible,
41 uiRequestCompleted = _ui_request_completed, bookmark = _bookmark;
42
43+ (void)initialize
44{
45 ios_init_freerdp();
46}
47
48static BOOL addArgument(int *argc, char ***argv, const char *fmt, ...)
49{
50 va_list ap = WINPR_C_ARRAY_INIT;
51 char *arg = nullptr;
52 char **tmp = realloc(*argv, (*argc + 1) * sizeof(char *));
53
54 if (!tmp)
55 return FALSE;
56
57 *argv = tmp;
58 *argc = *argc + 1;
59 va_start(ap, fmt);
60 const BOOL rc = vasprintf(&arg, fmt, ap) >= 0;
61 va_end(ap);
62 (*argv)[*argc - 1] = arg;
63 return rc;
64}
65
66static BOOL addFlag(int *argc, char ***argv, const char *str, BOOL flag)
67{
68 return addArgument(argc, argv, "%s%s", flag ? "+" : "-", str);
69}
70
71static void freeArguments(int argc, char **argv)
72{
73 for (int i = 0; i < argc; i++)
74 free(argv[i]);
75
76 free(argv);
77}
78
79// Designated initializer.
80- (id)initWithBookmark:(ComputerBookmark *)bookmark
81{
82 int status;
83 char **argv = nullptr;
84 int argc = 0;
85
86 if (!(self = [super init]))
87 return nil;
88
89 if (!bookmark)
90 [NSException raise:NSInvalidArgumentException
91 format:@"%s: params may not be nil.", __func__];
92
93 _bookmark = [bookmark retain];
94 _params = [[bookmark params] copy];
95 _name = [[bookmark label] retain];
96 _delegate = nil;
97 _toolbar_visible = YES;
98 _freerdp = ios_freerdp_new();
99 _ui_request_completed = [[NSCondition alloc] init];
100 BOOL connected_via_3g = ![bookmark conntectedViaWLAN];
101
102 if (!addArgument(&argc, &argv, "iFreeRDP"))
103 goto out_free;
104
105 if (!addArgument(&argc, &argv, "/gdi:sw"))
106 goto out_free;
107
108 if (!addArgument(&argc, &argv, "/relax-order-checks"))
109 goto out_free;
110 // Screen Size is set on connect (we need a valid delegate in case the user choose an automatic
111 // screen size)
112
113 // Other simple numeric settings
114 if ([_params hasValueForKey:@"colors"])
115 if (!addArgument(&argc, &argv, "/bpp:%d",
116 [_params intForKey:@"colors" with3GEnabled:connected_via_3g]))
117 goto out_free;
118
119 if ([_params hasValueForKey:@"port"])
120 if (!addArgument(&argc, &argv, "/port:%d", [_params intForKey:@"port"]))
121 goto out_free;
122
123 if ([_params boolForKey:@"console"])
124 if (!addArgument(&argc, &argv, "/admin"))
125 goto out_free;
126
127 if (!addArgument(&argc, &argv, "/v:%s", [_params UTF8StringForKey:@"hostname"]))
128 goto out_free;
129
130 // String settings
131 if ([[_params StringForKey:@"username"] length])
132 {
133 if (!addArgument(&argc, &argv, "/u:%s", [_params UTF8StringForKey:@"username"]))
134 goto out_free;
135 }
136
137 if ([[_params StringForKey:@"password"] length])
138 {
139 if (!addArgument(&argc, &argv, "/p:%s", [_params UTF8StringForKey:@"password"]))
140 goto out_free;
141 }
142
143 if ([[_params StringForKey:@"domain"] length])
144 {
145 if (!addArgument(&argc, &argv, "/d:%s", [_params UTF8StringForKey:@"domain"]))
146 goto out_free;
147 }
148
149 if ([[_params StringForKey:@"working_directory"] length])
150 {
151 if (!addArgument(&argc, &argv, "/shell-dir:%s",
152 [_params UTF8StringForKey:@"working_directory"]))
153 goto out_free;
154 }
155
156 if ([[_params StringForKey:@"remote_program"] length])
157 {
158 if (!addArgument(&argc, &argv, "/shell:%s", [_params UTF8StringForKey:@"remote_program"]))
159 goto out_free;
160 }
161
162 // RemoteFX
163 if ([_params boolForKey:@"perf_remotefx" with3GEnabled:connected_via_3g])
164 if (!addArgument(&argc, &argv, "/rfx"))
165 goto out_free;
166
167 if ([_params boolForKey:@"perf_gfx" with3GEnabled:connected_via_3g])
168 if (!addArgument(&argc, &argv, "/gfx"))
169 goto out_free;
170
171 if ([_params boolForKey:@"perf_h264" with3GEnabled:connected_via_3g])
172 if (!addArgument(&argc, &argv, "/gfx:AVC444"))
173 goto out_free;
174
175 if (![_params boolForKey:@"perf_remotefx" with3GEnabled:connected_via_3g] &&
176 ![_params boolForKey:@"perf_gfx" with3GEnabled:connected_via_3g] &&
177 ![_params boolForKey:@"perf_h264" with3GEnabled:connected_via_3g])
178 if (!addArgument(&argc, &argv, "/nsc"))
179 goto out_free;
180
181 if (!addArgument(&argc, &argv, "/cache:bitmap:on,glyph:on"))
182 goto out_free;
183
184 if (!addFlag(&argc, &argv, "wallpaper",
185 [_params boolForKey:@"perf_show_desktop" with3GEnabled:connected_via_3g]))
186 goto out_free;
187
188 if (!addFlag(&argc, &argv, "window-drag",
189 [_params boolForKey:@"perf_window_dragging" with3GEnabled:connected_via_3g]))
190 goto out_free;
191
192 if (!addFlag(&argc, &argv, "menu-anims",
193 [_params boolForKey:@"perf_menu_animation" with3GEnabled:connected_via_3g]))
194 goto out_free;
195
196 if (!addFlag(&argc, &argv, "themes",
197 [_params boolForKey:@"perf_windows_themes" with3GEnabled:connected_via_3g]))
198 goto out_free;
199
200 if (!addFlag(&argc, &argv, "fonts",
201 [_params boolForKey:@"perf_font_smoothing" with3GEnabled:connected_via_3g]))
202 goto out_free;
203
204 if (!addFlag(&argc, &argv, "aero",
205 [_params boolForKey:@"perf_desktop_composition" with3GEnabled:connected_via_3g]))
206 goto out_free;
207
208 if ([_params hasValueForKey:@"width"])
209 if (!addArgument(&argc, &argv, "/w:%d", [_params intForKey:@"width"]))
210 goto out_free;
211
212 if ([_params hasValueForKey:@"height"])
213 if (!addArgument(&argc, &argv, "/h:%d", [_params intForKey:@"height"]))
214 goto out_free;
215
216 // security
217 switch ([_params intForKey:@"security"])
218 {
219 case TSXProtocolSecurityNLA:
220 if (!addArgument(&argc, &argv, "/sec:NLA"))
221 goto out_free;
222
223 break;
224
225 case TSXProtocolSecurityTLS:
226 if (!addArgument(&argc, &argv, "/sec:TLS"))
227 goto out_free;
228
229 break;
230
231 case TSXProtocolSecurityRDP:
232 if (!addArgument(&argc, &argv, "/sec:RDP"))
233 goto out_free;
234
235 break;
236
237 default:
238 break;
239 }
240
241 // ts gateway settings
242 if ([_params boolForKey:@"enable_tsg_settings"])
243 {
244 if (!addArgument(&argc, &argv, "/gateway:g:%s:%d,u:%s,d:%s,p:%s",
245 [_params UTF8StringForKey:@"tsg_hostname"],
246 [_params intForKey:@"tsg_port"],
247 [_params UTF8StringForKey:@"tsg_username"],
248 [_params UTF8StringForKey:@"tsg_domain"],
249 [_params UTF8StringForKey:@"tsg_password"]))
250 goto out_free;
251 }
252
253 // Remote keyboard layout
254 if (!addArgument(&argc, &argv, "/kbd:layout:%d", 0x409))
255 goto out_free;
256
257 // send characters without a US scancode (e.g. Korean) as unicode input
258 if (!addArgument(&argc, &argv, "/kbd:unicode:on"))
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;
270out_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 width 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, nullptr);
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- (void)setRemoteCursor:(RDPCursor *)cursor
399{
400 if ([[self delegate] respondsToSelector:@selector(session:didSetRemoteCursor:)])
401 [[self delegate] session:self didSetRemoteCursor:cursor];
402}
403
404- (void)setRemoteCursorPositionValue:(NSValue *)positionValue
405{
406 if ([[self delegate] respondsToSelector:@selector(session:didMoveRemoteCursor:)])
407 [[self delegate] session:self didMoveRemoteCursor:[positionValue CGPointValue]];
408}
409
410- (void)hideRemoteCursor
411{
412 if ([[self delegate] respondsToSelector:@selector(sessionDidHideRemoteCursor:)])
413 [[self delegate] sessionDidHideRemoteCursor:self];
414}
415
416- (void)setDefaultRemoteCursor
417{
418 if ([[self delegate] respondsToSelector:@selector(sessionDidSetDefaultRemoteCursor:)])
419 [[self delegate] sessionDidSetDefaultRemoteCursor:self];
420}
421
422#pragma mark -
423#pragma mark interface functions
424
425- (UIImage *)getScreenshotWithSize:(CGSize)size
426{
427 NSAssert([self mfi]->bitmap_context != nil,
428 @"Screenshot requested while having no valid RDP drawing context");
429 CGImageRef cgImage = CGBitmapContextCreateImage([self mfi]->bitmap_context);
430 UIGraphicsBeginImageContext(size);
431 CGContextTranslateCTM(UIGraphicsGetCurrentContext(), 0, size.height);
432 CGContextScaleCTM(UIGraphicsGetCurrentContext(), 1.0, -1.0);
433 CGContextDrawImage(UIGraphicsGetCurrentContext(), CGRectMake(0, 0, size.width, size.height),
434 cgImage);
435 UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
436 UIGraphicsEndImageContext();
437 CGImageRelease(cgImage);
438 return viewImage;
439}
440
441- (rdpSettings *)getSessionParams
442{
443 return _freerdp->context->settings;
444}
445
446- (NSString *)sessionName
447{
448 return _name;
449}
450
451@end
452
453#pragma mark -
454@implementation RDPSession (Private)
455
456- (mfInfo *)mfi
457{
458 return MFI_FROM_INSTANCE(_freerdp);
459}
460
461// Blocks until rdp session finishes.
462- (void)runSession
463{
464 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
465 // Run the session
466 [self performSelectorOnMainThread:@selector(sessionWillConnect)
467 withObject:nil
468 waitUntilDone:YES];
469 int result_code = ios_run_freerdp(_freerdp);
470 [self mfi]->connection_state = TSXConnectionDisconnected;
471 [self performSelectorOnMainThread:@selector(runSessionFinished:)
472 withObject:[NSNumber numberWithInt:result_code]
473 waitUntilDone:YES];
474 [pool release];
475}
476
477// Main thread.
478- (void)runSessionFinished:(NSNumber *)result
479{
480 int result_code = [result intValue];
481
482 switch (result_code)
483 {
484 case MF_EXIT_CONN_CANCELED:
485 [self sessionDidDisconnect];
486 break;
487
488 case MF_EXIT_LOGON_TIMEOUT:
489 case MF_EXIT_CONN_FAILED:
490 [self sessionDidFailToConnect:result_code];
491 break;
492
493 case MF_EXIT_SUCCESS:
494 default:
495 [self sessionDidDisconnect];
496 break;
497 }
498}
499
500#pragma mark -
501#pragma mark Session management (main thread)
502
503- (void)sessionWillConnect
504{
505 if ([[self delegate] respondsToSelector:@selector(sessionWillConnect:)])
506 [[self delegate] sessionWillConnect:self];
507}
508
509- (void)sessionDidConnect
510{
511 if ([[self delegate] respondsToSelector:@selector(sessionDidConnect:)])
512 [[self delegate] sessionDidConnect:self];
513}
514
515- (void)sessionDidFailToConnect:(int)reason
516{
517 [[NSNotificationCenter defaultCenter]
518 postNotificationName:TSXSessionDidFailToConnectNotification
519 object:self];
520
521 if ([[self delegate] respondsToSelector:@selector(session:didFailToConnect:)])
522 [[self delegate] session:self didFailToConnect:reason];
523}
524
525- (void)sessionDidDisconnect
526{
527 [[NSNotificationCenter defaultCenter] postNotificationName:TSXSessionDidDisconnectNotification
528 object:self];
529
530 if ([[self delegate] respondsToSelector:@selector(sessionDidDisconnect:)])
531 [[self delegate] sessionDidDisconnect:self];
532}
533
534- (void)sessionBitmapContextWillChange
535{
536 if ([[self delegate] respondsToSelector:@selector(sessionBitmapContextWillChange:)])
537 [[self delegate] sessionBitmapContextWillChange:self];
538}
539
540- (void)sessionBitmapContextDidChange
541{
542 if ([[self delegate] respondsToSelector:@selector(sessionBitmapContextDidChange:)])
543 [[self delegate] sessionBitmapContextDidChange:self];
544}
545
546- (void)sessionRequestsAuthenticationWithParams:(NSMutableDictionary *)params
547{
548 if ([[self delegate] respondsToSelector:@selector(session:requestsAuthenticationWithParams:)])
549 [[self delegate] session:self requestsAuthenticationWithParams:params];
550}
551
552- (void)sessionVerifyCertificateWithParams:(NSMutableDictionary *)params
553{
554 if ([[self delegate] respondsToSelector:@selector(session:verifyCertificateWithParams:)])
555 [[self delegate] session:self verifyCertificateWithParams:params];
556}
557
558@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.