server: proxy: implement session capture feature
authorkubistika <kmizrachi18@gmail.com>
Tue, 22 Oct 2019 13:06:05 +0000 (16:06 +0300)
committerakallabeth <akallabeth@users.noreply.github.com>
Thu, 24 Oct 2019 10:19:31 +0000 (12:19 +0200)
14 files changed:
server/proxy/CMakeLists.txt
server/proxy/config.ini
server/proxy/pf_capture.c [new file with mode: 0644]
server/proxy/pf_capture.h [new file with mode: 0644]
server/proxy/pf_channels.c
server/proxy/pf_client.c
server/proxy/pf_config.c
server/proxy/pf_config.h
server/proxy/pf_context.h
server/proxy/pf_rdpgfx.c
server/proxy/pf_rdpgfx.h
server/proxy/pf_server.c
server/proxy/pf_update.c
server/proxy/pf_update.h

index c076f42..0ad4ef6 100644 (file)
@@ -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
   )
 
index 4e8edb6..cd34aee 100644 (file)
@@ -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 (file)
index 0000000..b52b42f
--- /dev/null
@@ -0,0 +1,77 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ * FreeRDP Proxy Server Session Capture
+ *
+ * Copyright 2019 Kobi Mizrachi <kmizrachi18@gmail.com>
+ *
+ * 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 <stdio.h>
+#include <string.h>
+
+#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 (file)
index 0000000..92aaf1c
--- /dev/null
@@ -0,0 +1,33 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ * FreeRDP Proxy Server Session Capture
+ *
+ * Copyright 2019 Kobi Mizrachi <kmizrachi18@gmail.com>
+ *
+ * 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 <winpr/image.h>
+#include <winpr/sysinfo.h>
+#include <winpr/path.h>
+#include <winpr/file.h>
+
+#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 */
index 15e74fc..6d8d316 100644 (file)
@@ -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)
        {
index c48d527..8c0c848 100644 (file)
@@ -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;
 }
index 44b6d60..a52fac5 100644 (file)
@@ -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);
index 553f8bb..c32d4bb 100644 (file)
@@ -22,6 +22,7 @@
 #define FREERDP_SERVER_PROXY_PFCONFIG_H
 
 #include <winpr/ini.h>
+#include <winpr/path.h>
 
 #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;
index b30dadb..3111233 100644 (file)
@@ -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;
 
index 74d11e8..74c24c7 100644 (file)
@@ -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;
index 4ac1f20..5f141c5 100644 (file)
@@ -24,6 +24,7 @@
 
 #include <freerdp/client/rdpgfx.h>
 #include <freerdp/server/rdpgfx.h>
+#include <freerdp/gdi/gfx.h>
 
 #include "pf_context.h"
 
index d010437..86fc21c 100644 (file)
@@ -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)
        {
index 81cdaf4..17bbc64 100644 (file)
  */
 
 #include <freerdp/display.h>
+#include <winpr/image.h>
+#include <winpr/sysinfo.h>
 
 #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)
index 7f704aa..30a6a19 100644 (file)
 #define FREERDP_SERVER_PROXY_PFUPDATE_H
 
 #include <freerdp/freerdp.h>
+#include <freerdp/gdi/gdi.h>
+#include <freerdp/gdi/bitmap.h>
+
+#include "pf_context.h"
 
 void pf_server_register_update_callbacks(rdpUpdate* update);
 void pf_client_register_update_callbacks(rdpUpdate* update);