From: kubistika Date: Tue, 22 Oct 2019 13:06:05 +0000 (+0300) Subject: server: proxy: implement session capture feature X-Git-Tag: 2.0.0~294 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=16d4d46da6cdec3d49e5e3e23e79397a53221377;p=platform%2Fupstream%2Ffreerdp.git server: proxy: implement session capture feature --- diff --git a/server/proxy/CMakeLists.txt b/server/proxy/CMakeLists.txt index c076f42..0ad4ef6 100644 --- a/server/proxy/CMakeLists.txt +++ b/server/proxy/CMakeLists.txt @@ -50,6 +50,8 @@ set(${MODULE_PREFIX}_SRCS pf_cliprdr.h pf_rdpsnd.c pf_rdpsnd.h + pf_capture.c + pf_capture.h pf_log.h ) diff --git a/server/proxy/config.ini b/server/proxy/config.ini index 4e8edb6..cd34aee 100644 --- a/server/proxy/config.ini +++ b/server/proxy/config.ini @@ -1,7 +1,10 @@ [Server] Host = "0.0.0.0" Port = 3389 -LocalOnly = 0 + +[SessionCapture] +Enabled = 1 +CapturesDirectory = "captures" [Target] ; If this value is set to TRUE, the target server info will be parsed using the diff --git a/server/proxy/pf_capture.c b/server/proxy/pf_capture.c new file mode 100644 index 0000000..b52b42f --- /dev/null +++ b/server/proxy/pf_capture.c @@ -0,0 +1,77 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * FreeRDP Proxy Server Session Capture + * + * Copyright 2019 Kobi Mizrachi + * + * 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 +#include + +#include "pf_capture.h" + +static BOOL pf_capture_create_dir_if_not_exists(const char* path) +{ + if (PathFileExistsA(path)) + return TRUE; + + return CreateDirectoryA(path, NULL); +} + +/* creates a directory to store captured session frames. + * + * @context: current session. + * + * directory path will be: base_dir/username/session-start-date. + * + * it is important to call this function only after the connection is fully established, as it uses + * settings->Username and settings->ServerHostname values to create the directory. After the + * connection established, we know that those values are valid. + */ +BOOL pf_capture_create_session_directory(pClientContext* pc) +{ + proxyConfig* config = pc->pdata->config; + rdpSettings* settings = pc->context.settings; + SYSTEMTIME localTime; + char tmp[MAX_PATH]; + const char* fmt = "%s/%s/%s_%02u-%02u-%"PRIu16"_%02u-%02u-%02u-%03u"; + + _snprintf(tmp, sizeof(tmp), "%s/%s", config->CapturesDirectory, settings->Username); + if (!pf_capture_create_dir_if_not_exists(tmp)) + return FALSE; + + pc->frames_dir = malloc(MAX_PATH); + if (!pc->frames_dir) + return FALSE; + + GetLocalTime(&localTime); + sprintf_s(pc->frames_dir, MAX_PATH, fmt, config->CapturesDirectory, settings->Username, + settings->ServerHostname, localTime.wDay, localTime.wMonth, localTime.wYear, + localTime.wHour, localTime.wMinute, localTime.wSecond, localTime.wMilliseconds); + + return pf_capture_create_dir_if_not_exists(pc->frames_dir); +} + +/* saves a captured frame in a BMP format. */ +BOOL pf_capture_save_frame(pClientContext* pc, const BYTE* frame) +{ + rdpSettings* settings = pc->context.settings; + const char* fmt = "%s/%"PRIu64".bmp"; + char file_path[MAX_PATH]; + + sprintf_s(file_path, sizeof(file_path), fmt, pc->frames_dir, pc->frames_count++); + return winpr_bitmap_write(file_path, frame, settings->DesktopWidth, settings->DesktopHeight, + settings->ColorDepth); +} diff --git a/server/proxy/pf_capture.h b/server/proxy/pf_capture.h new file mode 100644 index 0000000..92aaf1c --- /dev/null +++ b/server/proxy/pf_capture.h @@ -0,0 +1,33 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * FreeRDP Proxy Server Session Capture + * + * Copyright 2019 Kobi Mizrachi + * + * 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_SERVER_PROXY_CAPTURE_H +#define FREERDP_SERVER_PROXY_CAPTURE_H + +#include +#include +#include +#include + +#include "pf_context.h" + +BOOL pf_capture_create_session_directory(pClientContext* context); +BOOL pf_capture_save_frame(pClientContext* pc, const BYTE* frame); + +#endif /* FREERDP_SERVER_PROXY_CAPTURE_H */ diff --git a/server/proxy/pf_channels.c b/server/proxy/pf_channels.c index 15e74fc..6d8d316 100644 --- a/server/proxy/pf_channels.c +++ b/server/proxy/pf_channels.c @@ -73,8 +73,8 @@ void pf_OnChannelConnectedEventHandler(void* data, return; } - pc->gfx = (RdpgfxClientContext*) e->pInterface; - pf_rdpgfx_pipeline_init(pc->gfx, ps->gfx, pc->pdata); + pc->gfx_proxy = (RdpgfxClientContext*) e->pInterface; + pf_rdpgfx_pipeline_init(pc->gfx_proxy, ps->gfx, pc->pdata); } else if (strcmp(e->name, DISP_DVC_CHANNEL_NAME) == 0) { @@ -145,7 +145,8 @@ void pf_OnChannelDisconnectedEventHandler(void* data, if (!ps->gfx->Close(ps->gfx)) WLog_ERR(TAG, "failed to close gfx server"); - gdi_graphics_pipeline_uninit(context->gdi, (RdpgfxClientContext*) e->pInterface); + gdi_graphics_pipeline_uninit(context->gdi, pc->gfx_decoder); + rdpgfx_client_context_free(pc->gfx_decoder); } else if (strcmp(e->name, DISP_DVC_CHANNEL_NAME) == 0) { diff --git a/server/proxy/pf_client.c b/server/proxy/pf_client.c index c48d527..8c0c848 100644 --- a/server/proxy/pf_client.c +++ b/server/proxy/pf_client.c @@ -50,6 +50,7 @@ #include "pf_update.h" #include "pf_log.h" #include "pf_modules.h" +#include "pf_capture.h" #define TAG PROXY_TAG("client") @@ -196,14 +197,27 @@ static BOOL pf_client_post_connect(freerdp* instance) rdpContext* context; rdpSettings* settings; rdpUpdate* update; - pClientContext* pc; rdpContext* ps; + pClientContext* pc; + proxyConfig* config; context = instance->context; settings = instance->settings; update = instance->update; pc = (pClientContext*) context; ps = (rdpContext*) pc->pdata->ps; + config = pc->pdata->config; + + if (config->SessionCapture) + { + if (!pf_capture_create_session_directory(pc)) + { + WLog_ERR(TAG, "pf_capture_create_session_directory failed!"); + return FALSE; + } + + WLog_INFO(TAG, "frames dir created: %s", pc->frames_dir); + } if (!gdi_init(instance, PIXEL_FORMAT_BGRA32)) return FALSE; @@ -523,9 +537,21 @@ static BOOL pf_client_client_new(freerdp* instance, rdpContext* context) instance->VerifyCertificateEx = pf_client_verify_certificate_ex; instance->VerifyChangedCertificateEx = pf_client_verify_changed_certificate_ex; instance->LogonErrorInfo = pf_logon_error_info; + return TRUE; } +static void pf_client_client_free(freerdp* instance, rdpContext* context) +{ + pClientContext* pc = (pClientContext*) context; + + if (!pc) + return; + + free(pc->frames_dir); + pc->frames_dir = NULL; +} + static int pf_client_client_stop(rdpContext* context) { pClientContext* pc = (pClientContext*) context; @@ -558,6 +584,7 @@ int RdpClientEntry(RDP_CLIENT_ENTRY_POINTS* pEntryPoints) pEntryPoints->ContextSize = sizeof(pClientContext); /* Client init and finish */ pEntryPoints->ClientNew = pf_client_client_new; + pEntryPoints->ClientFree = pf_client_client_free; pEntryPoints->ClientStop = pf_client_client_stop; return 0; } diff --git a/server/proxy/pf_config.c b/server/proxy/pf_config.c index 44b6d60..a52fac5 100644 --- a/server/proxy/pf_config.c +++ b/server/proxy/pf_config.c @@ -69,7 +69,7 @@ static BOOL pf_config_get_uint32(wIniFile* ini, const char* section, const char* static BOOL pf_config_load_server(wIniFile* ini, proxyConfig* config) { config->Host = _strdup(CONFIG_GET_STR(ini, "Server", "Host")); - + if (!pf_config_get_uint16(ini, "Server", "Port", &config->Port)) return FALSE; @@ -150,6 +150,31 @@ static BOOL pf_config_load_modules(wIniFile* ini, proxyConfig* config) return TRUE; } +static BOOL pf_config_load_captures(wIniFile* ini, proxyConfig* config) +{ + const char* captures_dir; + + config->SessionCapture = CONFIG_GET_BOOL(ini, "SessionCapture", "Enabled"); + captures_dir = CONFIG_GET_STR(ini, "SessionCapture", "CapturesDirectory"); + + if (!captures_dir) + return FALSE; + + config->CapturesDirectory = strdup(captures_dir); + if (!config->CapturesDirectory) + return FALSE; + + if (!PathFileExistsA(config->CapturesDirectory)) + { + if (!CreateDirectoryA(config->CapturesDirectory, NULL)) + { + return FALSE; + } + } + + return TRUE; +} + BOOL pf_server_config_load(const char* path, proxyConfig* config) { BOOL ok = FALSE; @@ -188,6 +213,9 @@ BOOL pf_server_config_load(const char* path, proxyConfig* config) if (!pf_config_load_clipboard(ini, config)) goto out; + if (!pf_config_load_captures(ini, config)) + goto out; + ok = TRUE; out: IniFile_Free(ini); @@ -201,6 +229,7 @@ void pf_server_config_print(proxyConfig* config) CONFIG_PRINT_SECTION("Server"); CONFIG_PRINT_STR(config, Host); CONFIG_PRINT_UINT16(config, Port); + CONFIG_PRINT_BOOL(config, SessionCapture); if (!config->UseLoadBalanceInfo) { @@ -232,10 +261,14 @@ void pf_server_config_print(proxyConfig* config) CONFIG_PRINT_BOOL(config, TextOnly); if (config->MaxTextLength > 0) CONFIG_PRINT_UINT32(config, MaxTextLength); + + if (config->SessionCapture) + CONFIG_PRINT_STR(config, CapturesDirectory); } void pf_server_config_free(proxyConfig* config) { + free(config->CapturesDirectory); free(config->TargetHost); free(config->Host); free(config); diff --git a/server/proxy/pf_config.h b/server/proxy/pf_config.h index 553f8bb..c32d4bb 100644 --- a/server/proxy/pf_config.h +++ b/server/proxy/pf_config.h @@ -22,6 +22,7 @@ #define FREERDP_SERVER_PROXY_PFCONFIG_H #include +#include #define CONFIG_GET_STR(ini, section, key) IniFile_GetKeyValueString(ini, section, key) #define CONFIG_GET_BOOL(ini, section, key) IniFile_GetKeyValueInt(ini, section, key) @@ -61,6 +62,10 @@ struct proxy_config /* clipboard specific settings */ BOOL TextOnly; UINT32 MaxTextLength; + + /* session capture */ + BOOL SessionCapture; + char* CapturesDirectory; }; typedef struct proxy_config proxyConfig; diff --git a/server/proxy/pf_context.h b/server/proxy/pf_context.h index b30dadb..3111233 100644 --- a/server/proxy/pf_context.h +++ b/server/proxy/pf_context.h @@ -69,7 +69,8 @@ struct p_client_context proxyData* pdata; RdpeiClientContext* rdpei; - RdpgfxClientContext* gfx; + RdpgfxClientContext* gfx_proxy; + RdpgfxClientContext* gfx_decoder; DispClientContext* disp; CliprdrClientContext* cliprdr; @@ -82,6 +83,10 @@ struct p_client_context * to ensure graceful shutdown of the connection when it will be closed. */ BOOL allow_next_conn_failure; + + /* session capture */ + char* frames_dir; + UINT64 frames_count; }; typedef struct p_client_context pClientContext; diff --git a/server/proxy/pf_rdpgfx.c b/server/proxy/pf_rdpgfx.c index 74d11e8..74c24c7 100644 --- a/server/proxy/pf_rdpgfx.c +++ b/server/proxy/pf_rdpgfx.c @@ -47,100 +47,210 @@ BOOL pf_server_rdpgfx_init(pServerContext* ps) static UINT pf_rdpgfx_reset_graphics(RdpgfxClientContext* context, const RDPGFX_RESET_GRAPHICS_PDU* resetGraphics) { + UINT error; proxyData* pdata = (proxyData*) context->custom; + proxyConfig* config = pdata->config; RdpgfxServerContext* server = (RdpgfxServerContext*) pdata->ps->gfx; + RdpgfxClientContext* gfx_decoder = pdata->pc->gfx_decoder; WLog_VRB(TAG, __FUNCTION__); - return server->ResetGraphics(server, resetGraphics); + + if ((error = server->ResetGraphics(server, resetGraphics))) + return error; + + if (!config->SessionCapture) + return CHANNEL_RC_OK; + + return gfx_decoder->ResetGraphics(gfx_decoder, resetGraphics); } static UINT pf_rdpgfx_start_frame(RdpgfxClientContext* context, const RDPGFX_START_FRAME_PDU* startFrame) { + UINT error; proxyData* pdata = (proxyData*) context->custom; + proxyConfig* config = pdata->config; RdpgfxServerContext* server = (RdpgfxServerContext*) pdata->ps->gfx; + RdpgfxClientContext* gfx_decoder = pdata->pc->gfx_decoder; WLog_VRB(TAG, __FUNCTION__); - return server->StartFrame(server, startFrame); + + if ((error = server->StartFrame(server, startFrame))) + return error; + + if (!config->SessionCapture) + return CHANNEL_RC_OK; + + return gfx_decoder->StartFrame(gfx_decoder, startFrame); } static UINT pf_rdpgfx_end_frame(RdpgfxClientContext* context, const RDPGFX_END_FRAME_PDU* endFrame) { + UINT error; proxyData* pdata = (proxyData*) context->custom; + proxyConfig* config = pdata->config; RdpgfxServerContext* server = (RdpgfxServerContext*) pdata->ps->gfx; + RdpgfxClientContext* gfx_decoder = pdata->pc->gfx_decoder; WLog_VRB(TAG, __FUNCTION__); - return server->EndFrame(server, endFrame); + + if ((error = server->EndFrame(server, endFrame))) + return error; + + if (!config->SessionCapture) + return CHANNEL_RC_OK; + + return gfx_decoder->EndFrame(gfx_decoder, endFrame); } static UINT pf_rdpgfx_surface_command(RdpgfxClientContext* context, const RDPGFX_SURFACE_COMMAND* cmd) { + UINT error; proxyData* pdata = (proxyData*) context->custom; + proxyConfig* config = pdata->config; RdpgfxServerContext* server = (RdpgfxServerContext*) pdata->ps->gfx; + RdpgfxClientContext* gfx_decoder = pdata->pc->gfx_decoder; WLog_VRB(TAG, __FUNCTION__); - return server->SurfaceCommand(server, cmd); + + if ((error = server->SurfaceCommand(server, cmd))) + return error; + + if (!config->SessionCapture) + return CHANNEL_RC_OK; + + return gfx_decoder->SurfaceCommand(gfx_decoder, cmd); } static UINT pf_rdpgfx_delete_encoding_context(RdpgfxClientContext* context, const RDPGFX_DELETE_ENCODING_CONTEXT_PDU* deleteEncodingContext) { + UINT error; proxyData* pdata = (proxyData*) context->custom; + proxyConfig* config = pdata->config; RdpgfxServerContext* server = (RdpgfxServerContext*) pdata->ps->gfx; + RdpgfxClientContext* gfx_decoder = pdata->pc->gfx_decoder; WLog_VRB(TAG, __FUNCTION__); - return server->DeleteEncodingContext(server, deleteEncodingContext); + + if ((error = server->DeleteEncodingContext(server, deleteEncodingContext))) + return error; + + if (!config->SessionCapture) + return CHANNEL_RC_OK; + + return gfx_decoder->DeleteEncodingContext(gfx_decoder, deleteEncodingContext); } static UINT pf_rdpgfx_create_surface(RdpgfxClientContext* context, const RDPGFX_CREATE_SURFACE_PDU* createSurface) { + UINT error; proxyData* pdata = (proxyData*) context->custom; + proxyConfig* config = pdata->config; RdpgfxServerContext* server = (RdpgfxServerContext*) pdata->ps->gfx; + RdpgfxClientContext* gfx_decoder = pdata->pc->gfx_decoder; WLog_VRB(TAG, __FUNCTION__); - return server->CreateSurface(server, createSurface); + + if ((error = server->CreateSurface(server, createSurface))) + return error; + + if (!config->SessionCapture) + return CHANNEL_RC_OK; + + return gfx_decoder->CreateSurface(gfx_decoder, createSurface); } static UINT pf_rdpgfx_delete_surface(RdpgfxClientContext* context, const RDPGFX_DELETE_SURFACE_PDU* deleteSurface) { + UINT error; proxyData* pdata = (proxyData*) context->custom; + proxyConfig* config = pdata->config; RdpgfxServerContext* server = (RdpgfxServerContext*) pdata->ps->gfx; + RdpgfxClientContext* gfx_decoder = pdata->pc->gfx_decoder; WLog_VRB(TAG, __FUNCTION__); - return server->DeleteSurface(server, deleteSurface); + + if ((error = server->DeleteSurface(server, deleteSurface))) + return error; + + if (!config->SessionCapture) + return CHANNEL_RC_OK; + + return gfx_decoder->DeleteSurface(gfx_decoder, deleteSurface); } static UINT pf_rdpgfx_solid_fill(RdpgfxClientContext* context, const RDPGFX_SOLID_FILL_PDU* solidFill) { + UINT error; proxyData* pdata = (proxyData*) context->custom; + proxyConfig* config = pdata->config; RdpgfxServerContext* server = (RdpgfxServerContext*) pdata->ps->gfx; + RdpgfxClientContext* gfx_decoder = pdata->pc->gfx_decoder; WLog_VRB(TAG, __FUNCTION__); - return server->SolidFill(server, solidFill); + + if ((error = server->SolidFill(server, solidFill))) + return error; + + if (!config->SessionCapture) + return CHANNEL_RC_OK; + + return gfx_decoder->SolidFill(gfx_decoder, solidFill); } static UINT pf_rdpgfx_surface_to_surface(RdpgfxClientContext* context, const RDPGFX_SURFACE_TO_SURFACE_PDU* surfaceToSurface) { + UINT error; proxyData* pdata = (proxyData*) context->custom; + proxyConfig* config = pdata->config; RdpgfxServerContext* server = (RdpgfxServerContext*) pdata->ps->gfx; + RdpgfxClientContext* gfx_decoder = pdata->pc->gfx_decoder; WLog_VRB(TAG, __FUNCTION__); - return server->SurfaceToSurface(server, surfaceToSurface); + + if ((error = server->SurfaceToSurface(server, surfaceToSurface))) + return error; + + if (!config->SessionCapture) + return CHANNEL_RC_OK; + + return gfx_decoder->SurfaceToSurface(gfx_decoder, surfaceToSurface); } static UINT pf_rdpgfx_surface_to_cache(RdpgfxClientContext* context, const RDPGFX_SURFACE_TO_CACHE_PDU* surfaceToCache) { + UINT error; proxyData* pdata = (proxyData*) context->custom; + proxyConfig* config = pdata->config; RdpgfxServerContext* server = (RdpgfxServerContext*) pdata->ps->gfx; + RdpgfxClientContext* gfx_decoder = pdata->pc->gfx_decoder; WLog_VRB(TAG, __FUNCTION__); - return server->SurfaceToCache(server, surfaceToCache); + + if ((error = server->SurfaceToCache(server, surfaceToCache))) + return error; + + if (!config->SessionCapture) + return CHANNEL_RC_OK; + + return gfx_decoder->SurfaceToCache(gfx_decoder, surfaceToCache); } static UINT pf_rdpgfx_cache_to_surface(RdpgfxClientContext* context, const RDPGFX_CACHE_TO_SURFACE_PDU* cacheToSurface) { + UINT error; proxyData* pdata = (proxyData*) context->custom; + proxyConfig* config = pdata->config; RdpgfxServerContext* server = (RdpgfxServerContext*) pdata->ps->gfx; + RdpgfxClientContext* gfx_decoder = pdata->pc->gfx_decoder; WLog_VRB(TAG, __FUNCTION__); - return server->CacheToSurface(server, cacheToSurface); + + if ((error = server->CacheToSurface(server, cacheToSurface))) + return error; + + if (!config->SessionCapture) + return CHANNEL_RC_OK; + + return gfx_decoder->CacheToSurface(gfx_decoder, cacheToSurface); } static UINT pf_rdpgfx_cache_import_reply(RdpgfxClientContext* context, @@ -155,47 +265,97 @@ static UINT pf_rdpgfx_cache_import_reply(RdpgfxClientContext* context, static UINT pf_rdpgfx_evict_cache_entry(RdpgfxClientContext* context, const RDPGFX_EVICT_CACHE_ENTRY_PDU* evictCacheEntry) { + UINT error; proxyData* pdata = (proxyData*) context->custom; + proxyConfig* config = pdata->config; RdpgfxServerContext* server = (RdpgfxServerContext*) pdata->ps->gfx; + RdpgfxClientContext* gfx_decoder = pdata->pc->gfx_decoder; WLog_VRB(TAG, __FUNCTION__); - return server->EvictCacheEntry(server, evictCacheEntry); + + if ((error = server->EvictCacheEntry(server, evictCacheEntry))) + return error; + + if (!config->SessionCapture) + return CHANNEL_RC_OK; + + return gfx_decoder->EvictCacheEntry(gfx_decoder, evictCacheEntry); } static UINT pf_rdpgfx_map_surface_to_output(RdpgfxClientContext* context, const RDPGFX_MAP_SURFACE_TO_OUTPUT_PDU* surfaceToOutput) { + UINT error; proxyData* pdata = (proxyData*) context->custom; + proxyConfig* config = pdata->config; RdpgfxServerContext* server = (RdpgfxServerContext*) pdata->ps->gfx; + RdpgfxClientContext* gfx_decoder = pdata->pc->gfx_decoder; WLog_VRB(TAG, __FUNCTION__); - return server->MapSurfaceToOutput(server, surfaceToOutput); + + if ((error = server->MapSurfaceToOutput(server, surfaceToOutput))) + return error; + + if (!config->SessionCapture) + return CHANNEL_RC_OK; + + return gfx_decoder->MapSurfaceToOutput(gfx_decoder, surfaceToOutput); } static UINT pf_rdpgfx_map_surface_to_window(RdpgfxClientContext* context, const RDPGFX_MAP_SURFACE_TO_WINDOW_PDU* surfaceToWindow) { + UINT error; proxyData* pdata = (proxyData*) context->custom; + proxyConfig* config = pdata->config; RdpgfxServerContext* server = (RdpgfxServerContext*) pdata->ps->gfx; + RdpgfxClientContext* gfx_decoder = pdata->pc->gfx_decoder; WLog_VRB(TAG, __FUNCTION__); - return server->MapSurfaceToWindow(server, surfaceToWindow); + + if ((error = server->MapSurfaceToWindow(server, surfaceToWindow))) + return error; + + if (!config->SessionCapture) + return CHANNEL_RC_OK; + + return gfx_decoder->MapSurfaceToWindow(gfx_decoder, surfaceToWindow); } static UINT pf_rdpgfx_map_surface_to_scaled_window(RdpgfxClientContext* context, const RDPGFX_MAP_SURFACE_TO_SCALED_WINDOW_PDU* surfaceToScaledWindow) { + UINT error; proxyData* pdata = (proxyData*) context->custom; + proxyConfig* config = pdata->config; RdpgfxServerContext* server = (RdpgfxServerContext*) pdata->ps->gfx; + RdpgfxClientContext* gfx_decoder = pdata->pc->gfx_decoder; WLog_VRB(TAG, __FUNCTION__); - return server->MapSurfaceToScaledWindow(server, surfaceToScaledWindow); + + if ((error = server->MapSurfaceToScaledWindow(server, surfaceToScaledWindow))) + return error; + + if (!config->SessionCapture) + return CHANNEL_RC_OK; + + return gfx_decoder->MapSurfaceToScaledWindow(gfx_decoder, surfaceToScaledWindow); } static UINT pf_rdpgfx_map_surface_to_scaled_output(RdpgfxClientContext* context, const RDPGFX_MAP_SURFACE_TO_SCALED_OUTPUT_PDU* surfaceToScaledOutput) { + UINT error; proxyData* pdata = (proxyData*) context->custom; + proxyConfig* config = pdata->config; RdpgfxServerContext* server = (RdpgfxServerContext*) pdata->ps->gfx; + RdpgfxClientContext* gfx_decoder = pdata->pc->gfx_decoder; WLog_VRB(TAG, __FUNCTION__); - return server->MapSurfaceToScaledOutput(server, surfaceToScaledOutput); + + if ((error = server->MapSurfaceToScaledOutput(server, surfaceToScaledOutput))) + return error; + + if (!config->SessionCapture) + return CHANNEL_RC_OK; + + return gfx_decoder->MapSurfaceToScaledOutput(gfx_decoder, surfaceToScaledOutput); } static UINT pf_rdpgfx_on_open(RdpgfxClientContext* context, @@ -226,7 +386,7 @@ static UINT pf_rdpgfx_caps_advertise(RdpgfxServerContext* context, const RDPGFX_CAPS_ADVERTISE_PDU* capsAdvertise) { proxyData* pdata = (proxyData*) context->custom; - RdpgfxClientContext* client = (RdpgfxClientContext*) pdata->pc->gfx; + RdpgfxClientContext* client = (RdpgfxClientContext*) pdata->pc->gfx_proxy; UINT16 index; UINT16 proxySupportedCapsSetCount = 0; RDPGFX_CAPS_ADVERTISE_PDU supportedCapsAdvertise; @@ -260,7 +420,7 @@ static UINT pf_rdpgfx_frame_acknowledge(RdpgfxServerContext* context, const RDPGFX_FRAME_ACKNOWLEDGE_PDU* frameAcknowledge) { proxyData* pdata = (proxyData*) context->custom; - RdpgfxClientContext* client = (RdpgfxClientContext*) pdata->pc->gfx; + RdpgfxClientContext* client = (RdpgfxClientContext*) pdata->pc->gfx_proxy; WLog_VRB(TAG, __FUNCTION__); return client->FrameAcknowledge(client, frameAcknowledge); } @@ -269,7 +429,7 @@ static UINT pf_rdpgfx_qoe_frame_acknowledge(RdpgfxServerContext* context, const RDPGFX_QOE_FRAME_ACKNOWLEDGE_PDU* qoeFrameAcknowledge) { proxyData* pdata = (proxyData*) context->custom; - RdpgfxClientContext* client = (RdpgfxClientContext*) pdata->pc->gfx; + RdpgfxClientContext* client = (RdpgfxClientContext*) pdata->pc->gfx_proxy; WLog_VRB(TAG, __FUNCTION__); return client->QoeFrameAcknowledge(client, qoeFrameAcknowledge); } @@ -278,14 +438,34 @@ static UINT pf_rdpgfx_cache_import_offer(RdpgfxServerContext* context, const RDPGFX_CACHE_IMPORT_OFFER_PDU* cacheImportOffer) { proxyData* pdata = (proxyData*) context->custom; - RdpgfxClientContext* client = (RdpgfxClientContext*) pdata->pc->gfx; + RdpgfxClientContext* client = (RdpgfxClientContext*) pdata->pc->gfx_proxy; WLog_VRB(TAG, __FUNCTION__); + + if (pdata->config->SessionCapture) + { + /* do not proxy CacheImportOffer, as is it currently not supported by FREERDP. */ + return CHANNEL_RC_OK; + } + return client->CacheImportOffer(client, cacheImportOffer); } void pf_rdpgfx_pipeline_init(RdpgfxClientContext* gfx, RdpgfxServerContext* server, proxyData* pdata) { + pClientContext* pc = pdata->pc; + + /* create another gfx client and register it to the gdi graphics pipeline */ + pc->gfx_decoder = rdpgfx_client_context_new(pc->context.settings); + if (!pc->gfx_decoder) + { + WLog_ERR(TAG, "failed to initialize gfx capture client!"); + return; + } + + /* start GFX pipeline for fake client */ + gdi_graphics_pipeline_init(pc->context.gdi, pc->gfx_decoder); + /* Set server and client side references to proxy data */ gfx->custom = (void*) pdata; server->custom = (void*) pdata; diff --git a/server/proxy/pf_rdpgfx.h b/server/proxy/pf_rdpgfx.h index 4ac1f20..5f141c5 100644 --- a/server/proxy/pf_rdpgfx.h +++ b/server/proxy/pf_rdpgfx.h @@ -24,6 +24,7 @@ #include #include +#include #include "pf_context.h" diff --git a/server/proxy/pf_server.c b/server/proxy/pf_server.c index d010437..86fc21c 100644 --- a/server/proxy/pf_server.c +++ b/server/proxy/pf_server.c @@ -144,6 +144,12 @@ static BOOL pf_server_post_connect(freerdp_peer* client) ps = (pServerContext*)client->context; pdata = ps->pdata; + if (pdata->config->SessionCapture && !client->settings->SupportGraphicsPipeline) + { + WLog_ERR(TAG, "Session capture feature is enabled, only accepting connections with GFX"); + return FALSE; + } + pc = pf_context_create_client_context(client->settings); if (pc == NULL) { diff --git a/server/proxy/pf_update.c b/server/proxy/pf_update.c index 81cdaf4..17bbc64 100644 --- a/server/proxy/pf_update.c +++ b/server/proxy/pf_update.c @@ -20,9 +20,15 @@ */ #include +#include +#include #include "pf_update.h" +#include "pf_capture.h" #include "pf_context.h" +#include "pf_log.h" + +#define TAG PROXY_TAG("update") /* server callbacks */ @@ -66,7 +72,27 @@ static BOOL pf_client_end_paint(rdpContext* context) pClientContext* pc = (pClientContext*) context; proxyData* pdata = pc->pdata; rdpContext* ps = (rdpContext*)pdata->ps; - return ps->update->EndPaint(ps); + rdpGdi* gdi = context->gdi; + + /* proxy end paint */ + if (!ps->update->EndPaint(ps)) + return FALSE; + + if (!pdata->config->SessionCapture) + return TRUE; + + if (gdi->suppressOutput) + return TRUE; + + if (gdi->primary->hdc->hwnd->ninvalid < 1) + return TRUE; + + if (!pf_capture_save_frame(pc, gdi->primary_buffer)) + WLog_ERR(TAG, "failed to save captured frame!"); + + gdi->primary->hdc->hwnd->invalid->null = TRUE; + gdi->primary->hdc->hwnd->ninvalid = 0; + return TRUE; } static BOOL pf_client_bitmap_update(rdpContext* context, const BITMAP_UPDATE* bitmap) diff --git a/server/proxy/pf_update.h b/server/proxy/pf_update.h index 7f704aa..30a6a19 100644 --- a/server/proxy/pf_update.h +++ b/server/proxy/pf_update.h @@ -23,6 +23,10 @@ #define FREERDP_SERVER_PROXY_PFUPDATE_H #include +#include +#include + +#include "pf_context.h" void pf_server_register_update_callbacks(rdpUpdate* update); void pf_client_register_update_callbacks(rdpUpdate* update);