532 lines
14 KiB
Objective-C
532 lines
14 KiB
Objective-C
/*
|
|
RDP Session object
|
|
|
|
Copyright 2013 Thincast Technologies GmbH, Authors: Martin Fleisz, Dorian Johnson
|
|
|
|
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
|
|
If a copy of the MPL was not distributed with this file, You can obtain one at
|
|
http://mozilla.org/MPL/2.0/.
|
|
*/
|
|
|
|
#import "ios_freerdp.h"
|
|
#import "ios_freerdp_ui.h"
|
|
#import "ios_freerdp_events.h"
|
|
|
|
#import "RDPSession.h"
|
|
#import "TSXTypes.h"
|
|
#import "Bookmark.h"
|
|
#import "ConnectionParams.h"
|
|
|
|
NSString *TSXSessionDidDisconnectNotification = @"TSXSessionDidDisconnect";
|
|
NSString *TSXSessionDidFailToConnectNotification = @"TSXSessionDidFailToConnect";
|
|
|
|
@interface RDPSession (Private)
|
|
- (void)runSession;
|
|
- (void)runSessionFinished:(NSNumber *)result;
|
|
- (mfInfo *)mfi;
|
|
|
|
// The connection thread calls these on the main thread.
|
|
- (void)sessionWillConnect;
|
|
- (void)sessionDidConnect;
|
|
- (void)sessionDidDisconnect;
|
|
- (void)sessionDidFailToConnect:(int)reason;
|
|
- (void)sessionBitmapContextWillChange;
|
|
- (void)sessionBitmapContextDidChange;
|
|
@end
|
|
|
|
@implementation RDPSession
|
|
|
|
@synthesize delegate = _delegate, params = _params, toolbarVisible = _toolbar_visible,
|
|
uiRequestCompleted = _ui_request_completed, bookmark = _bookmark;
|
|
|
|
+ (void)initialize
|
|
{
|
|
ios_init_freerdp();
|
|
}
|
|
|
|
static BOOL addArgument(int *argc, char ***argv, const char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
char *arg = NULL;
|
|
char **tmp = realloc(*argv, (*argc + 1) * sizeof(char *));
|
|
|
|
if (!tmp)
|
|
return FALSE;
|
|
|
|
*argv = tmp;
|
|
*argc = *argc + 1;
|
|
va_start(ap, fmt);
|
|
vasprintf(&arg, fmt, ap);
|
|
va_end(ap);
|
|
(*argv)[*argc - 1] = arg;
|
|
return TRUE;
|
|
}
|
|
|
|
static BOOL addFlag(int *argc, char ***argv, const char *str, BOOL flag)
|
|
{
|
|
return addArgument(argc, argv, "%s%s", flag ? "+" : "-", str);
|
|
}
|
|
|
|
static void freeArguments(int argc, char **argv)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < argc; i++)
|
|
free(argv[i]);
|
|
|
|
free(argv);
|
|
}
|
|
|
|
// Designated initializer.
|
|
- (id)initWithBookmark:(ComputerBookmark *)bookmark
|
|
{
|
|
int status;
|
|
char **argv = NULL;
|
|
int argc = 0;
|
|
|
|
if (!(self = [super init]))
|
|
return nil;
|
|
|
|
if (!bookmark)
|
|
[NSException raise:NSInvalidArgumentException
|
|
format:@"%s: params may not be nil.", __func__];
|
|
|
|
_bookmark = [bookmark retain];
|
|
_params = [[bookmark params] copy];
|
|
_name = [[bookmark label] retain];
|
|
_delegate = nil;
|
|
_toolbar_visible = YES;
|
|
_freerdp = ios_freerdp_new();
|
|
_ui_request_completed = [[NSCondition alloc] init];
|
|
BOOL connected_via_3g = ![bookmark conntectedViaWLAN];
|
|
|
|
if (!addArgument(&argc, &argv, "iFreeRDP"))
|
|
goto out_free;
|
|
|
|
if (!addArgument(&argc, &argv, "/gdi:sw"))
|
|
goto out_free;
|
|
|
|
// Screen Size is set on connect (we need a valid delegate in case the user choose an automatic
|
|
// screen size)
|
|
|
|
// Other simple numeric settings
|
|
if ([_params hasValueForKey:@"colors"])
|
|
if (!addArgument(&argc, &argv, "/bpp:%d",
|
|
[_params intForKey:@"colors" with3GEnabled:connected_via_3g]))
|
|
goto out_free;
|
|
|
|
if ([_params hasValueForKey:@"port"])
|
|
if (!addArgument(&argc, &argv, "/port:%d", [_params intForKey:@"port"]))
|
|
goto out_free;
|
|
|
|
if ([_params boolForKey:@"console"])
|
|
if (!addArgument(&argc, &argv, "/admin"))
|
|
goto out_free;
|
|
|
|
if (!addArgument(&argc, &argv, "/v:%s", [_params UTF8StringForKey:@"hostname"]))
|
|
goto out_free;
|
|
|
|
// String settings
|
|
if ([[_params StringForKey:@"username"] length])
|
|
{
|
|
if (!addArgument(&argc, &argv, "/u:%s", [_params UTF8StringForKey:@"username"]))
|
|
goto out_free;
|
|
}
|
|
|
|
if ([[_params StringForKey:@"password"] length])
|
|
{
|
|
if (!addArgument(&argc, &argv, "/p:%s", [_params UTF8StringForKey:@"password"]))
|
|
goto out_free;
|
|
}
|
|
|
|
if ([[_params StringForKey:@"domain"] length])
|
|
{
|
|
if (!addArgument(&argc, &argv, "/d:%s", [_params UTF8StringForKey:@"domain"]))
|
|
goto out_free;
|
|
}
|
|
|
|
if ([[_params StringForKey:@"working_directory"] length])
|
|
{
|
|
if (!addArgument(&argc, &argv, "/shell-dir:%s",
|
|
[_params UTF8StringForKey:@"working_directory"]))
|
|
goto out_free;
|
|
}
|
|
|
|
if ([[_params StringForKey:@"remote_program"] length])
|
|
{
|
|
if (!addArgument(&argc, &argv, "/shell:%s", [_params UTF8StringForKey:@"remote_program"]))
|
|
goto out_free;
|
|
}
|
|
|
|
// RemoteFX
|
|
if ([_params boolForKey:@"perf_remotefx" with3GEnabled:connected_via_3g])
|
|
if (!addArgument(&argc, &argv, "/rfx"))
|
|
goto out_free;
|
|
|
|
if ([_params boolForKey:@"perf_gfx" with3GEnabled:connected_via_3g])
|
|
if (!addArgument(&argc, &argv, "/gfx"))
|
|
goto out_free;
|
|
|
|
if ([_params boolForKey:@"perf_h264" with3GEnabled:connected_via_3g])
|
|
if (!addArgument(&argc, &argv, "/gfx-h264"))
|
|
goto out_free;
|
|
|
|
if (![_params boolForKey:@"perf_remotefx" with3GEnabled:connected_via_3g] &&
|
|
![_params boolForKey:@"perf_gfx" with3GEnabled:connected_via_3g] &&
|
|
![_params boolForKey:@"perf_h264" with3GEnabled:connected_via_3g])
|
|
if (!addArgument(&argc, &argv, "/nsc"))
|
|
goto out_free;
|
|
|
|
if (!addFlag(&argc, &argv, "bitmap-cache", TRUE))
|
|
goto out_free;
|
|
|
|
if (!addFlag(&argc, &argv, "wallpaper",
|
|
[_params boolForKey:@"perf_show_desktop" with3GEnabled:connected_via_3g]))
|
|
goto out_free;
|
|
|
|
if (!addFlag(&argc, &argv, "window-drag",
|
|
[_params boolForKey:@"perf_window_dragging" with3GEnabled:connected_via_3g]))
|
|
goto out_free;
|
|
|
|
if (!addFlag(&argc, &argv, "menu-anims",
|
|
[_params boolForKey:@"perf_menu_animation" with3GEnabled:connected_via_3g]))
|
|
goto out_free;
|
|
|
|
if (!addFlag(&argc, &argv, "themes",
|
|
[_params boolForKey:@"perf_windows_themes" with3GEnabled:connected_via_3g]))
|
|
goto out_free;
|
|
|
|
if (!addFlag(&argc, &argv, "fonts",
|
|
[_params boolForKey:@"perf_font_smoothing" with3GEnabled:connected_via_3g]))
|
|
goto out_free;
|
|
|
|
if (!addFlag(&argc, &argv, "aero",
|
|
[_params boolForKey:@"perf_desktop_composition" with3GEnabled:connected_via_3g]))
|
|
goto out_free;
|
|
|
|
if ([_params hasValueForKey:@"width"])
|
|
if (!addArgument(&argc, &argv, "/w:%d", [_params intForKey:@"width"]))
|
|
goto out_free;
|
|
|
|
if ([_params hasValueForKey:@"height"])
|
|
if (!addArgument(&argc, &argv, "/h:%d", [_params intForKey:@"height"]))
|
|
goto out_free;
|
|
|
|
// security
|
|
switch ([_params intForKey:@"security"])
|
|
{
|
|
case TSXProtocolSecurityNLA:
|
|
if (!addArgument(&argc, &argv, "/sec:NLA"))
|
|
goto out_free;
|
|
|
|
break;
|
|
|
|
case TSXProtocolSecurityTLS:
|
|
if (!addArgument(&argc, &argv, "/sec:TLS"))
|
|
goto out_free;
|
|
|
|
break;
|
|
|
|
case TSXProtocolSecurityRDP:
|
|
if (!addArgument(&argc, &argv, "/sec:RDP"))
|
|
goto out_free;
|
|
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// ts gateway settings
|
|
if ([_params boolForKey:@"enable_tsg_settings"])
|
|
{
|
|
if (!addArgument(&argc, &argv, "/g:%s", [_params UTF8StringForKey:@"tsg_hostname"]))
|
|
goto out_free;
|
|
|
|
if (!addArgument(&argc, &argv, "/gp:%d", [_params intForKey:@"tsg_port"]))
|
|
goto out_free;
|
|
|
|
if (!addArgument(&argc, &argv, "/gu:%s", [_params intForKey:@"tsg_username"]))
|
|
goto out_free;
|
|
|
|
if (!addArgument(&argc, &argv, "/gp:%s", [_params intForKey:@"tsg_password"]))
|
|
goto out_free;
|
|
|
|
if (!addArgument(&argc, &argv, "/gd:%s", [_params intForKey:@"tsg_domain"]))
|
|
goto out_free;
|
|
}
|
|
|
|
// Remote keyboard layout
|
|
if (!addArgument(&argc, &argv, "/kbd:%d", 0x409))
|
|
goto out_free;
|
|
|
|
status = freerdp_client_settings_parse_command_line(_freerdp->settings, argc, argv, FALSE);
|
|
|
|
if (0 != status)
|
|
goto out_free;
|
|
|
|
freeArguments(argc, argv);
|
|
[self mfi]->session = self;
|
|
return self;
|
|
out_free:
|
|
freeArguments(argc, argv);
|
|
[self release];
|
|
return nil;
|
|
}
|
|
|
|
- (void)dealloc
|
|
{
|
|
[self setDelegate:nil];
|
|
[_bookmark release];
|
|
[_name release];
|
|
[_params release];
|
|
[_ui_request_completed release];
|
|
ios_freerdp_free(_freerdp);
|
|
[super dealloc];
|
|
}
|
|
|
|
- (CGContextRef)bitmapContext
|
|
{
|
|
return [self mfi]->bitmap_context;
|
|
}
|
|
|
|
#pragma mark -
|
|
#pragma mark Connecting and disconnecting
|
|
|
|
- (void)connect
|
|
{
|
|
// Set Screen Size to automatic if widht or height are still 0
|
|
rdpSettings *settings = _freerdp->settings;
|
|
|
|
if (settings->DesktopWidth == 0 || settings->DesktopHeight == 0)
|
|
{
|
|
CGSize size = CGSizeZero;
|
|
|
|
if ([[self delegate] respondsToSelector:@selector(sizeForFitScreenForSession:)])
|
|
size = [[self delegate] sizeForFitScreenForSession:self];
|
|
|
|
if (!CGSizeEqualToSize(CGSizeZero, size))
|
|
{
|
|
[_params setInt:size.width forKey:@"width"];
|
|
[_params setInt:size.height forKey:@"height"];
|
|
settings->DesktopWidth = size.width;
|
|
settings->DesktopHeight = size.height;
|
|
}
|
|
}
|
|
|
|
// TODO: This is a hack to ensure connections to RDVH with 16bpp don't have an odd screen
|
|
// resolution width
|
|
// Otherwise this could result in screen corruption ..
|
|
if (settings->ColorDepth <= 16)
|
|
settings->DesktopWidth &= (~1);
|
|
|
|
[self performSelectorInBackground:@selector(runSession) withObject:nil];
|
|
}
|
|
|
|
- (void)disconnect
|
|
{
|
|
mfInfo *mfi = [self mfi];
|
|
ios_events_send(mfi, [NSDictionary dictionaryWithObject:@"disconnect" forKey:@"type"]);
|
|
|
|
if (mfi->connection_state == TSXConnectionConnecting)
|
|
{
|
|
mfi->unwanted = YES;
|
|
[self sessionDidDisconnect];
|
|
return;
|
|
}
|
|
}
|
|
|
|
- (TSXConnectionState)connectionState
|
|
{
|
|
return [self mfi]->connection_state;
|
|
}
|
|
|
|
// suspends the session
|
|
- (void)suspend
|
|
{
|
|
if (!_suspended)
|
|
{
|
|
_suspended = YES;
|
|
// instance->update->SuppressOutput(instance->context, 0, NULL);
|
|
}
|
|
}
|
|
|
|
// resumes a previously suspended session
|
|
- (void)resume
|
|
{
|
|
if (_suspended)
|
|
{
|
|
/* RECTANGLE_16 rec;
|
|
rec.left = 0;
|
|
rec.top = 0;
|
|
rec.right = instance->settings->width;
|
|
rec.bottom = instance->settings->height;
|
|
*/
|
|
_suspended = NO;
|
|
// instance->update->SuppressOutput(instance->context, 1, &rec);
|
|
// [delegate sessionScreenSettingsChanged:self];
|
|
}
|
|
}
|
|
|
|
// returns YES if the session is started
|
|
- (BOOL)isSuspended
|
|
{
|
|
return _suspended;
|
|
}
|
|
|
|
#pragma mark -
|
|
#pragma mark Input events
|
|
|
|
- (void)sendInputEvent:(NSDictionary *)eventDescriptor
|
|
{
|
|
if ([self mfi]->connection_state == TSXConnectionConnected)
|
|
ios_events_send([self mfi], eventDescriptor);
|
|
}
|
|
|
|
#pragma mark -
|
|
#pragma mark Server events (main thread)
|
|
|
|
- (void)setNeedsDisplayInRectAsValue:(NSValue *)rect_value
|
|
{
|
|
if ([[self delegate] respondsToSelector:@selector(session:needsRedrawInRect:)])
|
|
[[self delegate] session:self needsRedrawInRect:[rect_value CGRectValue]];
|
|
}
|
|
|
|
#pragma mark -
|
|
#pragma mark interface functions
|
|
|
|
- (UIImage *)getScreenshotWithSize:(CGSize)size
|
|
{
|
|
NSAssert([self mfi]->bitmap_context != nil,
|
|
@"Screenshot requested while having no valid RDP drawing context");
|
|
CGImageRef cgImage = CGBitmapContextCreateImage([self mfi]->bitmap_context);
|
|
UIGraphicsBeginImageContext(size);
|
|
CGContextTranslateCTM(UIGraphicsGetCurrentContext(), 0, size.height);
|
|
CGContextScaleCTM(UIGraphicsGetCurrentContext(), 1.0, -1.0);
|
|
CGContextDrawImage(UIGraphicsGetCurrentContext(), CGRectMake(0, 0, size.width, size.height),
|
|
cgImage);
|
|
UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
|
|
UIGraphicsEndImageContext();
|
|
CGImageRelease(cgImage);
|
|
return viewImage;
|
|
}
|
|
|
|
- (rdpSettings *)getSessionParams
|
|
{
|
|
return _freerdp->settings;
|
|
}
|
|
|
|
- (NSString *)sessionName
|
|
{
|
|
return _name;
|
|
}
|
|
|
|
@end
|
|
|
|
#pragma mark -
|
|
@implementation RDPSession (Private)
|
|
|
|
- (mfInfo *)mfi
|
|
{
|
|
return MFI_FROM_INSTANCE(_freerdp);
|
|
}
|
|
|
|
// Blocks until rdp session finishes.
|
|
- (void)runSession
|
|
{
|
|
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
|
// Run the session
|
|
[self performSelectorOnMainThread:@selector(sessionWillConnect)
|
|
withObject:nil
|
|
waitUntilDone:YES];
|
|
int result_code = ios_run_freerdp(_freerdp);
|
|
[self mfi]->connection_state = TSXConnectionDisconnected;
|
|
[self performSelectorOnMainThread:@selector(runSessionFinished:)
|
|
withObject:[NSNumber numberWithInt:result_code]
|
|
waitUntilDone:YES];
|
|
[pool release];
|
|
}
|
|
|
|
// Main thread.
|
|
- (void)runSessionFinished:(NSNumber *)result
|
|
{
|
|
int result_code = [result intValue];
|
|
|
|
switch (result_code)
|
|
{
|
|
case MF_EXIT_CONN_CANCELED:
|
|
[self sessionDidDisconnect];
|
|
break;
|
|
|
|
case MF_EXIT_LOGON_TIMEOUT:
|
|
case MF_EXIT_CONN_FAILED:
|
|
[self sessionDidFailToConnect:result_code];
|
|
break;
|
|
|
|
case MF_EXIT_SUCCESS:
|
|
default:
|
|
[self sessionDidDisconnect];
|
|
break;
|
|
}
|
|
}
|
|
|
|
#pragma mark -
|
|
#pragma mark Session management (main thread)
|
|
|
|
- (void)sessionWillConnect
|
|
{
|
|
if ([[self delegate] respondsToSelector:@selector(sessionWillConnect:)])
|
|
[[self delegate] sessionWillConnect:self];
|
|
}
|
|
|
|
- (void)sessionDidConnect
|
|
{
|
|
if ([[self delegate] respondsToSelector:@selector(sessionDidConnect:)])
|
|
[[self delegate] sessionDidConnect:self];
|
|
}
|
|
|
|
- (void)sessionDidFailToConnect:(int)reason
|
|
{
|
|
[[NSNotificationCenter defaultCenter]
|
|
postNotificationName:TSXSessionDidFailToConnectNotification
|
|
object:self];
|
|
|
|
if ([[self delegate] respondsToSelector:@selector(session:didFailToConnect:)])
|
|
[[self delegate] session:self didFailToConnect:reason];
|
|
}
|
|
|
|
- (void)sessionDidDisconnect
|
|
{
|
|
[[NSNotificationCenter defaultCenter] postNotificationName:TSXSessionDidDisconnectNotification
|
|
object:self];
|
|
|
|
if ([[self delegate] respondsToSelector:@selector(sessionDidDisconnect:)])
|
|
[[self delegate] sessionDidDisconnect:self];
|
|
}
|
|
|
|
- (void)sessionBitmapContextWillChange
|
|
{
|
|
if ([[self delegate] respondsToSelector:@selector(sessionBitmapContextWillChange:)])
|
|
[[self delegate] sessionBitmapContextWillChange:self];
|
|
}
|
|
|
|
- (void)sessionBitmapContextDidChange
|
|
{
|
|
if ([[self delegate] respondsToSelector:@selector(sessionBitmapContextDidChange:)])
|
|
[[self delegate] sessionBitmapContextDidChange:self];
|
|
}
|
|
|
|
- (void)sessionRequestsAuthenticationWithParams:(NSMutableDictionary *)params
|
|
{
|
|
if ([[self delegate] respondsToSelector:@selector(session:requestsAuthenticationWithParams:)])
|
|
[[self delegate] session:self requestsAuthenticationWithParams:params];
|
|
}
|
|
|
|
- (void)sessionVerifyCertificateWithParams:(NSMutableDictionary *)params
|
|
{
|
|
if ([[self delegate] respondsToSelector:@selector(session:verifyCertificateWithParams:)])
|
|
[[self delegate] session:self verifyCertificateWithParams:params];
|
|
}
|
|
|
|
@end
|