wClipboard/posix: implement file size retrieval
authorilammy <a.lozovsky@gmail.com>
Sat, 8 Apr 2017 23:29:51 +0000 (02:29 +0300)
committerilammy <a.lozovsky@gmail.com>
Sun, 9 Apr 2017 00:15:49 +0000 (03:15 +0300)
This is an example of wClipboardDelegate method implementation. POSIX
subsystem uses synchronous methods, but the interface can be used for
asynchronous request processing as well. The client should call a
Client* callback to request some action and the wClipboard will process
the request and report the result by calling an approriate Clipboard*
callback. Usually there will be two callbacks: one for reporting success
and one to report errors.

All callbacks have at least two arguments: the wClipboardDelegate itself
to pass the system context, and the wClipboard*Request structure with
the arguments to pass the call context. The request context is also
passed to the result callbacks by wClipboard so that the client can
match up the result with its previous request.

The fields of wClipboard*Request structures are heavily influenced by
the MS-RDPECLIP spec and mirror the respective fields of
CLIPRDR_FILECONTENTS_REQUEST. wClipboard should not depend on
MS-RDPECLIP, that's the reason we don't use CLIPRDR_FILECONTENTS_REQUEST
directly. However, I believe that we should not have void* fields in the
request structs so that they can be easily copied around if needed.
This is why have the weird 'streamId' field there which has nothing to
do with wClipboard and will be used only by the clients when sending
replies to the server.

Return values of the callbacks are to be used for reporting errors with
processing the request or reply per se, not for errors encountered while
performing the action requested. Thus, for example, we return NO_ERROR
from posix_file_request_size() even when we fail to report the result to
the client, because we have successfully performed the request and do
not care if the client could not handle our reply for some reason.

Also note that setup_delegate() fills in dummy implementations of
Clipboard* reply callbacks so that we do not crash in case the client
does not fill them and do not have to perform paranoid NULL checks
before calling every single callback.

winpr/include/winpr/clipboard.h
winpr/libwinpr/clipboard/posix.c

index 4caaf12..ebede56 100644 (file)
@@ -27,12 +27,25 @@ typedef struct _wClipboard wClipboard;
 
 typedef void* (*CLIPBOARD_SYNTHESIZE_FN)(wClipboard* clipboard, UINT32 formatId, const void* data, UINT32* pSize);
 
+struct _wClipboardFileSizeRequest
+{
+       UINT32 streamId;
+       UINT32 listIndex;
+};
+typedef struct _wClipboardFileSizeRequest wClipboardFileSizeRequest;
+
 typedef struct _wClipboardDelegate wClipboardDelegate;
 
 struct _wClipboardDelegate
 {
        wClipboard* clipboard;
        void* custom;
+
+       UINT (*ClientRequestFileSize)(wClipboardDelegate*, const wClipboardFileSizeRequest*);
+       UINT (*ClipboardFileSizeSuccess)(wClipboardDelegate*, const wClipboardFileSizeRequest*,
+                       UINT64 fileSize);
+       UINT (*ClipboardFileSizeFailure)(wClipboardDelegate*, const wClipboardFileSizeRequest*,
+                       UINT errorCode);
 };
 
 #ifdef __cplusplus
index bf7e855..da934dc 100644 (file)
@@ -590,6 +590,66 @@ error:
        return FALSE;
 }
 
+static UINT posix_file_get_size(const struct posix_file* file, off_t* size)
+{
+       struct stat statbuf;
+
+       if (stat(file->local_name, &statbuf) < 0)
+       {
+               int err = errno;
+               WLog_ERR(TAG, "failed to stat %s: %s", file->local_name, strerror(err));
+               return ERROR_FILE_INVALID;
+       }
+
+       *size = statbuf.st_size;
+
+       return NO_ERROR;
+}
+
+static UINT posix_file_request_size(wClipboardDelegate* delegate,
+               const wClipboardFileSizeRequest* request)
+{
+       UINT error = NO_ERROR;
+       off_t size = 0;
+       struct posix_file* file = NULL;
+
+       if (!delegate || !delegate->clipboard || !request)
+               return ERROR_BAD_ARGUMENTS;
+
+       file = ArrayList_GetItem(delegate->clipboard->localFiles, request->listIndex);
+       if (!file)
+               return ERROR_INDEX_ABSENT;
+
+       error = posix_file_get_size(file, &size);
+
+       if (error)
+               error = delegate->ClipboardFileSizeFailure(delegate, request, error);
+       else
+               error = delegate->ClipboardFileSizeSuccess(delegate, request, size);
+
+       if (error)
+               WLog_WARN(TAG, "failed to report file size result: 0x%08X", error);
+
+       return NO_ERROR;
+}
+
+static UINT dummy_file_size_success(wClipboardDelegate* delegate, const wClipboardFileSizeRequest* request, UINT64 fileSize)
+{
+       return ERROR_NOT_SUPPORTED;
+}
+
+static UINT dummy_file_size_failure(wClipboardDelegate* delegate, const wClipboardFileSizeRequest* request, UINT errorCode)
+{
+       return ERROR_NOT_SUPPORTED;
+}
+
+static void setup_delegate(wClipboardDelegate* delegate)
+{
+       delegate->ClientRequestFileSize = posix_file_request_size;
+       delegate->ClipboardFileSizeSuccess = dummy_file_size_success;
+       delegate->ClipboardFileSizeFailure = dummy_file_size_failure;
+}
+
 BOOL ClipboardInitPosixFileSubsystem(wClipboard* clipboard)
 {
        if (!clipboard)
@@ -598,5 +658,7 @@ BOOL ClipboardInitPosixFileSubsystem(wClipboard* clipboard)
        if (!register_file_formats_and_synthesizers(clipboard))
                return FALSE;
 
+       setup_delegate(&clipboard->delegate);
+
        return TRUE;
 }