Milestone 5: deliver embedded RDP sessions and lifecycle hardening

This commit is contained in:
Keith Smith
2026-03-03 18:59:26 -07:00
parent 230a401386
commit 36006bd4aa
2941 changed files with 724359 additions and 77 deletions

View File

@@ -0,0 +1,48 @@
/*
Bookmark model abstraction
Copyright 2013 Thincast Technologies GmbH, Authors: 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 <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import "ConnectionParams.h"
@interface ComputerBookmark : NSObject <NSCoding>
{
@protected
ComputerBookmark *_parent;
NSString *_uuid, *_label;
UIImage *_image;
ConnectionParams *_connection_params;
BOOL _connected_via_wlan;
}
@property(nonatomic, assign) ComputerBookmark *parent;
@property(nonatomic, readonly) NSString *uuid;
@property(nonatomic, copy) NSString *label;
@property(nonatomic, retain) UIImage *image;
@property(readonly, nonatomic) ConnectionParams *params;
@property(nonatomic, assign) BOOL conntectedViaWLAN;
// Creates a copy of this object, with a new UUID
- (id)copy;
- (id)copyWithUUID;
// Whether user can delete, move, or rename this entry
- (BOOL)isDeletable;
- (BOOL)isMovable;
- (BOOL)isRenamable;
- (BOOL)hasImmutableHost;
- (id)initWithConnectionParameters:(ConnectionParams *)params;
- (id)initWithBaseDefaultParameters;
// A copy of @params, with _bookmark_uuid set.
- (ConnectionParams *)copyMarkedParams;
@end

View File

@@ -0,0 +1,312 @@
/*
Bookmark model abstraction
Copyright 2013 Thincast Technologies GmbH, Author: 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 "Bookmark.h"
#import "TSXAdditions.h"
#import "Utils.h"
#import "GlobalDefaults.h"
@interface ComputerBookmark (Private)
- (void)willChangeValueForKeyPath:(NSString *)keyPath;
- (void)didChangeValueForKeyPath:(NSString *)keyPath;
@end
@implementation ComputerBookmark
@synthesize parent = _parent, uuid = _uuid, label = _label, image = _image;
@synthesize params = _connection_params, conntectedViaWLAN = _connected_via_wlan;
- (id)init
{
if (!(self = [super init]))
return nil;
_uuid = [[NSString stringWithUUID] retain];
_label = @"";
_connected_via_wlan = NO;
return self;
}
// Designated initializer.
- (id)initWithConnectionParameters:(ConnectionParams *)params
{
if (!(self = [self init]))
return nil;
_connection_params = [params copy];
_connected_via_wlan = NO;
return self;
}
- (id)initWithCoder:(NSCoder *)decoder
{
if (!(self = [self init]))
return nil;
if (![decoder allowsKeyedCoding])
[NSException raise:NSInvalidArgumentException format:@"coder must support keyed archiving"];
if ([decoder containsValueForKey:@"uuid"])
{
[_uuid release];
_uuid = [[decoder decodeObjectForKey:@"uuid"] retain];
}
if ([decoder containsValueForKey:@"label"])
[self setLabel:[decoder decodeObjectForKey:@"label"]];
if ([decoder containsValueForKey:@"connectionParams"])
{
[_connection_params release];
_connection_params = [[decoder decodeObjectForKey:@"connectionParams"] retain];
}
return self;
}
- (id)initWithBaseDefaultParameters
{
return [self initWithConnectionParameters:[[[ConnectionParams alloc]
initWithBaseDefaultParameters] autorelease]];
}
- (id)copy
{
ComputerBookmark *copy = [[[self class] alloc] init];
[copy setLabel:[self label]];
copy->_connection_params = [_connection_params copy];
return copy;
}
- (id)copyWithUUID
{
ComputerBookmark *copy = [self copy];
copy->_uuid = [[self uuid] copy];
return copy;
}
- (void)encodeWithCoder:(NSCoder *)coder
{
if (![coder allowsKeyedCoding])
[NSException raise:NSInvalidArgumentException format:@"coder must support keyed archiving"];
[coder encodeObject:_uuid forKey:@"uuid"];
[coder encodeObject:_label forKey:@"label"];
[coder encodeObject:_connection_params forKey:@"connectionParams"];
}
- (void)dealloc
{
_parent = nil;
[_label release];
_label = nil;
[_uuid release];
_uuid = nil;
[_connection_params release];
_connection_params = nil;
[super dealloc];
}
- (UIImage *)image
{
return nil;
}
- (BOOL)isEqual:(id)object
{
return [object respondsToSelector:@selector(uuid)] && [[object uuid] isEqual:_uuid];
}
- (NSString *)description
{
return ([self label] != nil) ? [self label] : _uuid;
}
- (BOOL)validateValue:(id *)val forKey:(NSString *)key error:(NSError **)error
{
NSString *string_value = *val;
if ([key isEqualToString:@"label"])
{
if (![[string_value stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]
length])
{
if (error)
*error = [NSError
errorWithDomain:@""
code:NSKeyValueValidationError
userInfo:
[NSDictionary
dictionaryWithObjectsAndKeys:
NSLocalizedString(
@"Connection labels cannot be blank",
@"Bookmark data validation: label blank title."),
NSLocalizedDescriptionKey,
NSLocalizedString(
@"Please enter the short description of this Connection "
@"that will appear in the Connection list.",
@"Bookmark data validation: label blank message."),
NSLocalizedRecoverySuggestionErrorKey, nil]];
return NO;
}
}
return YES;
}
- (BOOL)validateValue:(id *)val forKeyPath:(NSString *)keyPath error:(NSError **)error
{
// Could be used to validate params.hostname, params.password, params.port, etc.
return [super validateValue:val forKeyPath:keyPath error:error];
}
- (BOOL)isDeletable
{
return YES;
}
- (BOOL)isMovable
{
return YES;
}
- (BOOL)isRenamable
{
return YES;
}
- (BOOL)hasImmutableHost
{
return NO;
}
#pragma mark Custom KVC
- (void)setValue:(id)value forKeyPath:(NSString *)keyPath
{
if ([keyPath isEqualToString:@"params.resolution"])
{
int width, height;
TSXScreenOptions type;
if (ScanScreenResolution(value, &width, &height, &type))
{
[_connection_params willChangeValueForKey:@"resolution"];
[[self params] setInt:type forKey:@"screen_resolution_type"];
if (type == TSXScreenOptionFixed)
{
[[self params] setInt:width forKey:@"width"];
[[self params] setInt:height forKey:@"height"];
}
[_connection_params didChangeValueForKey:@"resolution"];
}
else
[NSException raise:NSInvalidArgumentException
format:@"%s got invalid screen resolution '%@'", __func__, value];
}
else
{
[self willChangeValueForKeyPath:keyPath];
[super setValue:value forKeyPath:keyPath];
[self didChangeValueForKeyPath:keyPath];
}
}
- (id)valueForKeyPath:(NSString *)keyPath
{
if ([keyPath isEqualToString:@"params.resolution"])
return ScreenResolutionDescription([[self params] intForKey:@"screen_resolution_type"],
[[self params] intForKey:@"width"],
[[self params] intForKey:@"height"]);
return [super valueForKeyPath:keyPath];
}
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context
{
if ([[change objectForKey:NSKeyValueChangeNotificationIsPriorKey] boolValue])
[self willChangeValueForKeyPath:keyPath];
else
[self didChangeValueForKeyPath:keyPath];
}
- (NSDictionary *)targetForChangeNotificationForKeyPath:(NSString *)keyPath
{
NSString *changed_key = keyPath;
NSObject *changed_object = self;
if ([keyPath rangeOfString:@"params."].location == 0)
{
changed_key = [keyPath substringFromIndex:[@"params." length]];
changed_object = _connection_params;
}
return [NSDictionary
dictionaryWithObjectsAndKeys:changed_key, @"key", changed_object, @"object", nil];
}
- (void)willChangeValueForKeyPath:(NSString *)keyPath
{
NSDictionary *target = [self targetForChangeNotificationForKeyPath:keyPath];
[[target objectForKey:@"object"] willChangeValueForKey:[target objectForKey:@"key"]];
}
- (void)didChangeValueForKeyPath:(NSString *)keyPath
{
NSDictionary *target = [self targetForChangeNotificationForKeyPath:keyPath];
[[target objectForKey:@"object"] didChangeValueForKey:[target objectForKey:@"key"]];
}
- (ConnectionParams *)copyMarkedParams
{
ConnectionParams *param_copy = [[self params] copy];
[param_copy setValue:[self uuid] forKey:@"_bookmark_uuid"];
return param_copy;
}
#pragma mark No children
- (NSUInteger)numberOfChildren
{
return 0;
}
- (NSUInteger)numberOfDescendants
{
return 1;
}
- (BookmarkBase *)childAtIndex:(NSUInteger)index
{
return nil;
}
- (NSUInteger)indexOfChild:(BookmarkBase *)child
{
return 0;
}
- (void)removeChild:(BookmarkBase *)child
{
}
- (void)addChild:(BookmarkBase *)child
{
}
- (void)addChild:(BookmarkBase *)child afterExistingChild:(BookmarkBase *)existingChild
{
}
- (void)addChild:(BookmarkBase *)child atIndex:(NSInteger)index
{
}
- (BOOL)hasDescendant:(BookmarkBase *)needle
{
return NO;
}
- (BOOL)canContainChildren
{
return NO;
}
@end

View File

@@ -0,0 +1,46 @@
/*
Connection Parameters abstraction
Copyright 2013 Thincast Technologies GmbH, Author: 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 <Foundation/Foundation.h>
@interface ConnectionParams : NSObject
{
@private
NSMutableDictionary *_connection_params;
}
// Designated initializer.
- (id)initWithDictionary:(NSDictionary *)dict;
- (id)initWithBaseDefaultParameters;
// Getting/setting values
- (NSArray *)allKeys;
- (void)setValue:(id)value forKey:(NSString *)key;
- (id)valueForKey:(NSString *)key;
- (BOOL)hasValueForKey:(NSString *)key;
- (void)setInt:(int)integer forKey:(NSString *)key;
- (int)intForKey:(NSString *)key;
- (void)setBool:(BOOL)v forKey:(NSString *)key;
- (BOOL)boolForKey:(NSString *)key;
- (const char *)UTF8StringForKey:(NSString *)key;
- (NSString *)StringForKey:(NSString *)key;
- (BOOL)hasValueForKeyPath:(NSString *)key;
- (void)setInt:(int)integer forKeyPath:(NSString *)key;
- (int)intForKeyPath:(NSString *)key;
- (void)setBool:(BOOL)v forKeyPath:(NSString *)key;
- (BOOL)boolForKeyPath:(NSString *)key;
- (const char *)UTF8StringForKeyPath:(NSString *)key;
- (NSString *)StringForKeyPath:(NSString *)key;
- (int)intForKey:(NSString *)key with3GEnabled:(BOOL)enabled;
- (BOOL)boolForKey:(NSString *)key with3GEnabled:(BOOL)enabled;
@end

View File

@@ -0,0 +1,258 @@
/*
Connection Parameters abstraction
Copyright 2013 Thincast Technologies GmbH, Author: 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 "ConnectionParams.h"
#import "GlobalDefaults.h"
#import "EncryptionController.h"
#import "Utils.h"
#import "TSXAdditions.h"
@interface ConnectionParams (Private)
- (id)initWithConnectionParams:(ConnectionParams *)params;
@end
@implementation ConnectionParams
// Designated initializer.
- (id)initWithDictionary:(NSDictionary *)dict
{
if (!(self = [super init]))
return nil;
_connection_params = [dict mutableDeepCopy];
[self decryptPasswordForKey:@"password"];
[self decryptPasswordForKey:@"tsg_password"];
return self;
}
- (void)decryptPasswordForKey:(NSString *)key
{
if ([[_connection_params objectForKey:key] isKindOfClass:[NSData class]])
{
NSString *plaintext_password = [[[EncryptionController sharedEncryptionController]
decryptor] decryptString:[_connection_params objectForKey:key]];
[self setValue:plaintext_password forKey:key];
}
}
- (id)initWithBaseDefaultParameters
{
return [self initWithDictionary:[[NSUserDefaults standardUserDefaults]
dictionaryForKey:@"TSXDefaultComputerBookmarkSettings"]];
}
- (id)init
{
return [self initWithDictionary:[NSDictionary dictionary]];
}
- (id)initWithConnectionParams:(ConnectionParams *)params
{
return [self initWithDictionary:params->_connection_params];
}
- (void)dealloc
{
[_connection_params release];
_connection_params = nil;
[super dealloc];
}
- (id)copyWithZone:(NSZone *)zone
{
return [[ConnectionParams alloc] initWithDictionary:_connection_params];
}
- (NSString *)description
{
return [NSString stringWithFormat:@"ConnectionParams: %@", [_connection_params description]];
}
#pragma mark -
#pragma mark NSCoder
- (id)initWithCoder:(NSCoder *)decoder
{
if ([decoder containsValueForKey:@"connectionParams"])
return [self initWithDictionary:[decoder decodeObjectForKey:@"connectionParams"]];
return [self init];
}
- (void)encodeWithCoder:(NSCoder *)coder
{
NSSet *unserializable_keys = [NSSet setWithObjects:@"view", nil];
NSMutableDictionary *serializable_params =
[[NSMutableDictionary alloc] initWithCapacity:[_connection_params count]];
for (NSString *k in _connection_params)
if (([k characterAtIndex:0] != '_') && ![unserializable_keys containsObject:k])
[serializable_params setObject:[_connection_params objectForKey:k] forKey:k];
if ([serializable_params objectForKey:@"password"] != nil)
[self serializeDecryptedForKey:@"password" forParams:serializable_params];
if ([serializable_params objectForKey:@"tsg_password"] != nil)
[self serializeDecryptedForKey:@"tsg_password" forParams:serializable_params];
[coder encodeObject:serializable_params forKey:@"connectionParams"];
[serializable_params release];
}
- (void)serializeDecryptedForKey:(NSString *)key forParams:(NSMutableDictionary *)params
{
NSData *encrypted_password = [[[EncryptionController sharedEncryptionController] encryptor]
encryptString:[params objectForKey:key]];
if (encrypted_password)
[params setObject:encrypted_password forKey:key];
else
[params removeObjectForKey:key];
}
#pragma mark -
#pragma mark NSKeyValueCoding
- (void)setValue:(id)value forKey:(NSString *)key
{
[self willChangeValueForKey:key];
if (value == nil)
[self setNilValueForKey:key];
else
[_connection_params setValue:value forKey:key];
[self didChangeValueForKey:key];
}
- (void)setValue:(id)value forKeyPath:(NSString *)key
{
[self willChangeValueForKey:key];
if (value == nil)
[self setNilValueForKey:key];
else
[_connection_params setValue:value forKeyPath:key];
[self didChangeValueForKey:key];
}
- (void)setNilValueForKey:(NSString *)key
{
[_connection_params removeObjectForKey:key];
}
- (id)valueForKey:(NSString *)key
{
return [_connection_params valueForKey:key];
}
- (NSArray *)allKeys
{
return [_connection_params allKeys];
}
#pragma mark -
#pragma mark KV convenience
- (BOOL)hasValueForKey:(NSString *)key
{
return [_connection_params objectForKey:key] != nil;
}
- (void)setInt:(int)integer forKey:(NSString *)key
{
[self setValue:[NSNumber numberWithInteger:integer] forKey:key];
}
- (int)intForKey:(NSString *)key
{
return [[self valueForKey:key] intValue];
}
- (void)setBool:(BOOL)v forKey:(NSString *)key
{
[self setValue:[NSNumber numberWithBool:v] forKey:key];
}
- (BOOL)boolForKey:(NSString *)key
{
return [[_connection_params objectForKey:key] boolValue];
}
- (const char *)UTF8StringForKey:(NSString *)key
{
id val = [self valueForKey:key];
const char *str;
if ([val respondsToSelector:@selector(UTF8String)] && (str = [val UTF8String]))
return str;
return "";
}
- (NSString *)StringForKey:(NSString *)key
{
return [self valueForKey:key];
}
- (BOOL)hasValueForKeyPath:(NSString *)key
{
return [_connection_params valueForKeyPath:key] != nil;
}
- (void)setInt:(int)integer forKeyPath:(NSString *)key
{
[self setValue:[NSNumber numberWithInteger:integer] forKeyPath:key];
}
- (int)intForKeyPath:(NSString *)key
{
return [[self valueForKeyPath:key] intValue];
}
- (void)setBool:(BOOL)v forKeyPath:(NSString *)key
{
[self setValue:[NSNumber numberWithBool:v] forKeyPath:key];
}
- (BOOL)boolForKeyPath:(NSString *)key
{
return [[self valueForKeyPath:key] boolValue];
}
- (const char *)UTF8StringForKeyPath:(NSString *)key
{
id val = [self valueForKeyPath:key];
const char *str;
if ([val respondsToSelector:@selector(UTF8String)] && (str = [val UTF8String]))
return str;
return "";
}
- (NSString *)StringForKeyPath:(NSString *)key
{
return [self valueForKeyPath:key];
}
- (int)intForKey:(NSString *)key with3GEnabled:(BOOL)enabled
{
if (enabled && [self boolForKey:@"enable_3g_settings"])
return [self intForKeyPath:[NSString stringWithFormat:@"settings_3g.%@", key]];
return [self intForKeyPath:key];
}
- (BOOL)boolForKey:(NSString *)key with3GEnabled:(BOOL)enabled
{
if (enabled && [self boolForKey:@"enable_3g_settings"])
return [self boolForKeyPath:[NSString stringWithFormat:@"settings_3g.%@", key]];
return [self boolForKeyPath:key];
}
@end

View File

@@ -0,0 +1,43 @@
/*
Password Encryptor
Copyright 2013 Thincast Technologies GmbH, Author: 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/.
*/
/* Encrypts data using AES 128 with a 256 bit key derived using PBKDF2-HMAC-SHA1 */
#import <Foundation/Foundation.h>
// Encryption block cipher config
#define TSXEncryptorBlockCipherAlgo kCCAlgorithmAES128
#define TSXEncryptorBlockCipherKeySize kCCKeySizeAES256
#define TSXEncryptorBlockCipherOptions kCCOptionPKCS7Padding
#define TSXEncryptorBlockCipherBlockSize 16
// Key generation: If any of these are changed, existing password stores will no longer work
#define TSXEncryptorPBKDF2Rounds 100
#define TSXEncryptorPBKDF2Salt "9D¶3L}S¿lA[e€3C«"
#define TSXEncryptorPBKDF2SaltLen TSXEncryptorBlockCipherOptions
#define TSXEncryptorPBKDF2KeySize TSXEncryptorBlockCipherKeySize
@interface Encryptor : NSObject
{
@private
NSData *_encryption_key;
NSString *_plaintext_password;
}
@property(readonly) NSString *plaintextPassword;
- (id)initWithPassword:(NSString *)plaintext_password;
- (NSData *)encryptData:(NSData *)plaintext_data;
- (NSData *)decryptData:(NSData *)encrypted_data;
- (NSData *)encryptString:(NSString *)plaintext_string;
- (NSString *)decryptString:(NSData *)encrypted_string;
@end

View File

@@ -0,0 +1,206 @@
/*
Password Encryptor
Copyright 2013 Thincast Technologies GmbH, Author: 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/.
*/
/* We try to use CommonCrypto as much as possible. PBKDF2 was added to CommonCrypto in iOS 5, so use
* OpenSSL only as a fallback to do PBKDF2 on pre iOS 5 systems. */
#import "Encryptor.h"
#import <CommonCrypto/CommonKeyDerivation.h>
#import <CommonCrypto/CommonCryptor.h>
#import <CommonCrypto/CommonDigest.h>
#import <openssl/evp.h> // For PBKDF2 on < 5.0
#include <fcntl.h>
#pragma mark -
@interface Encryptor (Private)
- (NSData *)randomInitializationVector;
@end
@implementation Encryptor
@synthesize plaintextPassword = _plaintext_password;
- (id)initWithPassword:(NSString *)plaintext_password
{
if (plaintext_password == nil)
return nil;
if (!(self = [super init]))
return nil;
_plaintext_password = [plaintext_password retain];
const char *plaintext_password_data =
[plaintext_password length] ? [plaintext_password UTF8String] : " ";
if (!plaintext_password_data || !strlen(plaintext_password_data))
[NSException raise:NSInternalInconsistencyException
format:@"%s: plaintext password data is zero length!", __func__];
uint8_t *derived_key = calloc(1, TSXEncryptorPBKDF2KeySize);
if (CCKeyDerivationPBKDF != nullptr)
{
int ret = CCKeyDerivationPBKDF(
kCCPBKDF2, plaintext_password_data, strlen(plaintext_password_data) - 1,
(const uint8_t *)TSXEncryptorPBKDF2Salt, TSXEncryptorPBKDF2SaltLen, kCCPRFHmacAlgSHA1,
TSXEncryptorPBKDF2Rounds, derived_key, TSXEncryptorPBKDF2KeySize);
// NSLog(@"CCKeyDerivationPBKDF ret = %d; key: %@", ret, [NSData
// dataWithBytesNoCopy:derived_key length:TWEncryptorPBKDF2KeySize freeWhenDone:NO]);
if (ret)
{
NSLog(@"%s: CCKeyDerivationPBKDF ret == %d, indicating some sort of failure.", __func__,
ret);
free(derived_key);
[self autorelease];
return nil;
}
}
else
{
// iOS 4.x or earlier -- use OpenSSL
unsigned long ret = PKCS5_PBKDF2_HMAC_SHA1(
plaintext_password_data, (int)strlen(plaintext_password_data) - 1,
(const unsigned char *)TSXEncryptorPBKDF2Salt, TSXEncryptorPBKDF2SaltLen,
TSXEncryptorPBKDF2Rounds, TSXEncryptorPBKDF2KeySize, derived_key);
// NSLog(@"PKCS5_PBKDF2_HMAC_SHA1 ret = %lu; key: %@", ret, [NSData
// dataWithBytesNoCopy:derived_key length:TWEncryptorPBKDF2KeySize freeWhenDone:NO]);
if (ret != 1)
{
NSLog(@"%s: PKCS5_PBKDF2_HMAC_SHA1 ret == %lu, indicating some sort of failure.",
__func__, ret);
free(derived_key);
[self release];
return nil;
}
}
_encryption_key = [[NSData alloc] initWithBytesNoCopy:derived_key
length:TSXEncryptorPBKDF2KeySize
freeWhenDone:YES];
return self;
}
#pragma mark -
#pragma mark Encrypting/Decrypting data
- (NSData *)encryptData:(NSData *)plaintext_data
{
if (![plaintext_data length])
return nil;
NSData *iv = [self randomInitializationVector];
NSMutableData *encrypted_data = [NSMutableData
dataWithLength:[iv length] + [plaintext_data length] + TSXEncryptorBlockCipherBlockSize];
[encrypted_data replaceBytesInRange:NSMakeRange(0, [iv length]) withBytes:[iv bytes]];
size_t data_out_moved = 0;
int ret = CCCrypt(kCCEncrypt, TSXEncryptorBlockCipherAlgo, TSXEncryptorBlockCipherOptions,
[_encryption_key bytes], TSXEncryptorBlockCipherKeySize, [iv bytes],
[plaintext_data bytes], [plaintext_data length],
[encrypted_data mutableBytes] + [iv length],
[encrypted_data length] - [iv length], &data_out_moved);
switch (ret)
{
case kCCSuccess:
[encrypted_data setLength:[iv length] + data_out_moved];
return encrypted_data;
default:
NSLog(
@"%s: uncaught error, ret CCCryptorStatus = %d (plaintext len = %lu; buffer size = "
@"%lu)",
__func__, ret, (unsigned long)[plaintext_data length],
(unsigned long)([encrypted_data length] - [iv length]));
return nil;
}
return nil;
}
- (NSData *)decryptData:(NSData *)encrypted_data
{
if ([encrypted_data length] <= TSXEncryptorBlockCipherBlockSize)
return nil;
NSMutableData *plaintext_data =
[NSMutableData dataWithLength:[encrypted_data length] + TSXEncryptorBlockCipherBlockSize];
size_t data_out_moved = 0;
int ret =
CCCrypt(kCCDecrypt, TSXEncryptorBlockCipherAlgo, TSXEncryptorBlockCipherOptions,
[_encryption_key bytes], TSXEncryptorBlockCipherKeySize, [encrypted_data bytes],
[encrypted_data bytes] + TSXEncryptorBlockCipherBlockSize,
[encrypted_data length] - TSXEncryptorBlockCipherBlockSize,
[plaintext_data mutableBytes], [plaintext_data length], &data_out_moved);
switch (ret)
{
case kCCSuccess:
[plaintext_data setLength:data_out_moved];
return plaintext_data;
case kCCBufferTooSmall: // Our output buffer is big enough to decrypt valid data. This
// return code indicates malformed data.
case kCCAlignmentError: // Shouldn't get this, since we're using padding.
case kCCDecodeError: // Wrong key.
return nil;
default:
NSLog(@"%s: uncaught error, ret CCCryptorStatus = %d (encrypted data len = %lu; buffer "
@"size = %lu; dom = %lu)",
__func__, ret, (unsigned long)[encrypted_data length],
(unsigned long)[plaintext_data length], data_out_moved);
return nil;
}
return nil;
}
- (NSData *)encryptString:(NSString *)plaintext_string
{
return [self encryptData:[plaintext_string dataUsingEncoding:NSUTF8StringEncoding]];
}
- (NSString *)decryptString:(NSData *)encrypted_string
{
return [[[NSString alloc] initWithData:[self decryptData:encrypted_string]
encoding:NSUTF8StringEncoding] autorelease];
}
- (NSData *)randomInitializationVector
{
NSMutableData *iv = [NSMutableData dataWithLength:TSXEncryptorBlockCipherBlockSize];
int fd;
if ((fd = open("/dev/urandom", O_RDONLY)) < 0)
return nil;
NSInteger bytes_needed = [iv length];
char *p = [iv mutableBytes];
while (bytes_needed)
{
long bytes_read = read(fd, p, bytes_needed);
if (bytes_read < 0)
continue;
p += bytes_read;
bytes_needed -= bytes_read;
}
close(fd);
return iv;
}
@end

View File

@@ -0,0 +1,30 @@
/*
Global default bookmark settings
Copyright 2013 Thincast Technologies GmbH, Author: 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 <Foundation/Foundation.h>
@class ConnectionParams, ComputerBookmark;
@interface GlobalDefaults : NSObject
{
@private
ComputerBookmark *_default_bookmark;
}
+ (GlobalDefaults *)sharedGlobalDefaults;
// The same object is always returned from this method.
@property(readonly, nonatomic) ComputerBookmark *bookmark;
- (ConnectionParams *)newParams;
- (ComputerBookmark *)newBookmark;
- (ComputerBookmark *)newTestServerBookmark;
@end

View File

@@ -0,0 +1,88 @@
/*
Global default bookmark settings
Copyright 2013 Thincast Technologies GmbH, Author: 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 "GlobalDefaults.h"
#import "Bookmark.h"
#import "ConnectionParams.h"
@implementation GlobalDefaults
+ (GlobalDefaults *)sharedGlobalDefaults
{
static GlobalDefaults *_shared_global_defaults = nil;
if (_shared_global_defaults == nil)
{
@synchronized(self)
{
if (_shared_global_defaults == nil)
_shared_global_defaults = [[GlobalDefaults alloc] init];
}
}
return _shared_global_defaults;
}
- (id)init
{
if (!(self = [super init]))
return nil;
ComputerBookmark *bookmark = nil;
NSData *bookmark_data =
[[NSUserDefaults standardUserDefaults] objectForKey:@"TSXSharedGlobalDefaultBookmark"];
if (bookmark_data && [bookmark_data length])
bookmark = [NSKeyedUnarchiver unarchiveObjectWithData:bookmark_data];
if (!bookmark)
bookmark = [[[ComputerBookmark alloc] initWithBaseDefaultParameters] autorelease];
_default_bookmark = [bookmark retain];
return self;
}
- (void)dealloc
{
[_default_bookmark release];
[super dealloc];
}
#pragma mark -
@synthesize bookmark = _default_bookmark;
- (ComputerBookmark *)newBookmark
{
return [[ComputerBookmark alloc] initWithConnectionParameters:[[self newParams] autorelease]];
}
- (ConnectionParams *)newParams
{
ConnectionParams *param_copy = [[[self bookmark] params] copy];
return param_copy;
}
- (ComputerBookmark *)newTestServerBookmark
{
ComputerBookmark *bm = [self newBookmark];
[bm setLabel:@"Test Server"];
[[bm params] setValue:@"testservice.ifreerdp.com" forKey:@"hostname"];
[[bm params] setInt:0 forKey:@"screen_resolution_type"];
[[bm params] setInt:1024 forKey:@"width"];
[[bm params] setInt:768 forKey:@"height"];
[[bm params] setInt:32 forKey:@"colors"];
[[bm params] setBool:YES forKey:@"perf_remotefx"];
[[bm params] setBool:YES forKey:@"perf_gfx"];
[[bm params] setBool:YES forKey:@"perf_h264"];
return bm;
}
@end

View File

@@ -0,0 +1,76 @@
/*
RDP Keyboard helper
Copyright 2013 Thincast Technologies GmbH, Author: Martin Fleisz
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 <Foundation/Foundation.h>
#import "RDPSession.h"
@class RDPKeyboard;
@protocol RDPKeyboardDelegate <NSObject>
@optional
- (void)modifiersChangedForKeyboard:(RDPKeyboard *)keyboard;
@end
@interface RDPKeyboard : NSObject
{
RDPSession *_session;
int _virtual_key_map[256];
int _unicode_map[256];
NSDictionary *_special_keys;
NSObject<RDPKeyboardDelegate> *_delegate;
BOOL _ctrl_pressed;
BOOL _alt_pressed;
BOOL _shift_pressed;
BOOL _win_pressed;
}
@property(assign) id<RDPKeyboardDelegate> delegate;
@property(readonly) BOOL ctrlPressed;
@property(readonly) BOOL altPressed;
@property(readonly) BOOL shiftPressed;
@property(readonly) BOOL winPressed;
// returns a keyboard instance
+ (RDPKeyboard *)getSharedRDPKeyboard;
// init the keyboard and assign the given rdp session and delegate
- (void)initWithSession:(RDPSession *)session delegate:(NSObject<RDPKeyboardDelegate> *)delegate;
// called to reset any pending key states (i.e. pressed modifier keys)
- (void)reset;
// sends the given unicode character to the server
- (void)sendUnicode:(int)character;
// send a key stroke event using the given virtual key code
- (void)sendVirtualKeyCode:(int)keyCode;
// toggle ctrl key, returns true if pressed, otherwise false
- (void)toggleCtrlKey;
// toggle alt key, returns true if pressed, otherwise false
- (void)toggleAltKey;
// toggle shift key, returns true if pressed, otherwise false
- (void)toggleShiftKey;
// toggle windows key, returns true if pressed, otherwise false
- (void)toggleWinKey;
// send key strokes
- (void)sendEnterKeyStroke;
- (void)sendEscapeKeyStroke;
- (void)sendBackspaceKeyStroke;
@end

View File

@@ -0,0 +1,310 @@
/*
RDP Keyboard helper
Copyright 2013 Thincast Technologies GmbH, Author: Martin Fleisz
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 "RDPKeyboard.h"
#include <freerdp/locale/keyboard.h>
@interface RDPKeyboard (Private)
- (void)sendVirtualKey:(int)vKey up:(BOOL)up;
- (void)handleSpecialKey:(int)character;
- (void)handleAlphaNumChar:(int)character;
- (void)notifyDelegateModifiersChanged;
@end
@implementation RDPKeyboard
@synthesize delegate = _delegate, ctrlPressed = _ctrl_pressed, altPressed = _alt_pressed,
shiftPressed = _shift_pressed, winPressed = _win_pressed;
- (id)init
{
if ((self = [super init]) != nil)
{
[self initWithSession:nil delegate:nil];
memset(_virtual_key_map, 0, sizeof(_virtual_key_map));
memset(_unicode_map, 0, sizeof(_unicode_map));
// init vkey map - used for alpha-num characters
_virtual_key_map['0'] = VK_KEY_0;
_virtual_key_map['1'] = VK_KEY_1;
_virtual_key_map['2'] = VK_KEY_2;
_virtual_key_map['3'] = VK_KEY_3;
_virtual_key_map['4'] = VK_KEY_4;
_virtual_key_map['5'] = VK_KEY_5;
_virtual_key_map['6'] = VK_KEY_6;
_virtual_key_map['7'] = VK_KEY_7;
_virtual_key_map['8'] = VK_KEY_8;
_virtual_key_map['9'] = VK_KEY_9;
_virtual_key_map['a'] = VK_KEY_A;
_virtual_key_map['b'] = VK_KEY_B;
_virtual_key_map['c'] = VK_KEY_C;
_virtual_key_map['d'] = VK_KEY_D;
_virtual_key_map['e'] = VK_KEY_E;
_virtual_key_map['f'] = VK_KEY_F;
_virtual_key_map['g'] = VK_KEY_G;
_virtual_key_map['h'] = VK_KEY_H;
_virtual_key_map['i'] = VK_KEY_I;
_virtual_key_map['j'] = VK_KEY_J;
_virtual_key_map['k'] = VK_KEY_K;
_virtual_key_map['l'] = VK_KEY_L;
_virtual_key_map['m'] = VK_KEY_M;
_virtual_key_map['n'] = VK_KEY_N;
_virtual_key_map['o'] = VK_KEY_O;
_virtual_key_map['p'] = VK_KEY_P;
_virtual_key_map['q'] = VK_KEY_Q;
_virtual_key_map['r'] = VK_KEY_R;
_virtual_key_map['s'] = VK_KEY_S;
_virtual_key_map['t'] = VK_KEY_T;
_virtual_key_map['u'] = VK_KEY_U;
_virtual_key_map['v'] = VK_KEY_V;
_virtual_key_map['w'] = VK_KEY_W;
_virtual_key_map['x'] = VK_KEY_X;
_virtual_key_map['y'] = VK_KEY_Y;
_virtual_key_map['z'] = VK_KEY_Z;
// init scancode map - used for special characters
_unicode_map['-'] = 45;
_unicode_map['/'] = 47;
_unicode_map[':'] = 58;
_unicode_map[';'] = 59;
_unicode_map['('] = 40;
_unicode_map[')'] = 41;
_unicode_map['&'] = 38;
_unicode_map['@'] = 64;
_unicode_map['.'] = 46;
_unicode_map[','] = 44;
_unicode_map['?'] = 63;
_unicode_map['!'] = 33;
_unicode_map['\''] = 39;
_unicode_map['\"'] = 34;
_unicode_map['['] = 91;
_unicode_map[']'] = 93;
_unicode_map['{'] = 123;
_unicode_map['}'] = 125;
_unicode_map['#'] = 35;
_unicode_map['%'] = 37;
_unicode_map['^'] = 94;
_unicode_map['*'] = 42;
_unicode_map['+'] = 43;
_unicode_map['='] = 61;
_unicode_map['_'] = 95;
_unicode_map['\\'] = 92;
_unicode_map['|'] = 124;
_unicode_map['~'] = 126;
_unicode_map['<'] = 60;
_unicode_map['>'] = 62;
_unicode_map['$'] = 36;
}
return self;
}
- (void)dealloc
{
[super dealloc];
}
#pragma mark -
#pragma mark class methods
// return a keyboard instance
+ (RDPKeyboard *)getSharedRDPKeyboard
{
static RDPKeyboard *_shared_keyboard = nil;
if (_shared_keyboard == nil)
{
@synchronized(self)
{
if (_shared_keyboard == nil)
_shared_keyboard = [[RDPKeyboard alloc] init];
}
}
return _shared_keyboard;
}
// reset the keyboard instance and assign the given rdp instance
- (void)initWithSession:(RDPSession *)session delegate:(NSObject<RDPKeyboardDelegate> *)delegate
{
_alt_pressed = NO;
_ctrl_pressed = NO;
_shift_pressed = NO;
_win_pressed = NO;
_session = session;
_delegate = delegate;
}
- (void)reset
{
// reset pressed ctrl, alt, shift or win key
if (_shift_pressed)
[self toggleShiftKey];
if (_alt_pressed)
[self toggleAltKey];
if (_ctrl_pressed)
[self toggleCtrlKey];
if (_win_pressed)
[self toggleWinKey];
}
// handles button pressed input event from the iOS keyboard
// performs all conversions etc.
- (void)sendUnicode:(int)character
{
if (isalnum(character))
[self handleAlphaNumChar:character];
else
[self handleSpecialKey:character];
[self reset];
}
// send a backspace key press
- (void)sendVirtualKeyCode:(int)keyCode
{
[self sendVirtualKey:keyCode up:NO];
[self sendVirtualKey:keyCode up:YES];
}
#pragma mark modifier key handling
// toggle ctrl key, returns true if pressed, otherwise false
- (void)toggleCtrlKey
{
[self sendVirtualKey:VK_LCONTROL up:_ctrl_pressed];
_ctrl_pressed = !_ctrl_pressed;
[self notifyDelegateModifiersChanged];
}
// toggle alt key, returns true if pressed, otherwise false
- (void)toggleAltKey
{
[self sendVirtualKey:VK_LMENU up:_alt_pressed];
_alt_pressed = !_alt_pressed;
[self notifyDelegateModifiersChanged];
}
// toggle shift key, returns true if pressed, otherwise false
- (void)toggleShiftKey
{
[self sendVirtualKey:VK_LSHIFT up:_shift_pressed];
_shift_pressed = !_shift_pressed;
[self notifyDelegateModifiersChanged];
}
// toggle windows key, returns true if pressed, otherwise false
- (void)toggleWinKey
{
[self sendVirtualKey:(VK_LWIN | KBDEXT) up:_win_pressed];
_win_pressed = !_win_pressed;
[self notifyDelegateModifiersChanged];
}
#pragma mark Sending special key strokes
- (void)sendEnterKeyStroke
{
[self sendVirtualKeyCode:(VK_RETURN | KBDEXT)];
}
- (void)sendEscapeKeyStroke
{
[self sendVirtualKeyCode:VK_ESCAPE];
}
- (void)sendBackspaceKeyStroke
{
[self sendVirtualKeyCode:VK_BACK];
}
@end
#pragma mark -
@implementation RDPKeyboard (Private)
- (void)handleAlphaNumChar:(int)character
{
// if we receive an uppercase letter - make it lower and send an shift down event to server
BOOL shift_was_sent = NO;
if (isupper(character) && _shift_pressed == NO)
{
character = tolower(character);
[self sendVirtualKey:VK_LSHIFT up:NO];
shift_was_sent = YES;
}
// convert the character to a VK
int vk = _virtual_key_map[character];
if (vk != 0)
{
// send key pressed
[self sendVirtualKey:vk up:NO];
[self sendVirtualKey:vk up:YES];
}
// send the missing shift up if we had a shift down
if (shift_was_sent)
[self sendVirtualKey:VK_LSHIFT up:YES];
}
- (void)handleSpecialKey:(int)character
{
NSDictionary *eventDescriptor = nil;
if (character < 256)
{
// convert the character to a unicode character
int code = _unicode_map[character];
if (code != 0)
eventDescriptor = [NSDictionary
dictionaryWithObjectsAndKeys:@"keyboard", @"type", @"unicode", @"subtype",
[NSNumber numberWithUnsignedShort:0], @"flags",
[NSNumber numberWithUnsignedShort:code],
@"unicode_char", nil];
}
if (eventDescriptor == nil)
eventDescriptor = [NSDictionary
dictionaryWithObjectsAndKeys:@"keyboard", @"type", @"unicode", @"subtype",
[NSNumber numberWithUnsignedShort:0], @"flags",
[NSNumber numberWithUnsignedShort:character],
@"unicode_char", nil];
[_session sendInputEvent:eventDescriptor];
}
// sends the vk code to the session
- (void)sendVirtualKey:(int)vKey up:(BOOL)up
{
DWORD scancode = GetVirtualScanCodeFromVirtualKeyCode(vKey, 4);
int flags = (up ? KBD_FLAGS_RELEASE : KBD_FLAGS_DOWN);
flags |= ((scancode & KBDEXT) ? KBD_FLAGS_EXTENDED : 0);
[_session
sendInputEvent:[NSDictionary
dictionaryWithObjectsAndKeys:@"keyboard", @"type", @"scancode",
@"subtype",
[NSNumber numberWithUnsignedShort:flags],
@"flags",
[NSNumber
numberWithUnsignedShort:(scancode &
0xFF)],
@"scancode", nil]];
}
- (void)notifyDelegateModifiersChanged
{
if ([[self delegate] respondsToSelector:@selector(modifiersChangedForKeyboard:)])
[[self delegate] modifiersChangedForKeyboard:self];
}
@end

View File

@@ -0,0 +1,109 @@
/*
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 <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#include <freerdp/freerdp.h>
// forward declaration
@class RDPSession;
@class ComputerBookmark;
@class ConnectionParams;
// notification handler for session disconnect
extern NSString *TSXSessionDidDisconnectNotification;
extern NSString *TSXSessionDidFailToConnectNotification;
// protocol for session notifications
@protocol RDPSessionDelegate <NSObject>
@optional
- (void)session:(RDPSession *)session didFailToConnect:(int)reason;
- (void)sessionWillConnect:(RDPSession *)session;
- (void)sessionDidConnect:(RDPSession *)session;
- (void)sessionWillDisconnect:(RDPSession *)session;
- (void)sessionDidDisconnect:(RDPSession *)session;
- (void)sessionBitmapContextWillChange:(RDPSession *)session;
- (void)sessionBitmapContextDidChange:(RDPSession *)session;
- (void)session:(RDPSession *)session needsRedrawInRect:(CGRect)rect;
- (CGSize)sizeForFitScreenForSession:(RDPSession *)session;
- (void)session:(RDPSession *)session
requestsAuthenticationWithParams:(NSMutableDictionary *)params;
- (void)session:(RDPSession *)session verifyCertificateWithParams:(NSMutableDictionary *)params;
@end
// rdp session
@interface RDPSession : NSObject
{
@private
freerdp *_freerdp;
ComputerBookmark *_bookmark;
ConnectionParams *_params;
NSObject<RDPSessionDelegate> *_delegate;
NSCondition *_ui_request_completed;
NSString *_name;
// flag if the session is suspended
BOOL _suspended;
// flag that specifies whether the RDP toolbar is visible
BOOL _toolbar_visible;
}
@property(readonly) ConnectionParams *params;
@property(readonly) ComputerBookmark *bookmark;
@property(assign) id<RDPSessionDelegate> delegate;
@property(assign) BOOL toolbarVisible;
@property(readonly) CGContextRef bitmapContext;
@property(readonly) NSCondition *uiRequestCompleted;
// initialize a new session with the given bookmark
- (id)initWithBookmark:(ComputerBookmark *)bookmark;
#pragma mark - session control functions
// connect the session
- (void)connect;
// disconnect session
- (void)disconnect;
// suspends the session
- (void)suspend;
// resumes a previously suspended session
- (void)resume;
// returns YES if the session is started
- (BOOL)isSuspended;
// send input event to the server
- (void)sendInputEvent:(NSDictionary *)event;
// session needs a refresh of its view
- (void)setNeedsDisplayInRectAsValue:(NSValue *)rect_value;
// get a small session screenshot
- (UIImage *)getScreenshotWithSize:(CGSize)size;
// returns the session's current parameters
- (rdpSettings *)getSessionParams;
// returns the session's name (usually the label of the bookmark the session was created with)
- (NSString *)sessionName;
@end

View File

@@ -0,0 +1,534 @@
/*
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 = WINPR_C_ARRAY_INIT;
char *arg = nullptr;
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)
{
for (int i = 0; i < argc; i++)
free(argv[i]);
free(argv);
}
// Designated initializer.
- (id)initWithBookmark:(ComputerBookmark *)bookmark
{
int status;
char **argv = nullptr;
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->context->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 width or height are still 0
rdpSettings *settings = _freerdp->context->settings;
if (freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth) == 0 ||
freerdp_settings_get_uint32(settings, FreeRDP_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"];
(void)freerdp_settings_set_uint32(settings, FreeRDP_DesktopWidth, size.width);
(void)freerdp_settings_set_uint32(settings, FreeRDP_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 (freerdp_settings_get_uint32(settings, FreeRDP_ColorDepth) <= 16)
{
const UINT32 w = freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth) & (~1);
(void)freerdp_settings_set_uint32(settings, FreeRDP_DesktopWidth, w);
}
[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, nullptr);
}
}
// resumes a previously suspended session
- (void)resume
{
if (_suspended)
{
/* RECTANGLE_16 rec;
rec.left = 0;
rec.top = 0;
rec.right = freerdp_settings_get_uint32(instance->settings, FreeRDP_DesktopWidth);
rec.bottom = freerdp_settings_get_uint32(instance->settings, FreeRDP_DesktopHeight);
*/
_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->context->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