Milestone 5: deliver embedded RDP sessions and lifecycle hardening
This commit is contained in:
34
third_party/FreeRDP/client/iOS/FreeRDP/ios_cliprdr.h
vendored
Normal file
34
third_party/FreeRDP/client/iOS/FreeRDP/ios_cliprdr.h
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* Android Clipboard Redirection
|
||||
*
|
||||
* Copyright 2013 Felix Long
|
||||
* Copyright 2023 Iordan Iordanov
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef FREERDP_CLIENT_IOS_CLIPRDR_H
|
||||
#define FREERDP_CLIENT_IOS_CLIPRDR_H
|
||||
|
||||
#include <freerdp/client/cliprdr.h>
|
||||
#include <freerdp/api.h>
|
||||
|
||||
#include "ios_freerdp.h"
|
||||
|
||||
FREERDP_LOCAL UINT ios_cliprdr_send_client_format_list(CliprdrClientContext* cliprdr);
|
||||
|
||||
FREERDP_LOCAL BOOL ios_cliprdr_init(mfContext* context, CliprdrClientContext* cliprdr);
|
||||
FREERDP_LOCAL BOOL ios_cliprdr_uninit(mfContext* context, CliprdrClientContext* cliprdr);
|
||||
|
||||
#endif /* FREERDP_CLIENT_IOS_CLIPRDR_H */
|
||||
499
third_party/FreeRDP/client/iOS/FreeRDP/ios_cliprdr.m
vendored
Normal file
499
third_party/FreeRDP/client/iOS/FreeRDP/ios_cliprdr.m
vendored
Normal file
@@ -0,0 +1,499 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* Android Clipboard Redirection
|
||||
*
|
||||
* Copyright 2013 Felix Long
|
||||
* Copyright 2015 Thincast Technologies GmbH
|
||||
* Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com>
|
||||
* Copyright 2023 Iordan Iordanov
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <freerdp/config.h>
|
||||
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/stream.h>
|
||||
#include <winpr/clipboard.h>
|
||||
|
||||
#include <freerdp/client/channels.h>
|
||||
#include <freerdp/client/cliprdr.h>
|
||||
|
||||
#include "ios_cliprdr.h"
|
||||
|
||||
UINT ios_cliprdr_send_client_format_list(CliprdrClientContext *cliprdr)
|
||||
{
|
||||
UINT rc = ERROR_INTERNAL_ERROR;
|
||||
UINT32 formatId;
|
||||
UINT32 numFormats;
|
||||
UINT32 *pFormatIds;
|
||||
const char *formatName;
|
||||
CLIPRDR_FORMAT *formats;
|
||||
CLIPRDR_FORMAT_LIST formatList = WINPR_C_ARRAY_INIT;
|
||||
|
||||
if (!cliprdr)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
mfContext *afc = (mfContext *)cliprdr->custom;
|
||||
|
||||
if (!afc || !afc->cliprdr)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
pFormatIds = nullptr;
|
||||
numFormats = ClipboardGetFormatIds(afc->clipboard, &pFormatIds);
|
||||
formats = (CLIPRDR_FORMAT *)calloc(numFormats, sizeof(CLIPRDR_FORMAT));
|
||||
|
||||
if (!formats)
|
||||
goto fail;
|
||||
|
||||
for (UINT32 index = 0; index < numFormats; index++)
|
||||
{
|
||||
formatId = pFormatIds[index];
|
||||
formatName = ClipboardGetFormatName(afc->clipboard, formatId);
|
||||
formats[index].formatId = formatId;
|
||||
formats[index].formatName = nullptr;
|
||||
|
||||
if ((formatId > CF_MAX) && formatName)
|
||||
{
|
||||
formats[index].formatName = _strdup(formatName);
|
||||
|
||||
if (!formats[index].formatName)
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
formatList.common.msgFlags = 0;
|
||||
formatList.numFormats = numFormats;
|
||||
formatList.formats = formats;
|
||||
formatList.common.msgType = CB_FORMAT_LIST;
|
||||
|
||||
if (!afc->cliprdr->ClientFormatList)
|
||||
goto fail;
|
||||
|
||||
rc = afc->cliprdr->ClientFormatList(afc->cliprdr, &formatList);
|
||||
fail:
|
||||
free(pFormatIds);
|
||||
free(formats);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static UINT ios_cliprdr_send_client_format_data_request(CliprdrClientContext *cliprdr,
|
||||
UINT32 formatId)
|
||||
{
|
||||
UINT rc = ERROR_INVALID_PARAMETER;
|
||||
CLIPRDR_FORMAT_DATA_REQUEST formatDataRequest = WINPR_C_ARRAY_INIT;
|
||||
mfContext *afc;
|
||||
|
||||
if (!cliprdr)
|
||||
goto fail;
|
||||
|
||||
afc = (mfContext *)cliprdr->custom;
|
||||
|
||||
if (!afc || !afc->clipboardRequestEvent || !cliprdr->ClientFormatDataRequest)
|
||||
goto fail;
|
||||
|
||||
formatDataRequest.common.msgType = CB_FORMAT_DATA_REQUEST;
|
||||
formatDataRequest.common.msgFlags = 0;
|
||||
formatDataRequest.requestedFormatId = formatId;
|
||||
afc->requestedFormatId = formatId;
|
||||
(void)ResetEvent(afc->clipboardRequestEvent);
|
||||
rc = cliprdr->ClientFormatDataRequest(cliprdr, &formatDataRequest);
|
||||
fail:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static UINT ios_cliprdr_send_client_capabilities(CliprdrClientContext *cliprdr)
|
||||
{
|
||||
CLIPRDR_CAPABILITIES capabilities;
|
||||
CLIPRDR_GENERAL_CAPABILITY_SET generalCapabilitySet;
|
||||
|
||||
if (!cliprdr || !cliprdr->ClientCapabilities)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
capabilities.cCapabilitiesSets = 1;
|
||||
capabilities.capabilitySets = (CLIPRDR_CAPABILITY_SET *)&(generalCapabilitySet);
|
||||
generalCapabilitySet.capabilitySetType = CB_CAPSTYPE_GENERAL;
|
||||
generalCapabilitySet.capabilitySetLength = 12;
|
||||
generalCapabilitySet.version = CB_CAPS_VERSION_2;
|
||||
generalCapabilitySet.generalFlags = CB_USE_LONG_FORMAT_NAMES;
|
||||
return cliprdr->ClientCapabilities(cliprdr, &capabilities);
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT ios_cliprdr_monitor_ready(CliprdrClientContext *cliprdr,
|
||||
const CLIPRDR_MONITOR_READY *monitorReady)
|
||||
{
|
||||
UINT rc;
|
||||
mfContext *afc;
|
||||
|
||||
if (!cliprdr || !monitorReady)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
afc = (mfContext *)cliprdr->custom;
|
||||
|
||||
if (!afc)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
if ((rc = ios_cliprdr_send_client_capabilities(cliprdr)) != CHANNEL_RC_OK)
|
||||
return rc;
|
||||
|
||||
if ((rc = ios_cliprdr_send_client_format_list(cliprdr)) != CHANNEL_RC_OK)
|
||||
return rc;
|
||||
|
||||
afc->clipboardSync = TRUE;
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT ios_cliprdr_server_capabilities(CliprdrClientContext *cliprdr,
|
||||
const CLIPRDR_CAPABILITIES *capabilities)
|
||||
{
|
||||
CLIPRDR_CAPABILITY_SET *capabilitySet;
|
||||
mfContext *afc;
|
||||
|
||||
if (!cliprdr || !capabilities)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
afc = (mfContext *)cliprdr->custom;
|
||||
|
||||
if (!afc)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
for (UINT32 index = 0; index < capabilities->cCapabilitiesSets; index++)
|
||||
{
|
||||
capabilitySet = &(capabilities->capabilitySets[index]);
|
||||
|
||||
if ((capabilitySet->capabilitySetType == CB_CAPSTYPE_GENERAL) &&
|
||||
(capabilitySet->capabilitySetLength >= CB_CAPSTYPE_GENERAL_LEN))
|
||||
{
|
||||
CLIPRDR_GENERAL_CAPABILITY_SET *generalCapabilitySet =
|
||||
(CLIPRDR_GENERAL_CAPABILITY_SET *)capabilitySet;
|
||||
afc->clipboardCapabilities = generalCapabilitySet->generalFlags;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT ios_cliprdr_server_format_list(CliprdrClientContext *cliprdr,
|
||||
const CLIPRDR_FORMAT_LIST *formatList)
|
||||
{
|
||||
UINT rc;
|
||||
CLIPRDR_FORMAT *format;
|
||||
mfContext *afc;
|
||||
|
||||
if (!cliprdr || !formatList)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
afc = (mfContext *)cliprdr->custom;
|
||||
|
||||
if (!afc)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
if (afc->serverFormats)
|
||||
{
|
||||
for (UINT32 index = 0; index < afc->numServerFormats; index++)
|
||||
free(afc->serverFormats[index].formatName);
|
||||
|
||||
free(afc->serverFormats);
|
||||
afc->serverFormats = nullptr;
|
||||
afc->numServerFormats = 0;
|
||||
}
|
||||
|
||||
if (formatList->numFormats < 1)
|
||||
return CHANNEL_RC_OK;
|
||||
|
||||
afc->numServerFormats = formatList->numFormats;
|
||||
afc->serverFormats = (CLIPRDR_FORMAT *)calloc(afc->numServerFormats, sizeof(CLIPRDR_FORMAT));
|
||||
|
||||
if (!afc->serverFormats)
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
|
||||
for (UINT32 index = 0; index < afc->numServerFormats; index++)
|
||||
{
|
||||
afc->serverFormats[index].formatId = formatList->formats[index].formatId;
|
||||
afc->serverFormats[index].formatName = nullptr;
|
||||
|
||||
if (formatList->formats[index].formatName)
|
||||
{
|
||||
afc->serverFormats[index].formatName = _strdup(formatList->formats[index].formatName);
|
||||
|
||||
if (!afc->serverFormats[index].formatName)
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
}
|
||||
|
||||
BOOL unicode = FALSE;
|
||||
BOOL text = FALSE;
|
||||
for (UINT32 index = 0; index < afc->numServerFormats; index++)
|
||||
{
|
||||
format = &(afc->serverFormats[index]);
|
||||
|
||||
if (format->formatId == CF_UNICODETEXT)
|
||||
unicode = TRUE;
|
||||
|
||||
else if (format->formatId == CF_TEXT)
|
||||
text = TRUE;
|
||||
}
|
||||
|
||||
if (unicode)
|
||||
return ios_cliprdr_send_client_format_data_request(cliprdr, CF_UNICODETEXT);
|
||||
if (text)
|
||||
return ios_cliprdr_send_client_format_data_request(cliprdr, CF_TEXT);
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT
|
||||
ios_cliprdr_server_format_list_response(CliprdrClientContext *cliprdr,
|
||||
const CLIPRDR_FORMAT_LIST_RESPONSE *formatListResponse)
|
||||
{
|
||||
if (!cliprdr || !formatListResponse)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT
|
||||
ios_cliprdr_server_lock_clipboard_data(CliprdrClientContext *cliprdr,
|
||||
const CLIPRDR_LOCK_CLIPBOARD_DATA *lockClipboardData)
|
||||
{
|
||||
if (!cliprdr || !lockClipboardData)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT
|
||||
ios_cliprdr_server_unlock_clipboard_data(CliprdrClientContext *cliprdr,
|
||||
const CLIPRDR_UNLOCK_CLIPBOARD_DATA *unlockClipboardData)
|
||||
{
|
||||
if (!cliprdr || !unlockClipboardData)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT
|
||||
ios_cliprdr_server_format_data_request(CliprdrClientContext *cliprdr,
|
||||
const CLIPRDR_FORMAT_DATA_REQUEST *formatDataRequest)
|
||||
{
|
||||
UINT rc;
|
||||
BYTE *data;
|
||||
UINT32 size;
|
||||
UINT32 formatId;
|
||||
CLIPRDR_FORMAT_DATA_RESPONSE response = WINPR_C_ARRAY_INIT;
|
||||
mfContext *afc;
|
||||
|
||||
if (!cliprdr || !formatDataRequest || !cliprdr->ClientFormatDataResponse)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
afc = (mfContext *)cliprdr->custom;
|
||||
|
||||
if (!afc)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
formatId = formatDataRequest->requestedFormatId;
|
||||
data = (BYTE *)ClipboardGetData(afc->clipboard, formatId, &size);
|
||||
response.common.msgFlags = CB_RESPONSE_OK;
|
||||
response.common.dataLen = size;
|
||||
response.requestedFormatData = data;
|
||||
|
||||
if (!data)
|
||||
{
|
||||
response.common.msgFlags = CB_RESPONSE_FAIL;
|
||||
response.common.dataLen = 0;
|
||||
response.requestedFormatData = nullptr;
|
||||
}
|
||||
|
||||
rc = cliprdr->ClientFormatDataResponse(cliprdr, &response);
|
||||
free(data);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT
|
||||
ios_cliprdr_server_format_data_response(CliprdrClientContext *cliprdr,
|
||||
const CLIPRDR_FORMAT_DATA_RESPONSE *formatDataResponse)
|
||||
{
|
||||
BYTE *data;
|
||||
UINT32 size;
|
||||
UINT32 formatId;
|
||||
CLIPRDR_FORMAT *format = nullptr;
|
||||
mfContext *afc;
|
||||
freerdp *instance;
|
||||
|
||||
if (!cliprdr || !formatDataResponse)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
afc = (mfContext *)cliprdr->custom;
|
||||
|
||||
if (!afc)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
instance = ((rdpContext *)afc)->instance;
|
||||
|
||||
if (!instance)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
for (UINT32 index = 0; index < afc->numServerFormats; index++)
|
||||
{
|
||||
if (afc->requestedFormatId == afc->serverFormats[index].formatId)
|
||||
format = &(afc->serverFormats[index]);
|
||||
}
|
||||
|
||||
if (!format)
|
||||
{
|
||||
(void)SetEvent(afc->clipboardRequestEvent);
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
if (format->formatName)
|
||||
formatId = ClipboardRegisterFormat(afc->clipboard, format->formatName);
|
||||
else
|
||||
formatId = format->formatId;
|
||||
|
||||
size = formatDataResponse->common.dataLen;
|
||||
|
||||
ClipboardLock(afc->clipboard);
|
||||
if (!ClipboardSetData(afc->clipboard, formatId, formatDataResponse->requestedFormatData, size))
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
|
||||
(void)SetEvent(afc->clipboardRequestEvent);
|
||||
|
||||
if ((formatId == CF_TEXT) || (formatId == CF_UNICODETEXT))
|
||||
{
|
||||
formatId = ClipboardRegisterFormat(afc->clipboard, "UTF8_STRING");
|
||||
data = (BYTE *)ClipboardGetData(afc->clipboard, formatId, &size);
|
||||
size = (UINT32)strnlen(data, size);
|
||||
if (afc->ServerCutText != nullptr)
|
||||
{
|
||||
afc->ServerCutText((rdpContext *)afc, (uint8_t *)data, size);
|
||||
}
|
||||
}
|
||||
ClipboardUnlock(afc->clipboard);
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT
|
||||
ios_cliprdr_server_file_contents_request(CliprdrClientContext *cliprdr,
|
||||
const CLIPRDR_FILE_CONTENTS_REQUEST *fileContentsRequest)
|
||||
{
|
||||
if (!cliprdr || !fileContentsRequest)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT ios_cliprdr_server_file_contents_response(
|
||||
CliprdrClientContext *cliprdr, const CLIPRDR_FILE_CONTENTS_RESPONSE *fileContentsResponse)
|
||||
{
|
||||
if (!cliprdr || !fileContentsResponse)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
BOOL ios_cliprdr_init(mfContext *afc, CliprdrClientContext *cliprdr)
|
||||
{
|
||||
wClipboard *clipboard;
|
||||
HANDLE hevent;
|
||||
|
||||
if (!afc || !cliprdr)
|
||||
return FALSE;
|
||||
|
||||
if (!(hevent = CreateEvent(nullptr, TRUE, FALSE, nullptr)))
|
||||
return FALSE;
|
||||
|
||||
if (!(clipboard = ClipboardCreate()))
|
||||
{
|
||||
(void)CloseHandle(hevent);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
afc->cliprdr = cliprdr;
|
||||
afc->clipboard = clipboard;
|
||||
afc->clipboardRequestEvent = hevent;
|
||||
cliprdr->custom = (void *)afc;
|
||||
cliprdr->MonitorReady = ios_cliprdr_monitor_ready;
|
||||
cliprdr->ServerCapabilities = ios_cliprdr_server_capabilities;
|
||||
cliprdr->ServerFormatList = ios_cliprdr_server_format_list;
|
||||
cliprdr->ServerFormatListResponse = ios_cliprdr_server_format_list_response;
|
||||
cliprdr->ServerLockClipboardData = ios_cliprdr_server_lock_clipboard_data;
|
||||
cliprdr->ServerUnlockClipboardData = ios_cliprdr_server_unlock_clipboard_data;
|
||||
cliprdr->ServerFormatDataRequest = ios_cliprdr_server_format_data_request;
|
||||
cliprdr->ServerFormatDataResponse = ios_cliprdr_server_format_data_response;
|
||||
cliprdr->ServerFileContentsRequest = ios_cliprdr_server_file_contents_request;
|
||||
cliprdr->ServerFileContentsResponse = ios_cliprdr_server_file_contents_response;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL ios_cliprdr_uninit(mfContext *afc, CliprdrClientContext *cliprdr)
|
||||
{
|
||||
if (!afc || !cliprdr)
|
||||
return FALSE;
|
||||
|
||||
cliprdr->custom = nullptr;
|
||||
afc->cliprdr = nullptr;
|
||||
ClipboardDestroy(afc->clipboard);
|
||||
(void)CloseHandle(afc->clipboardRequestEvent);
|
||||
return TRUE;
|
||||
}
|
||||
87
third_party/FreeRDP/client/iOS/FreeRDP/ios_freerdp.h
vendored
Normal file
87
third_party/FreeRDP/client/iOS/FreeRDP/ios_freerdp.h
vendored
Normal file
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
RDP run-loop
|
||||
|
||||
Copyright 2013 Thincast Technologies GmbH, Authors: Martin Fleisz, Dorian Johnson
|
||||
Copyright 2023 Iordan Iordanov
|
||||
|
||||
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 <CoreGraphics/CoreGraphics.h>
|
||||
|
||||
#import <freerdp/freerdp.h>
|
||||
#import <freerdp/channels/channels.h>
|
||||
#import "TSXTypes.h"
|
||||
#import <winpr/clipboard.h>
|
||||
#import <freerdp/client/cliprdr.h>
|
||||
|
||||
@class RDPSession, RDPSessionView;
|
||||
|
||||
typedef BOOL (*pServerCutText)(rdpContext *context, UINT8 *data, UINT32 size);
|
||||
|
||||
// FreeRDP extended structs
|
||||
typedef struct mf_info mfInfo;
|
||||
|
||||
typedef struct mf_context
|
||||
{
|
||||
rdpContext _p;
|
||||
|
||||
mfInfo *mfi;
|
||||
rdpSettings *settings;
|
||||
|
||||
BOOL clipboardSync;
|
||||
wClipboard *clipboard;
|
||||
UINT32 numServerFormats;
|
||||
UINT32 requestedFormatId;
|
||||
HANDLE clipboardRequestEvent;
|
||||
CLIPRDR_FORMAT *serverFormats;
|
||||
CliprdrClientContext *cliprdr;
|
||||
UINT32 clipboardCapabilities;
|
||||
WINPR_ATTR_NODISCARD pServerCutText ServerCutText;
|
||||
} mfContext;
|
||||
|
||||
struct mf_info
|
||||
{
|
||||
// RDP
|
||||
freerdp *instance;
|
||||
mfContext *context;
|
||||
rdpContext *_context;
|
||||
|
||||
// UI
|
||||
RDPSession *session;
|
||||
|
||||
// Graphics
|
||||
CGContextRef bitmap_context;
|
||||
|
||||
// Events
|
||||
int event_pipe_producer;
|
||||
int event_pipe_consumer;
|
||||
HANDLE handle;
|
||||
|
||||
// Tracking connection state
|
||||
volatile TSXConnectionState connection_state;
|
||||
volatile BOOL
|
||||
unwanted; // set when controlling Session no longer wants the connection to continue
|
||||
};
|
||||
|
||||
#define MFI_FROM_INSTANCE(inst) (((mfContext *)((inst)->context))->mfi)
|
||||
|
||||
enum MF_EXIT_CODE
|
||||
{
|
||||
MF_EXIT_SUCCESS = 0,
|
||||
|
||||
MF_EXIT_CONN_FAILED = 128,
|
||||
MF_EXIT_CONN_CANCELED = 129,
|
||||
MF_EXIT_LOGON_TIMEOUT = 130,
|
||||
|
||||
MF_EXIT_UNKNOWN = 255
|
||||
};
|
||||
|
||||
void ios_init_freerdp(void);
|
||||
void ios_uninit_freerdp(void);
|
||||
freerdp *ios_freerdp_new(void);
|
||||
int ios_run_freerdp(freerdp *instance);
|
||||
void ios_freerdp_free(freerdp *instance);
|
||||
void ios_send_clipboard_data(void *context, const void *data, UINT32 size);
|
||||
443
third_party/FreeRDP/client/iOS/FreeRDP/ios_freerdp.m
vendored
Normal file
443
third_party/FreeRDP/client/iOS/FreeRDP/ios_freerdp.m
vendored
Normal file
@@ -0,0 +1,443 @@
|
||||
/*
|
||||
RDP run-loop
|
||||
|
||||
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/.
|
||||
*/
|
||||
|
||||
#include <winpr/assert.h>
|
||||
#import <winpr/clipboard.h>
|
||||
|
||||
#import <freerdp/gdi/gdi.h>
|
||||
#import <freerdp/channels/channels.h>
|
||||
#import <freerdp/client/channels.h>
|
||||
#import <freerdp/client/cmdline.h>
|
||||
#import <freerdp/freerdp.h>
|
||||
#import <freerdp/gdi/gfx.h>
|
||||
#import <freerdp/client/cliprdr.h>
|
||||
|
||||
#import "ios_freerdp.h"
|
||||
#import "ios_freerdp_ui.h"
|
||||
#import "ios_freerdp_events.h"
|
||||
#import "ios_cliprdr.h"
|
||||
|
||||
#import "RDPSession.h"
|
||||
#import "Utils.h"
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#define TAG FREERDP_TAG("iOS")
|
||||
|
||||
#pragma mark Connection helpers
|
||||
|
||||
static void ios_OnChannelConnectedEventHandler(void *context, const ChannelConnectedEventArgs *e)
|
||||
{
|
||||
WLog_INFO(TAG, "ios_OnChannelConnectedEventHandler, channel %s", e->name);
|
||||
rdpSettings *settings;
|
||||
mfContext *afc;
|
||||
|
||||
if (!context || !e)
|
||||
{
|
||||
WLog_FATAL(TAG, "(context=%p, EventArgs=%p", context, (void *)e);
|
||||
return;
|
||||
}
|
||||
|
||||
afc = (mfContext *)context;
|
||||
settings = afc->_p.settings;
|
||||
|
||||
if (strcmp(e->name, RDPGFX_DVC_CHANNEL_NAME) == 0)
|
||||
{
|
||||
if (freerdp_settings_get_bool(settings, FreeRDP_SoftwareGdi))
|
||||
{
|
||||
gdi_graphics_pipeline_init(afc->_p.gdi, (RdpgfxClientContext *)e->pInterface);
|
||||
}
|
||||
else
|
||||
{
|
||||
WLog_WARN(TAG, "GFX without software GDI requested. "
|
||||
" This is not supported, add /gdi:sw");
|
||||
}
|
||||
}
|
||||
else if (strcmp(e->name, CLIPRDR_SVC_CHANNEL_NAME) == 0)
|
||||
{
|
||||
ios_cliprdr_init(afc, (CliprdrClientContext *)e->pInterface);
|
||||
}
|
||||
}
|
||||
|
||||
static void ios_OnChannelDisconnectedEventHandler(void *context,
|
||||
const ChannelDisconnectedEventArgs *e)
|
||||
{
|
||||
WLog_INFO(TAG, "ios_OnChannelConnectedEventHandler, channel %s", e->name);
|
||||
rdpSettings *settings;
|
||||
mfContext *afc;
|
||||
|
||||
if (!context || !e)
|
||||
{
|
||||
WLog_FATAL(TAG, "(context=%p, EventArgs=%p", context, (void *)e);
|
||||
return;
|
||||
}
|
||||
|
||||
afc = (mfContext *)context;
|
||||
settings = afc->_p.settings;
|
||||
|
||||
if (strcmp(e->name, RDPGFX_DVC_CHANNEL_NAME) == 0)
|
||||
{
|
||||
if (freerdp_settings_get_bool(settings, FreeRDP_SoftwareGdi))
|
||||
{
|
||||
gdi_graphics_pipeline_uninit(afc->_p.gdi, (RdpgfxClientContext *)e->pInterface);
|
||||
}
|
||||
else
|
||||
{
|
||||
WLog_WARN(TAG, "GFX without software GDI requested. "
|
||||
" This is not supported, add /gdi:sw");
|
||||
}
|
||||
}
|
||||
else if (strcmp(e->name, CLIPRDR_SVC_CHANNEL_NAME) == 0)
|
||||
{
|
||||
ios_cliprdr_uninit(afc, (CliprdrClientContext *)e->pInterface);
|
||||
}
|
||||
}
|
||||
|
||||
static BOOL ios_pre_connect(freerdp *instance)
|
||||
{
|
||||
int rc;
|
||||
rdpSettings *settings;
|
||||
|
||||
if (!instance || !instance->context)
|
||||
return FALSE;
|
||||
|
||||
settings = instance->context->settings;
|
||||
WINPR_ASSERT(settings);
|
||||
|
||||
const char *Password = freerdp_settings_get_string(settings, FreeRDP_Password);
|
||||
if (!freerdp_settings_set_bool(settings, FreeRDP_AutoLogonEnabled,
|
||||
Password && (Password && (strlen(Password) > 0))))
|
||||
return FALSE;
|
||||
|
||||
// Verify screen width/height are sane
|
||||
if ((freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth) < 64) ||
|
||||
(freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight) < 64) ||
|
||||
(freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth) > 4096) ||
|
||||
(freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight) > 4096))
|
||||
{
|
||||
NSLog(@"%s: invalid dimensions %d %d", __func__,
|
||||
freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth),
|
||||
freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
rc = PubSub_SubscribeChannelConnected(instance->context->pubSub,
|
||||
ios_OnChannelConnectedEventHandler);
|
||||
|
||||
if (rc != CHANNEL_RC_OK)
|
||||
{
|
||||
WLog_ERR(TAG, "Could not subscribe to connect event handler [%l08X]", rc);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
rc = PubSub_SubscribeChannelDisconnected(instance->context->pubSub,
|
||||
ios_OnChannelDisconnectedEventHandler);
|
||||
|
||||
if (rc != CHANNEL_RC_OK)
|
||||
{
|
||||
WLog_ERR(TAG, "Could not subscribe to disconnect event handler [%l08X]", rc);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!freerdp_client_load_addins(instance->context->channels, settings))
|
||||
{
|
||||
WLog_ERR(TAG, "Failed to load addins [%l08X]", GetLastError());
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL ios_Pointer_New(rdpContext *context, rdpPointer *pointer)
|
||||
{
|
||||
if (!context || !pointer || !context->gdi)
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void ios_Pointer_Free(rdpContext *context, rdpPointer *pointer)
|
||||
{
|
||||
if (!context || !pointer)
|
||||
return;
|
||||
}
|
||||
|
||||
static BOOL ios_Pointer_Set(rdpContext *context, rdpPointer *pointer)
|
||||
{
|
||||
if (!context)
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL ios_Pointer_SetPosition(rdpContext *context, UINT32 x, UINT32 y)
|
||||
{
|
||||
if (!context)
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL ios_Pointer_SetNull(rdpContext *context)
|
||||
{
|
||||
if (!context)
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL ios_Pointer_SetDefault(rdpContext *context)
|
||||
{
|
||||
if (!context)
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL ios_register_pointer(rdpGraphics *graphics)
|
||||
{
|
||||
rdpPointer pointer = WINPR_C_ARRAY_INIT;
|
||||
|
||||
if (!graphics)
|
||||
return FALSE;
|
||||
|
||||
pointer.size = sizeof(pointer);
|
||||
pointer.New = ios_Pointer_New;
|
||||
pointer.Free = ios_Pointer_Free;
|
||||
pointer.Set = ios_Pointer_Set;
|
||||
pointer.SetNull = ios_Pointer_SetNull;
|
||||
pointer.SetDefault = ios_Pointer_SetDefault;
|
||||
pointer.SetPosition = ios_Pointer_SetPosition;
|
||||
graphics_register_pointer(graphics, &pointer);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL ios_post_connect(freerdp *instance)
|
||||
{
|
||||
mfInfo *mfi;
|
||||
|
||||
if (!instance)
|
||||
return FALSE;
|
||||
|
||||
mfi = MFI_FROM_INSTANCE(instance);
|
||||
|
||||
if (!mfi)
|
||||
return FALSE;
|
||||
|
||||
if (!gdi_init(instance, PIXEL_FORMAT_BGRA32))
|
||||
return FALSE;
|
||||
|
||||
if (!ios_register_pointer(instance->context->graphics))
|
||||
return FALSE;
|
||||
|
||||
ios_allocate_display_buffer(mfi);
|
||||
instance->context->update->BeginPaint = ios_ui_begin_paint;
|
||||
instance->context->update->EndPaint = ios_ui_end_paint;
|
||||
instance->context->update->DesktopResize = ios_ui_resize_window;
|
||||
[mfi->session performSelectorOnMainThread:@selector(sessionDidConnect)
|
||||
withObject:nil
|
||||
waitUntilDone:YES];
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void ios_post_disconnect(freerdp *instance)
|
||||
{
|
||||
gdi_free(instance);
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Running the connection
|
||||
|
||||
int ios_run_freerdp(freerdp *instance)
|
||||
{
|
||||
mfContext *context = (mfContext *)instance->context;
|
||||
mfInfo *mfi = context->mfi;
|
||||
rdpChannels *channels = instance->context->channels;
|
||||
mfi->connection_state = TSXConnectionConnecting;
|
||||
|
||||
if (!freerdp_connect(instance))
|
||||
{
|
||||
NSLog(@"%s: inst->rdp_connect failed", __func__);
|
||||
return mfi->unwanted ? MF_EXIT_CONN_CANCELED : MF_EXIT_CONN_FAILED;
|
||||
}
|
||||
|
||||
if (mfi->unwanted)
|
||||
return MF_EXIT_CONN_CANCELED;
|
||||
|
||||
mfi->connection_state = TSXConnectionConnected;
|
||||
// Connection main loop
|
||||
NSAutoreleasePool *pool;
|
||||
|
||||
while (!freerdp_shall_disconnect_context(instance->context))
|
||||
{
|
||||
DWORD status;
|
||||
DWORD nCount = 0;
|
||||
HANDLE handles[MAXIMUM_WAIT_OBJECTS] = WINPR_C_ARRAY_INIT;
|
||||
pool = [[NSAutoreleasePool alloc] init];
|
||||
|
||||
nCount = freerdp_get_event_handles(instance->context, handles, ARRAYSIZE(handles));
|
||||
if (nCount == 0)
|
||||
{
|
||||
NSLog(@"%s: freerdp_get_event_handles failed", __func__);
|
||||
break;
|
||||
}
|
||||
|
||||
handles[nCount++] = ios_events_get_handle(mfi);
|
||||
|
||||
status = WaitForMultipleObjects(nCount, handles, FALSE, INFINITE);
|
||||
|
||||
if (WAIT_FAILED == status)
|
||||
{
|
||||
NSLog(@"%s: WaitForMultipleObjects failed!", __func__);
|
||||
break;
|
||||
}
|
||||
|
||||
// Check the libfreerdp fds
|
||||
if (!freerdp_check_event_handles(instance->context))
|
||||
{
|
||||
NSLog(@"%s: freerdp_check_event_handles failed.", __func__);
|
||||
break;
|
||||
}
|
||||
|
||||
// Check input event fds
|
||||
if (ios_events_check_handle(mfi) != TRUE)
|
||||
{
|
||||
// This event will fail when the app asks for a disconnect.
|
||||
// NSLog(@"%s: ios_events_check_fds failed: terminating connection.", __func__);
|
||||
break;
|
||||
}
|
||||
|
||||
[pool release];
|
||||
pool = nil;
|
||||
}
|
||||
|
||||
CGContextRelease(mfi->bitmap_context);
|
||||
mfi->bitmap_context = nullptr;
|
||||
mfi->connection_state = TSXConnectionDisconnected;
|
||||
// Cleanup
|
||||
freerdp_disconnect(instance);
|
||||
gdi_free(instance);
|
||||
cache_free(instance->context->cache);
|
||||
[pool release];
|
||||
pool = nil;
|
||||
return MF_EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Context callbacks
|
||||
|
||||
static BOOL ios_client_new(freerdp *instance, rdpContext *context)
|
||||
{
|
||||
mfContext *ctx = (mfContext *)context;
|
||||
|
||||
if (!instance || !context)
|
||||
return FALSE;
|
||||
|
||||
if ((ctx->mfi = calloc(1, sizeof(mfInfo))) == nullptr)
|
||||
return FALSE;
|
||||
|
||||
ctx->mfi->context = (mfContext *)context;
|
||||
ctx->mfi->_context = context;
|
||||
ctx->mfi->instance = instance;
|
||||
|
||||
if (!ios_events_create_pipe(ctx->mfi))
|
||||
return FALSE;
|
||||
|
||||
instance->PreConnect = ios_pre_connect;
|
||||
instance->PostConnect = ios_post_connect;
|
||||
instance->PostDisconnect = ios_post_disconnect;
|
||||
instance->Authenticate = ios_ui_authenticate;
|
||||
instance->GatewayAuthenticate = ios_ui_gw_authenticate;
|
||||
instance->VerifyCertificateEx = ios_ui_verify_certificate_ex;
|
||||
instance->VerifyChangedCertificateEx = ios_ui_verify_changed_certificate_ex;
|
||||
instance->LogonErrorInfo = nullptr;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void ios_client_free(freerdp *instance, rdpContext *context)
|
||||
{
|
||||
mfInfo *mfi;
|
||||
|
||||
if (!context)
|
||||
return;
|
||||
|
||||
mfi = ((mfContext *)context)->mfi;
|
||||
ios_events_free_pipe(mfi);
|
||||
free(mfi);
|
||||
}
|
||||
|
||||
static int RdpClientEntry(RDP_CLIENT_ENTRY_POINTS *pEntryPoints)
|
||||
{
|
||||
WINPR_ASSERT(pEntryPoints);
|
||||
|
||||
ZeroMemory(pEntryPoints, sizeof(RDP_CLIENT_ENTRY_POINTS));
|
||||
pEntryPoints->Version = RDP_CLIENT_INTERFACE_VERSION;
|
||||
pEntryPoints->Size = sizeof(RDP_CLIENT_ENTRY_POINTS_V1);
|
||||
pEntryPoints->GlobalInit = nullptr;
|
||||
pEntryPoints->GlobalUninit = nullptr;
|
||||
pEntryPoints->ContextSize = sizeof(mfContext);
|
||||
pEntryPoints->ClientNew = ios_client_new;
|
||||
pEntryPoints->ClientFree = ios_client_free;
|
||||
pEntryPoints->ClientStart = nullptr;
|
||||
pEntryPoints->ClientStop = nullptr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Initialization and cleanup
|
||||
|
||||
freerdp *ios_freerdp_new()
|
||||
{
|
||||
rdpContext *context;
|
||||
RDP_CLIENT_ENTRY_POINTS clientEntryPoints;
|
||||
RdpClientEntry(&clientEntryPoints);
|
||||
context = freerdp_client_context_new(&clientEntryPoints);
|
||||
|
||||
if (!context)
|
||||
return nullptr;
|
||||
|
||||
return context->instance;
|
||||
}
|
||||
|
||||
void ios_freerdp_free(freerdp *instance)
|
||||
{
|
||||
if (!instance || !instance->context)
|
||||
return;
|
||||
|
||||
freerdp_client_context_free(instance->context);
|
||||
}
|
||||
|
||||
void ios_init_freerdp()
|
||||
{
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
}
|
||||
|
||||
void ios_uninit_freerdp()
|
||||
{
|
||||
}
|
||||
|
||||
/* compatibility functions */
|
||||
size_t fwrite$UNIX2003(const void *ptr, size_t size, size_t nmemb, FILE *stream)
|
||||
{
|
||||
return fwrite(ptr, size, nmemb, stream);
|
||||
}
|
||||
|
||||
void ios_send_clipboard_data(void *context, const void *data, UINT32 size)
|
||||
{
|
||||
mfContext *afc = (mfContext *)context;
|
||||
ClipboardLock(afc->clipboard);
|
||||
UINT32 formatId = ClipboardRegisterFormat(afc->clipboard, "UTF8_STRING");
|
||||
if (size)
|
||||
ClipboardSetData(afc->clipboard, formatId, data, size);
|
||||
else
|
||||
ClipboardEmpty(afc->clipboard);
|
||||
ClipboardUnlock(afc->clipboard);
|
||||
ios_cliprdr_send_client_format_list(afc->cliprdr);
|
||||
}
|
||||
27
third_party/FreeRDP/client/iOS/FreeRDP/ios_freerdp_events.h
vendored
Normal file
27
third_party/FreeRDP/client/iOS/FreeRDP/ios_freerdp_events.h
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
RDP event queuing
|
||||
|
||||
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/.
|
||||
*/
|
||||
|
||||
#ifndef IOS_RDP_EVENT_H
|
||||
#define IOS_RDP_EVENT_H
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "ios_freerdp.h"
|
||||
|
||||
// For UI: use to send events
|
||||
BOOL ios_events_send(mfInfo *mfi, NSDictionary *event_description);
|
||||
|
||||
// For connection runloop: use to poll for queued input events
|
||||
HANDLE ios_events_get_handle(mfInfo *mfi);
|
||||
BOOL ios_events_check_handle(mfInfo *mfi);
|
||||
|
||||
BOOL ios_events_create_pipe(mfInfo *mfi);
|
||||
void ios_events_free_pipe(mfInfo *mfi);
|
||||
|
||||
#endif /* IOS_RDP_EVENT_H */
|
||||
174
third_party/FreeRDP/client/iOS/FreeRDP/ios_freerdp_events.m
vendored
Normal file
174
third_party/FreeRDP/client/iOS/FreeRDP/ios_freerdp_events.m
vendored
Normal file
@@ -0,0 +1,174 @@
|
||||
/*
|
||||
RDP event queuing
|
||||
|
||||
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/.
|
||||
*/
|
||||
|
||||
#include <winpr/assert.h>
|
||||
|
||||
#include "ios_freerdp_events.h"
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Sending compacted input events (from main thread)
|
||||
|
||||
// While this function may be called from any thread that has an autorelease pool allocated, it is
|
||||
// not threadsafe: caller is responsible for synchronization
|
||||
BOOL ios_events_send(mfInfo *mfi, NSDictionary *event_description)
|
||||
{
|
||||
NSData *encoded_description = [NSKeyedArchiver archivedDataWithRootObject:event_description];
|
||||
|
||||
WINPR_ASSERT(mfi);
|
||||
|
||||
if ([encoded_description length] > 32000 || (mfi->event_pipe_producer == -1))
|
||||
return FALSE;
|
||||
|
||||
uint32_t archived_data_len = (uint32_t)[encoded_description length];
|
||||
|
||||
// NSLog(@"writing %d bytes to input event pipe", archived_data_len);
|
||||
|
||||
if (write(mfi->event_pipe_producer, &archived_data_len, 4) == -1)
|
||||
{
|
||||
NSLog(@"%s: Failed to write length descriptor to pipe.", __func__);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (write(mfi->event_pipe_producer, [encoded_description bytes], archived_data_len) == -1)
|
||||
{
|
||||
NSLog(@"%s: Failed to write %d bytes into the event queue (event type: %@).", __func__,
|
||||
(int)[encoded_description length], [event_description objectForKey:@"type"]);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Processing compacted input events (from connection thread runloop)
|
||||
|
||||
static BOOL ios_events_handle_event(mfInfo *mfi, NSDictionary *event_description)
|
||||
{
|
||||
NSString *event_type = [event_description objectForKey:@"type"];
|
||||
BOOL should_continue = TRUE;
|
||||
rdpInput *input;
|
||||
|
||||
WINPR_ASSERT(mfi);
|
||||
|
||||
freerdp *instance = mfi->instance;
|
||||
WINPR_ASSERT(instance);
|
||||
WINPR_ASSERT(instance->context);
|
||||
|
||||
input = instance->context->input;
|
||||
WINPR_ASSERT(input);
|
||||
|
||||
if ([event_type isEqualToString:@"mouse"])
|
||||
{
|
||||
input->MouseEvent(input, [[event_description objectForKey:@"flags"] unsignedShortValue],
|
||||
[[event_description objectForKey:@"coord_x"] unsignedShortValue],
|
||||
[[event_description objectForKey:@"coord_y"] unsignedShortValue]);
|
||||
}
|
||||
else if ([event_type isEqualToString:@"keyboard"])
|
||||
{
|
||||
if ([[event_description objectForKey:@"subtype"] isEqualToString:@"scancode"])
|
||||
freerdp_input_send_keyboard_event(
|
||||
input, [[event_description objectForKey:@"flags"] unsignedShortValue],
|
||||
[[event_description objectForKey:@"scancode"] unsignedShortValue]);
|
||||
else if ([[event_description objectForKey:@"subtype"] isEqualToString:@"unicode"])
|
||||
freerdp_input_send_unicode_keyboard_event(
|
||||
input, [[event_description objectForKey:@"flags"] unsignedShortValue],
|
||||
[[event_description objectForKey:@"unicode_char"] unsignedShortValue]);
|
||||
else
|
||||
NSLog(@"%s: doesn't know how to send keyboard input with subtype %@", __func__,
|
||||
[event_description objectForKey:@"subtype"]);
|
||||
}
|
||||
else if ([event_type isEqualToString:@"disconnect"])
|
||||
should_continue = FALSE;
|
||||
else
|
||||
NSLog(@"%s: unrecognized event type: %@", __func__, event_type);
|
||||
|
||||
return should_continue;
|
||||
}
|
||||
|
||||
BOOL ios_events_check_handle(mfInfo *mfi)
|
||||
{
|
||||
WINPR_ASSERT(mfi);
|
||||
|
||||
if (WaitForSingleObject(mfi->handle, 0) != WAIT_OBJECT_0)
|
||||
return TRUE;
|
||||
|
||||
if (mfi->event_pipe_consumer == -1)
|
||||
return TRUE;
|
||||
|
||||
uint32_t archived_data_length = 0;
|
||||
ssize_t bytes_read;
|
||||
|
||||
// First, read the length of the blob
|
||||
bytes_read = read(mfi->event_pipe_consumer, &archived_data_length, 4);
|
||||
|
||||
if (bytes_read == -1 || archived_data_length < 1 || archived_data_length > 32000)
|
||||
{
|
||||
NSLog(@"%s: just read length descriptor. bytes_read=%ld, archived_data_length=%u", __func__,
|
||||
bytes_read, archived_data_length);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// NSLog(@"reading %d bytes from input event pipe", archived_data_length);
|
||||
|
||||
NSMutableData *archived_object_data =
|
||||
[[NSMutableData alloc] initWithLength:archived_data_length];
|
||||
bytes_read =
|
||||
read(mfi->event_pipe_consumer, [archived_object_data mutableBytes], archived_data_length);
|
||||
|
||||
if (bytes_read != archived_data_length)
|
||||
{
|
||||
NSLog(@"%s: attempted to read data; read %ld bytes but wanted %d bytes.", __func__,
|
||||
bytes_read, archived_data_length);
|
||||
[archived_object_data release];
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
id unarchived_object_data = [NSKeyedUnarchiver unarchiveObjectWithData:archived_object_data];
|
||||
[archived_object_data release];
|
||||
|
||||
return ios_events_handle_event(mfi, unarchived_object_data);
|
||||
}
|
||||
|
||||
HANDLE ios_events_get_handle(mfInfo *mfi)
|
||||
{
|
||||
WINPR_ASSERT(mfi);
|
||||
return mfi->handle;
|
||||
}
|
||||
|
||||
// Sets up the event pipe
|
||||
BOOL ios_events_create_pipe(mfInfo *mfi)
|
||||
{
|
||||
int pipe_fds[2];
|
||||
|
||||
WINPR_ASSERT(mfi);
|
||||
|
||||
if (pipe(pipe_fds) == -1)
|
||||
{
|
||||
NSLog(@"%s: pipe failed.", __func__);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
mfi->event_pipe_consumer = pipe_fds[0];
|
||||
mfi->event_pipe_producer = pipe_fds[1];
|
||||
mfi->handle = CreateFileDescriptorEvent(nullptr, FALSE, FALSE, mfi->event_pipe_consumer,
|
||||
WINPR_FD_READ | WINPR_FD_WRITE);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void ios_events_free_pipe(mfInfo *mfi)
|
||||
{
|
||||
WINPR_ASSERT(mfi);
|
||||
int consumer_fd = mfi->event_pipe_consumer, producer_fd = mfi->event_pipe_producer;
|
||||
|
||||
mfi->event_pipe_consumer = mfi->event_pipe_producer = -1;
|
||||
close(producer_fd);
|
||||
close(consumer_fd);
|
||||
(void)CloseHandle(mfi->handle);
|
||||
}
|
||||
29
third_party/FreeRDP/client/iOS/FreeRDP/ios_freerdp_ui.h
vendored
Normal file
29
third_party/FreeRDP/client/iOS/FreeRDP/ios_freerdp_ui.h
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
RDP ui callbacks
|
||||
|
||||
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"
|
||||
|
||||
BOOL ios_ui_begin_paint(rdpContext* context);
|
||||
BOOL ios_ui_end_paint(rdpContext* context);
|
||||
BOOL ios_ui_resize_window(rdpContext* context);
|
||||
|
||||
BOOL ios_ui_authenticate(freerdp* instance, char** username, char** password, char** domain);
|
||||
BOOL ios_ui_gw_authenticate(freerdp* instance, char** username, char** password, char** domain);
|
||||
DWORD ios_ui_verify_certificate_ex(freerdp* instance, const char* host, UINT16 port,
|
||||
const char* common_name, const char* subject, const char* issuer,
|
||||
const char* fingerprint, DWORD flags);
|
||||
|
||||
DWORD ios_ui_verify_changed_certificate_ex(freerdp* instance, const char* host, UINT16 port,
|
||||
const char* common_name, const char* subject,
|
||||
const char* issuer, const char* fingerprint,
|
||||
const char* old_subject, const char* old_issuer,
|
||||
const char* old_fingerprint, DWORD flags);
|
||||
|
||||
void ios_allocate_display_buffer(mfInfo* mfi);
|
||||
241
third_party/FreeRDP/client/iOS/FreeRDP/ios_freerdp_ui.m
vendored
Normal file
241
third_party/FreeRDP/client/iOS/FreeRDP/ios_freerdp_ui.m
vendored
Normal file
@@ -0,0 +1,241 @@
|
||||
/*
|
||||
RDP ui callbacks
|
||||
|
||||
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 <freerdp/gdi/gdi.h>
|
||||
#import "ios_freerdp_ui.h"
|
||||
|
||||
#import "RDPSession.h"
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Certificate authentication
|
||||
|
||||
static void ios_resize_display_buffer(mfInfo *mfi);
|
||||
static BOOL ios_ui_authenticate_raw(freerdp *instance, char **username, char **password,
|
||||
char **domain, const char *title)
|
||||
{
|
||||
mfInfo *mfi = MFI_FROM_INSTANCE(instance);
|
||||
NSMutableDictionary *params = [NSMutableDictionary
|
||||
dictionaryWithObjectsAndKeys:(*username) ? [NSString stringWithUTF8String:*username] : @"",
|
||||
@"username",
|
||||
(*password) ? [NSString stringWithUTF8String:*password] : @"",
|
||||
@"password",
|
||||
(*domain) ? [NSString stringWithUTF8String:*domain] : @"",
|
||||
@"domain",
|
||||
[NSString stringWithUTF8String:freerdp_settings_get_string(
|
||||
instance->context->settings,
|
||||
FreeRDP_ServerHostname)],
|
||||
@"hostname", // used for the auth prompt message; not changed
|
||||
nil];
|
||||
// request auth UI
|
||||
[mfi->session performSelectorOnMainThread:@selector(sessionRequestsAuthenticationWithParams:)
|
||||
withObject:params
|
||||
waitUntilDone:YES];
|
||||
// wait for UI request to be completed
|
||||
[[mfi->session uiRequestCompleted] lock];
|
||||
[[mfi->session uiRequestCompleted] wait];
|
||||
[[mfi->session uiRequestCompleted] unlock];
|
||||
|
||||
if (![[params valueForKey:@"result"] boolValue])
|
||||
{
|
||||
mfi->unwanted = YES;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Free old values
|
||||
free(*username);
|
||||
free(*password);
|
||||
free(*domain);
|
||||
// set values back
|
||||
*username = _strdup([[params objectForKey:@"username"] UTF8String]);
|
||||
*password = _strdup([[params objectForKey:@"password"] UTF8String]);
|
||||
*domain = _strdup([[params objectForKey:@"domain"] UTF8String]);
|
||||
|
||||
if (!(*username) || !(*password) || !(*domain))
|
||||
{
|
||||
free(*username);
|
||||
free(*password);
|
||||
free(*domain);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL ios_ui_authenticate(freerdp *instance, char **username, char **password, char **domain)
|
||||
{
|
||||
return ios_ui_authenticate_raw(instance, username, password, domain, "");
|
||||
}
|
||||
|
||||
BOOL ios_ui_gw_authenticate(freerdp *instance, char **username, char **password, char **domain)
|
||||
{
|
||||
return ios_ui_authenticate_raw(instance, username, password, domain, "gateway");
|
||||
}
|
||||
|
||||
DWORD ios_ui_verify_certificate_ex(freerdp *instance, const char *host, UINT16 port,
|
||||
const char *common_name, const char *subject, const char *issuer,
|
||||
const char *fingerprint, DWORD flags)
|
||||
{
|
||||
// check whether we accept all certificates
|
||||
if ([[NSUserDefaults standardUserDefaults] boolForKey:@"security.accept_certificates"] == YES)
|
||||
return 2;
|
||||
|
||||
mfInfo *mfi = MFI_FROM_INSTANCE(instance);
|
||||
NSMutableDictionary *params = [NSMutableDictionary
|
||||
dictionaryWithObjectsAndKeys:(subject) ? [NSString stringWithUTF8String:subject] : @"",
|
||||
@"subject",
|
||||
(issuer) ? [NSString stringWithUTF8String:issuer] : @"",
|
||||
@"issuer",
|
||||
(fingerprint) ? [NSString stringWithUTF8String:subject] : @"",
|
||||
@"fingerprint", nil];
|
||||
// request certificate verification UI
|
||||
[mfi->session performSelectorOnMainThread:@selector(sessionVerifyCertificateWithParams:)
|
||||
withObject:params
|
||||
waitUntilDone:YES];
|
||||
// wait for UI request to be completed
|
||||
[[mfi->session uiRequestCompleted] lock];
|
||||
[[mfi->session uiRequestCompleted] wait];
|
||||
[[mfi->session uiRequestCompleted] unlock];
|
||||
|
||||
if (![[params valueForKey:@"result"] boolValue])
|
||||
{
|
||||
mfi->unwanted = YES;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
DWORD ios_ui_verify_changed_certificate_ex(freerdp *instance, const char *host, UINT16 port,
|
||||
const char *common_name, const char *subject,
|
||||
const char *issuer, const char *fingerprint,
|
||||
const char *old_subject, const char *old_issuer,
|
||||
const char *old_fingerprint, DWORD flags)
|
||||
{
|
||||
return ios_ui_verify_certificate_ex(instance, host, port, common_name, subject, issuer,
|
||||
fingerprint, flags);
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Graphics updates
|
||||
|
||||
BOOL ios_ui_begin_paint(rdpContext *context)
|
||||
{
|
||||
WINPR_ASSERT(context);
|
||||
|
||||
rdpGdi *gdi = context->gdi;
|
||||
WINPR_ASSERT(gdi);
|
||||
WINPR_ASSERT(gdi->primary);
|
||||
|
||||
HGDI_DC hdc = gdi->primary->hdc;
|
||||
WINPR_ASSERT(hdc);
|
||||
if (!hdc->hwnd)
|
||||
return TRUE;
|
||||
|
||||
HGDI_WND hwnd = hdc->hwnd;
|
||||
if (!hwnd->invalid)
|
||||
return TRUE;
|
||||
hwnd->invalid->null = TRUE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL ios_ui_end_paint(rdpContext *context)
|
||||
{
|
||||
WINPR_ASSERT(context);
|
||||
|
||||
mfInfo *mfi = MFI_FROM_INSTANCE(context->instance);
|
||||
WINPR_ASSERT(mfi);
|
||||
|
||||
rdpGdi *gdi = context->gdi;
|
||||
WINPR_ASSERT(gdi);
|
||||
WINPR_ASSERT(gdi->primary);
|
||||
|
||||
HGDI_DC hdc = gdi->primary->hdc;
|
||||
WINPR_ASSERT(hdc);
|
||||
if (!hdc->hwnd)
|
||||
return TRUE;
|
||||
|
||||
HGDI_WND hwnd = hdc->hwnd;
|
||||
WINPR_ASSERT(hwnd->invalid || (hwnd->ninvalid == 0));
|
||||
|
||||
if (hwnd->invalid->null)
|
||||
return TRUE;
|
||||
|
||||
CGRect dirty_rect =
|
||||
CGRectMake(hwnd->invalid->x, hwnd->invalid->y, hwnd->invalid->w, hwnd->invalid->h);
|
||||
|
||||
if (!hwnd->invalid->null)
|
||||
[mfi->session performSelectorOnMainThread:@selector(setNeedsDisplayInRectAsValue:)
|
||||
withObject:[NSValue valueWithCGRect:dirty_rect]
|
||||
waitUntilDone:NO];
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL ios_ui_resize_window(rdpContext *context)
|
||||
{
|
||||
rdpSettings *settings;
|
||||
rdpGdi *gdi;
|
||||
|
||||
if (!context || !context->settings)
|
||||
return FALSE;
|
||||
|
||||
settings = context->settings;
|
||||
gdi = context->gdi;
|
||||
|
||||
if (!gdi_resize(gdi, freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth),
|
||||
freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight)))
|
||||
return FALSE;
|
||||
|
||||
ios_resize_display_buffer(MFI_FROM_INSTANCE(context->instance));
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Exported
|
||||
|
||||
static void ios_create_bitmap_context(mfInfo *mfi)
|
||||
{
|
||||
[mfi->session performSelectorOnMainThread:@selector(sessionBitmapContextWillChange)
|
||||
withObject:nil
|
||||
waitUntilDone:YES];
|
||||
rdpGdi *gdi = mfi->instance->context->gdi;
|
||||
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
|
||||
|
||||
if (FreeRDPGetBytesPerPixel(gdi->dstFormat) == 2)
|
||||
mfi->bitmap_context = CGBitmapContextCreate(
|
||||
gdi->primary_buffer, gdi->width, gdi->height, 5, gdi->stride, colorSpace,
|
||||
kCGBitmapByteOrder16Little | kCGImageAlphaNoneSkipFirst);
|
||||
else
|
||||
mfi->bitmap_context = CGBitmapContextCreate(
|
||||
gdi->primary_buffer, gdi->width, gdi->height, 8, gdi->stride, colorSpace,
|
||||
kCGBitmapByteOrder32Little | kCGImageAlphaNoneSkipFirst);
|
||||
|
||||
CGColorSpaceRelease(colorSpace);
|
||||
[mfi->session performSelectorOnMainThread:@selector(sessionBitmapContextDidChange)
|
||||
withObject:nil
|
||||
waitUntilDone:YES];
|
||||
}
|
||||
|
||||
void ios_allocate_display_buffer(mfInfo *mfi)
|
||||
{
|
||||
ios_create_bitmap_context(mfi);
|
||||
}
|
||||
|
||||
void ios_resize_display_buffer(mfInfo *mfi)
|
||||
{
|
||||
// Release the old context in a thread-safe manner
|
||||
CGContextRef old_context = mfi->bitmap_context;
|
||||
mfi->bitmap_context = nullptr;
|
||||
CGContextRelease(old_context);
|
||||
// Create the new context
|
||||
ios_create_bitmap_context(mfi);
|
||||
}
|
||||
Reference in New Issue
Block a user