--- /dev/null
+/**
+ * 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);
+}
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,
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,
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;
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);
}
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);
}
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;