FreeRDP
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Modules Pages
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
20NSString *TSXSessionDidDisconnectNotification = @"TSXSessionDidDisconnect";
21NSString *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
47static 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
65static BOOL addFlag(int *argc, char ***argv, const char *str, BOOL flag)
66{
67 return addArgument(argc, argv, "%s%s", flag ? "+" : "-", str);
68}
69
70static 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;
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, 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.