Blame winpr/libwinpr/clipboard/posix.c

Packit Service fa4841
/**
Packit Service fa4841
 * WinPR: Windows Portable Runtime
Packit Service fa4841
 * Clipboard Functions: POSIX file handling
Packit Service fa4841
 *
Packit Service fa4841
 * Copyright 2017 Alexei Lozovsky <a.lozovsky@gmail.com>
Packit Service fa4841
 *
Packit Service fa4841
 * Licensed under the Apache License, Version 2.0 (the "License");
Packit Service fa4841
 * you may not use this file except in compliance with the License.
Packit Service fa4841
 * You may obtain a copy of the License at
Packit Service fa4841
 *
Packit Service fa4841
 *     http://www.apache.org/licenses/LICENSE-2.0
Packit Service fa4841
 *
Packit Service fa4841
 * Unless required by applicable law or agreed to in writing, software
Packit Service fa4841
 * distributed under the License is distributed on an "AS IS" BASIS,
Packit Service fa4841
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
Packit Service fa4841
 * See the License for the specific language governing permissions and
Packit Service fa4841
 * limitations under the License.
Packit Service fa4841
 */
Packit Service fa4841
Packit Service fa4841
#ifdef HAVE_CONFIG_H
Packit Service fa4841
#include "config.h"
Packit Service fa4841
#endif
Packit Service fa4841
Packit Service fa4841
#define _FILE_OFFSET_BITS 64
Packit Service fa4841
Packit Service fa4841
#include <stddef.h>
Packit Service fa4841
#include <stdlib.h>
Packit Service fa4841
#include <errno.h>
Packit Service fa4841
Packit Service fa4841
#include <dirent.h>
Packit Service fa4841
#include <fcntl.h>
Packit Service fa4841
#include <unistd.h>
Packit Service fa4841
#include <sys/types.h>
Packit Service fa4841
#include <sys/stat.h>
Packit Service fa4841
Packit Service fa4841
#include <winpr/crt.h>
Packit Service fa4841
#include <winpr/clipboard.h>
Packit Service fa4841
#include <winpr/collections.h>
Packit Service fa4841
#include <winpr/file.h>
Packit Service fa4841
#include <winpr/shell.h>
Packit Service fa4841
#include <winpr/string.h>
Packit Service fa4841
#include <winpr/wlog.h>
Packit Service fa4841
Packit Service fa4841
#include "clipboard.h"
Packit Service fa4841
#include "posix.h"
Packit Service fa4841
Packit Service fa4841
#include "../log.h"
Packit Service fa4841
#define TAG WINPR_TAG("clipboard.posix")
Packit Service fa4841
Packit Service fa4841
struct posix_file
Packit Service fa4841
{
Packit Service fa4841
	char* local_name;
Packit Service fa4841
	WCHAR* remote_name;
Packit Service fa4841
	BOOL is_directory;
Packit Service fa4841
Packit Service fa4841
	int fd;
Packit Service fa4841
	INT64 offset;
Packit Service fa4841
	INT64 size;
Packit Service fa4841
};
Packit Service fa4841
Packit Service fa4841
static struct posix_file* make_posix_file(const char* local_name, const WCHAR* remote_name)
Packit Service fa4841
{
Packit Service fa4841
	struct posix_file* file = NULL;
Packit Service fa4841
	struct stat statbuf;
Packit Service fa4841
	file = calloc(1, sizeof(*file));
Packit Service fa4841
Packit Service fa4841
	if (!file)
Packit Service fa4841
		return NULL;
Packit Service fa4841
Packit Service fa4841
	file->fd = -1;
Packit Service fa4841
	file->offset = 0;
Packit Service fa4841
	file->local_name = _strdup(local_name);
Packit Service fa4841
	file->remote_name = _wcsdup(remote_name);
Packit Service fa4841
Packit Service fa4841
	if (!file->local_name || !file->remote_name)
Packit Service fa4841
		goto error;
Packit Service fa4841
Packit Service fa4841
	if (stat(local_name, &statbuf))
Packit Service fa4841
	{
Packit Service fa4841
		int err = errno;
Packit Service fa4841
		WLog_ERR(TAG, "failed to stat %s: %s", local_name, strerror(err));
Packit Service fa4841
		goto error;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	file->is_directory = S_ISDIR(statbuf.st_mode);
Packit Service fa4841
	file->size = statbuf.st_size;
Packit Service fa4841
	return file;
Packit Service fa4841
error:
Packit Service fa4841
	free(file->local_name);
Packit Service fa4841
	free(file->remote_name);
Packit Service fa4841
	free(file);
Packit Service fa4841
	return NULL;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
static void free_posix_file(void* the_file)
Packit Service fa4841
{
Packit Service fa4841
	struct posix_file* file = the_file;
Packit Service fa4841
Packit Service fa4841
	if (!file)
Packit Service fa4841
		return;
Packit Service fa4841
Packit Service fa4841
	if (file->fd >= 0)
Packit Service fa4841
	{
Packit Service fa4841
		if (close(file->fd) < 0)
Packit Service fa4841
		{
Packit Service fa4841
			int err = errno;
Packit Service fa4841
			WLog_WARN(TAG, "failed to close fd %d: %s", file->fd, strerror(err));
Packit Service fa4841
		}
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	free(file->local_name);
Packit Service fa4841
	free(file->remote_name);
Packit Service fa4841
	free(file);
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
static unsigned char hex_to_dec(char c, BOOL* valid)
Packit Service fa4841
{
Packit Service fa4841
	if (('0' <= c) && (c <= '9'))
Packit Service fa4841
		return (c - '0');
Packit Service fa4841
Packit Service fa4841
	if (('a' <= c) && (c <= 'f'))
Packit Service fa4841
		return (c - 'a') + 10;
Packit Service fa4841
Packit Service fa4841
	if (('A' <= c) && (c <= 'F'))
Packit Service fa4841
		return (c - 'A') + 10;
Packit Service fa4841
Packit Service fa4841
	*valid = FALSE;
Packit Service fa4841
	return 0;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
static BOOL decode_percent_encoded_byte(const char* str, const char* end, char* value)
Packit Service fa4841
{
Packit Service fa4841
	BOOL valid = TRUE;
Packit Service fa4841
Packit Service fa4841
	if ((end < str) || ((size_t)(end - str) < strlen("%20")))
Packit Service fa4841
		return FALSE;
Packit Service fa4841
Packit Service fa4841
	*value = 0;
Packit Service fa4841
	*value |= hex_to_dec(str[1], &valid);
Packit Service fa4841
	*value <<= 4;
Packit Service fa4841
	*value |= hex_to_dec(str[2], &valid);
Packit Service fa4841
	return valid;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
static char* decode_percent_encoded_string(const char* str, size_t len)
Packit Service fa4841
{
Packit Service fa4841
	char* buffer = NULL;
Packit Service fa4841
	char* next = NULL;
Packit Service fa4841
	const char* cur = str;
Packit Service fa4841
	const char* lim = str + len;
Packit Service fa4841
	/* Percent decoding shrinks data length, so len bytes will be enough. */
Packit Service fa4841
	buffer = calloc(len + 1, sizeof(char));
Packit Service fa4841
Packit Service fa4841
	if (!buffer)
Packit Service fa4841
		return NULL;
Packit Service fa4841
Packit Service fa4841
	next = buffer;
Packit Service fa4841
Packit Service fa4841
	while (cur < lim)
Packit Service fa4841
	{
Packit Service fa4841
		if (*cur != '%')
Packit Service fa4841
		{
Packit Service fa4841
			*next++ = *cur++;
Packit Service fa4841
		}
Packit Service fa4841
		else
Packit Service fa4841
		{
Packit Service fa4841
			if (!decode_percent_encoded_byte(cur, lim, next))
Packit Service fa4841
			{
Packit Service fa4841
				WLog_ERR(TAG, "invalid percent encoding");
Packit Service fa4841
				goto error;
Packit Service fa4841
			}
Packit Service fa4841
Packit Service fa4841
			cur += strlen("%20");
Packit Service fa4841
			next += 1;
Packit Service fa4841
		}
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	return buffer;
Packit Service fa4841
error:
Packit Service fa4841
	free(buffer);
Packit Service fa4841
	return NULL;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
/*
Packit Service fa4841
 * Note that the function converts a single file name component,
Packit Service fa4841
 * it does not take care of component separators.
Packit Service fa4841
 */
Packit Service fa4841
static WCHAR* convert_local_name_component_to_remote(const char* local_name)
Packit Service fa4841
{
Packit Service fa4841
	WCHAR* remote_name = NULL;
Packit Service fa4841
Packit Service fa4841
	/*
Packit Service fa4841
	 * Note that local file names are not actually guaranteed to be
Packit Service fa4841
	 * encoded in UTF-8. Filesystems and users can use whatever they
Packit Service fa4841
	 * want. The OS does not care, aside from special treatment of
Packit Service fa4841
	 * '\0' and '/' bytes. But we need to make some decision here.
Packit Service fa4841
	 * Assuming UTF-8 is currently the most sane thing.
Packit Service fa4841
	 */
Packit Service fa4841
	if (!ConvertToUnicode(CP_UTF8, 0, local_name, -1, &remote_name, 0))
Packit Service fa4841
	{
Packit Service fa4841
		WLog_ERR(TAG, "Unicode conversion failed for %s", local_name);
Packit Service fa4841
		goto error;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	/*
Packit Service fa4841
	 * Some file names are not valid on Windows. Check for these now
Packit Service fa4841
	 * so that we won't get ourselves into a trouble later as such names
Packit Service fa4841
	 * are known to crash some Windows shells when pasted via clipboard.
Packit Service fa4841
	 */
Packit Service fa4841
	if (!ValidFileNameComponent(remote_name))
Packit Service fa4841
	{
Packit Service fa4841
		WLog_ERR(TAG, "invalid file name component: %s", local_name);
Packit Service fa4841
		goto error;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	return remote_name;
Packit Service fa4841
error:
Packit Service fa4841
	free(remote_name);
Packit Service fa4841
	return NULL;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
static char* concat_local_name(const char* dir, const char* file)
Packit Service fa4841
{
Packit Service fa4841
	size_t len_dir = 0;
Packit Service fa4841
	size_t len_file = 0;
Packit Service fa4841
	char* buffer = NULL;
Packit Service fa4841
	len_dir = strlen(dir);
Packit Service fa4841
	len_file = strlen(file);
Packit Service fa4841
	buffer = calloc(len_dir + 1 + len_file + 1, sizeof(char));
Packit Service fa4841
Packit Service fa4841
	if (!buffer)
Packit Service fa4841
		return NULL;
Packit Service fa4841
Packit Service fa4841
	memcpy(buffer, dir, len_dir * sizeof(char));
Packit Service fa4841
	buffer[len_dir] = '/';
Packit Service fa4841
	memcpy(buffer + len_dir + 1, file, len_file * sizeof(char));
Packit Service fa4841
	return buffer;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
static WCHAR* concat_remote_name(const WCHAR* dir, const WCHAR* file)
Packit Service fa4841
{
Packit Service fa4841
	size_t len_dir = 0;
Packit Service fa4841
	size_t len_file = 0;
Packit Service fa4841
	WCHAR* buffer = NULL;
Packit Service fa4841
	len_dir = _wcslen(dir);
Packit Service fa4841
	len_file = _wcslen(file);
Packit Service fa4841
	buffer = calloc(len_dir + 1 + len_file + 2, sizeof(WCHAR));
Packit Service fa4841
Packit Service fa4841
	if (!buffer)
Packit Service fa4841
		return NULL;
Packit Service fa4841
Packit Service fa4841
	memcpy(buffer, dir, len_dir * sizeof(WCHAR));
Packit Service fa4841
	buffer[len_dir] = L'\\';
Packit Service fa4841
	memcpy(buffer + len_dir + 1, file, len_file * sizeof(WCHAR));
Packit Service fa4841
	return buffer;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
static BOOL add_file_to_list(const char* local_name, const WCHAR* remote_name, wArrayList* files);
Packit Service fa4841
Packit Service fa4841
static BOOL add_directory_entry_to_list(const char* local_dir_name, const WCHAR* remote_dir_name,
Packit Service fa4841
                                        const struct dirent* entry, wArrayList* files)
Packit Service fa4841
{
Packit Service fa4841
	BOOL result = FALSE;
Packit Service fa4841
	char* local_name = NULL;
Packit Service fa4841
	WCHAR* remote_name = NULL;
Packit Service fa4841
	WCHAR* remote_base_name = NULL;
Packit Service fa4841
Packit Service fa4841
	/* Skip special directory entries. */
Packit Service fa4841
	if ((strcmp(entry->d_name, ".") == 0) || (strcmp(entry->d_name, "..") == 0))
Packit Service fa4841
		return TRUE;
Packit Service fa4841
Packit Service fa4841
	remote_base_name = convert_local_name_component_to_remote(entry->d_name);
Packit Service fa4841
Packit Service fa4841
	if (!remote_base_name)
Packit Service fa4841
		return FALSE;
Packit Service fa4841
Packit Service fa4841
	local_name = concat_local_name(local_dir_name, entry->d_name);
Packit Service fa4841
	remote_name = concat_remote_name(remote_dir_name, remote_base_name);
Packit Service fa4841
Packit Service fa4841
	if (local_name && remote_name)
Packit Service fa4841
		result = add_file_to_list(local_name, remote_name, files);
Packit Service fa4841
Packit Service fa4841
	free(remote_base_name);
Packit Service fa4841
	free(remote_name);
Packit Service fa4841
	free(local_name);
Packit Service fa4841
	return result;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
static BOOL do_add_directory_contents_to_list(const char* local_name, const WCHAR* remote_name,
Packit Service fa4841
                                              DIR* dirp, wArrayList* files)
Packit Service fa4841
{
Packit Service fa4841
	/*
Packit Service fa4841
	 * For some reason POSIX does not require readdir() to be thread-safe.
Packit Service fa4841
	 * However, readdir_r() has really insane interface and is pretty bad
Packit Service fa4841
	 * replacement for it. Fortunately, most C libraries guarantee thread-
Packit Service fa4841
	 * safety of readdir() when it is used for distinct directory streams.
Packit Service fa4841
	 *
Packit Service fa4841
	 * Thus we can use readdir() in multithreaded applications if we are
Packit Service fa4841
	 * sure that it will not corrupt some global data. It would be nice
Packit Service fa4841
	 * to have a compile-time check for this here, but some C libraries
Packit Service fa4841
	 * do not provide a #define because of reasons (I'm looking at you,
Packit Service fa4841
	 * musl). We should not be breaking people's builds because of that,
Packit Service fa4841
	 * so we do nothing and proceed with fingers crossed.
Packit Service fa4841
	 */
Packit Service fa4841
	for (;;)
Packit Service fa4841
	{
Packit Service fa4841
		struct dirent* entry = NULL;
Packit Service fa4841
		errno = 0;
Packit Service fa4841
		entry = readdir(dirp);
Packit Service fa4841
Packit Service fa4841
		if (!entry)
Packit Service fa4841
		{
Packit Service fa4841
			int err = errno;
Packit Service fa4841
Packit Service fa4841
			if (!err)
Packit Service fa4841
				break;
Packit Service fa4841
Packit Service fa4841
			WLog_ERR(TAG, "failed to read directory: %s", strerror(err));
Packit Service fa4841
			return FALSE;
Packit Service fa4841
		}
Packit Service fa4841
Packit Service fa4841
		if (!add_directory_entry_to_list(local_name, remote_name, entry, files))
Packit Service fa4841
			return FALSE;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	return TRUE;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
static BOOL add_directory_contents_to_list(const char* local_name, const WCHAR* remote_name,
Packit Service fa4841
                                           wArrayList* files)
Packit Service fa4841
{
Packit Service fa4841
	BOOL result = FALSE;
Packit Service fa4841
	DIR* dirp = NULL;
Packit Service fa4841
	WLog_VRB(TAG, "adding directory: %s", local_name);
Packit Service fa4841
	dirp = opendir(local_name);
Packit Service fa4841
Packit Service fa4841
	if (!dirp)
Packit Service fa4841
	{
Packit Service fa4841
		int err = errno;
Packit Service fa4841
		WLog_ERR(TAG, "failed to open directory %s: %s", local_name, strerror(err));
Packit Service fa4841
		goto out;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	result = do_add_directory_contents_to_list(local_name, remote_name, dirp, files);
Packit Service fa4841
Packit Service fa4841
	if (closedir(dirp))
Packit Service fa4841
	{
Packit Service fa4841
		int err = errno;
Packit Service fa4841
		WLog_WARN(TAG, "failed to close directory: %s", strerror(err));
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
out:
Packit Service fa4841
	return result;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
static BOOL add_file_to_list(const char* local_name, const WCHAR* remote_name, wArrayList* files)
Packit Service fa4841
{
Packit Service fa4841
	struct posix_file* file = NULL;
Packit Service fa4841
	WLog_VRB(TAG, "adding file: %s", local_name);
Packit Service fa4841
	file = make_posix_file(local_name, remote_name);
Packit Service fa4841
Packit Service fa4841
	if (!file)
Packit Service fa4841
		return FALSE;
Packit Service fa4841
Packit Service fa4841
	if (ArrayList_Add(files, file) < 0)
Packit Service fa4841
	{
Packit Service fa4841
		free_posix_file(file);
Packit Service fa4841
		return FALSE;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	if (file->is_directory)
Packit Service fa4841
	{
Packit Service fa4841
		/*
Packit Service fa4841
		 * This is effectively a recursive call, but we do not track
Packit Service fa4841
		 * recursion depth, thus filesystem loops can cause a crash.
Packit Service fa4841
		 */
Packit Service fa4841
		if (!add_directory_contents_to_list(local_name, remote_name, files))
Packit Service fa4841
			return FALSE;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	return TRUE;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
static const char* get_basename(const char* name)
Packit Service fa4841
{
Packit Service fa4841
	const char* c = name;
Packit Service fa4841
	const char* last_name = name;
Packit Service fa4841
Packit Service fa4841
	while (*c++)
Packit Service fa4841
	{
Packit Service fa4841
		if (*c == '/')
Packit Service fa4841
			last_name = c + 1;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	return last_name;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
static BOOL process_file_name(const char* local_name, wArrayList* files)
Packit Service fa4841
{
Packit Service fa4841
	BOOL result = FALSE;
Packit Service fa4841
	const char* base_name = NULL;
Packit Service fa4841
	WCHAR* remote_name = NULL;
Packit Service fa4841
	/*
Packit Service fa4841
	 * Start with the base name of the file. text/uri-list contains the
Packit Service fa4841
	 * exact files selected by the user, and we want the remote files
Packit Service fa4841
	 * to have names relative to that selection.
Packit Service fa4841
	 */
Packit Service fa4841
	base_name = get_basename(local_name);
Packit Service fa4841
	remote_name = convert_local_name_component_to_remote(base_name);
Packit Service fa4841
Packit Service fa4841
	if (!remote_name)
Packit Service fa4841
		return FALSE;
Packit Service fa4841
Packit Service fa4841
	result = add_file_to_list(local_name, remote_name, files);
Packit Service fa4841
	free(remote_name);
Packit Service fa4841
	return result;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
static BOOL process_uri(const char* uri, size_t uri_len, wArrayList* files)
Packit Service fa4841
{
Packit Service fa4841
	const char prefix[] = "file://";
Packit Service fa4841
	BOOL result = FALSE;
Packit Service fa4841
	char* name = NULL;
Packit Service fa4841
	const size_t prefixLen = strnlen(prefix, sizeof(prefix));
Packit Service fa4841
	WLog_VRB(TAG, "processing URI: %.*s", uri_len, uri);
Packit Service fa4841
Packit Service fa4841
	if ((uri_len < prefixLen) || strncmp(uri, prefix, prefixLen))
Packit Service fa4841
	{
Packit Service fa4841
		WLog_ERR(TAG, "non-'file://' URI schemes are not supported");
Packit Service fa4841
		goto out;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	name = decode_percent_encoded_string(uri + prefixLen, uri_len - prefixLen);
Packit Service fa4841
Packit Service fa4841
	if (!name)
Packit Service fa4841
		goto out;
Packit Service fa4841
Packit Service fa4841
	result = process_file_name(name, files);
Packit Service fa4841
out:
Packit Service fa4841
	free(name);
Packit Service fa4841
	return result;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
static BOOL process_uri_list(const char* data, size_t length, wArrayList* files)
Packit Service fa4841
{
Packit Service fa4841
	const char* cur = data;
Packit Service fa4841
	const char* lim = data + length;
Packit Service fa4841
	const char* start = NULL;
Packit Service fa4841
	const char* stop = NULL;
Packit Service fa4841
	WLog_VRB(TAG, "processing URI list:\n%.*s", length, data);
Packit Service fa4841
	ArrayList_Clear(files);
Packit Service fa4841
Packit Service fa4841
	/*
Packit Service fa4841
	 * The "text/uri-list" Internet Media Type is specified by RFC 2483.
Packit Service fa4841
	 *
Packit Service fa4841
	 * While the RFCs 2046 and 2483 require the lines of text/... formats
Packit Service fa4841
	 * to be terminated by CRLF sequence, be prepared for those who don't
Packit Service fa4841
	 * read the spec, use plain LFs, and don't leave the trailing CRLF.
Packit Service fa4841
	 */
Packit Service fa4841
Packit Service fa4841
	while (cur < lim)
Packit Service fa4841
	{
Packit Service fa4841
		BOOL comment = (*cur == '#');
Packit Service fa4841
		start = cur;
Packit Service fa4841
Packit Service fa4841
		for (stop = cur; stop < lim; stop++)
Packit Service fa4841
		{
Packit Service fa4841
			if (*stop == '\r')
Packit Service fa4841
			{
Packit Service fa4841
				if ((stop + 1 < lim) && (*(stop + 1) == '\n'))
Packit Service fa4841
					cur = stop + 2;
Packit Service fa4841
				else
Packit Service fa4841
					cur = stop + 1;
Packit Service fa4841
Packit Service fa4841
				break;
Packit Service fa4841
			}
Packit Service fa4841
Packit Service fa4841
			if (*stop == '\n')
Packit Service fa4841
			{
Packit Service fa4841
				cur = stop + 1;
Packit Service fa4841
				break;
Packit Service fa4841
			}
Packit Service fa4841
		}
Packit Service fa4841
Packit Service fa4841
		if (stop == lim)
Packit Service fa4841
			cur = lim;
Packit Service fa4841
Packit Service fa4841
		if (comment)
Packit Service fa4841
			continue;
Packit Service fa4841
Packit Service fa4841
		if (!process_uri(start, stop - start, files))
Packit Service fa4841
			return FALSE;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	return TRUE;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
static BOOL convert_local_file_to_filedescriptor(const struct posix_file* file,
Packit Service fa4841
                                                 FILEDESCRIPTOR* descriptor)
Packit Service fa4841
{
Packit Service fa4841
	size_t remote_len = 0;
Packit Service fa4841
	descriptor->dwFlags = FD_ATTRIBUTES | FD_FILESIZE | FD_SHOWPROGRESSUI;
Packit Service fa4841
Packit Service fa4841
	if (file->is_directory)
Packit Service fa4841
	{
Packit Service fa4841
		descriptor->dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY;
Packit Service fa4841
		descriptor->nFileSizeLow = 0;
Packit Service fa4841
		descriptor->nFileSizeHigh = 0;
Packit Service fa4841
	}
Packit Service fa4841
	else
Packit Service fa4841
	{
Packit Service fa4841
		descriptor->dwFileAttributes = FILE_ATTRIBUTE_NORMAL;
Packit Service fa4841
		descriptor->nFileSizeLow = (file->size >> 0) & 0xFFFFFFFF;
Packit Service fa4841
		descriptor->nFileSizeHigh = (file->size >> 32) & 0xFFFFFFFF;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	remote_len = _wcslen(file->remote_name);
Packit Service fa4841
Packit Service fa4841
	if (remote_len + 1 > ARRAYSIZE(descriptor->cFileName))
Packit Service fa4841
	{
Packit Service fa4841
		WLog_ERR(TAG, "file name too long (%" PRIuz " characters)", remote_len);
Packit Service fa4841
		return FALSE;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	memcpy(descriptor->cFileName, file->remote_name, remote_len * sizeof(WCHAR));
Packit Service fa4841
	return TRUE;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
static FILEDESCRIPTOR* convert_local_file_list_to_filedescriptors(wArrayList* files)
Packit Service fa4841
{
Packit Service fa4841
	int i;
Packit Service fa4841
	int count = 0;
Packit Service fa4841
	FILEDESCRIPTOR* descriptors = NULL;
Packit Service fa4841
	count = ArrayList_Count(files);
Packit Service fa4841
	descriptors = calloc(count, sizeof(descriptors[0]));
Packit Service fa4841
Packit Service fa4841
	if (!descriptors)
Packit Service fa4841
		goto error;
Packit Service fa4841
Packit Service fa4841
	for (i = 0; i < count; i++)
Packit Service fa4841
	{
Packit Service fa4841
		const struct posix_file* file = ArrayList_GetItem(files, i);
Packit Service fa4841
Packit Service fa4841
		if (!convert_local_file_to_filedescriptor(file, &descriptors[i]))
Packit Service fa4841
			goto error;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	return descriptors;
Packit Service fa4841
error:
Packit Service fa4841
	free(descriptors);
Packit Service fa4841
	return NULL;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
static void* convert_uri_list_to_filedescriptors(wClipboard* clipboard, UINT32 formatId,
Packit Service fa4841
                                                 const void* data, UINT32* pSize)
Packit Service fa4841
{
Packit Service fa4841
	FILEDESCRIPTOR* descriptors = NULL;
Packit Service fa4841
Packit Service fa4841
	if (!clipboard || !data || !pSize)
Packit Service fa4841
		return NULL;
Packit Service fa4841
Packit Service fa4841
	if (formatId != ClipboardGetFormatId(clipboard, "text/uri-list"))
Packit Service fa4841
		return NULL;
Packit Service fa4841
Packit Service fa4841
	if (!process_uri_list((const char*)data, *pSize, clipboard->localFiles))
Packit Service fa4841
		return NULL;
Packit Service fa4841
Packit Service fa4841
	descriptors = convert_local_file_list_to_filedescriptors(clipboard->localFiles);
Packit Service fa4841
Packit Service fa4841
	if (!descriptors)
Packit Service fa4841
		return NULL;
Packit Service fa4841
Packit Service fa4841
	*pSize = ArrayList_Count(clipboard->localFiles) * sizeof(FILEDESCRIPTOR);
Packit Service fa4841
	clipboard->fileListSequenceNumber = clipboard->sequenceNumber;
Packit Service fa4841
	return descriptors;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
static void* convert_filedescriptors_to_uri_list(wClipboard* clipboard, UINT32 formatId,
Packit Service fa4841
                                                 const void* data, UINT32* pSize)
Packit Service fa4841
{
Packit Service fa4841
	const FILEDESCRIPTOR* descriptors;
Packit Service fa4841
	UINT32 nrDescriptors = 0;
Packit Service fa4841
	size_t count, x, alloc, pos, baseLength = 0;
Packit Service fa4841
	const char* src = (const char*)data;
Packit Service fa4841
	char* dst;
Packit Service fa4841
Packit Service fa4841
	if (!clipboard || !data || !pSize)
Packit Service fa4841
		return NULL;
Packit Service fa4841
Packit Service fa4841
	if (*pSize < sizeof(UINT32))
Packit Service fa4841
		return NULL;
Packit Service fa4841
Packit Service fa4841
	if (clipboard->delegate.basePath)
Packit Service fa4841
		baseLength = strnlen(clipboard->delegate.basePath, MAX_PATH);
Packit Service fa4841
Packit Service fa4841
	if (baseLength < 1)
Packit Service fa4841
		return NULL;
Packit Service fa4841
Packit Service fa4841
	if (clipboard->delegate.ClientRequestFileSize)
Packit Service fa4841
		nrDescriptors = (UINT32)(src[3] << 24) | (UINT32)(src[2] << 16) | (UINT32)(src[1] << 8) |
Packit Service fa4841
		                (src[0] & 0xFF);
Packit Service fa4841
Packit Service fa4841
	count = (*pSize - 4) / sizeof(FILEDESCRIPTOR);
Packit Service fa4841
Packit Service fa4841
	if ((count < 1) || (count != nrDescriptors))
Packit Service fa4841
		return NULL;
Packit Service fa4841
Packit Service fa4841
	descriptors = (const FILEDESCRIPTOR*)&src[4];
Packit Service fa4841
Packit Service fa4841
	if (formatId != ClipboardGetFormatId(clipboard, "FileGroupDescriptorW"))
Packit Service fa4841
		return NULL;
Packit Service fa4841
Packit Service fa4841
	alloc = 0;
Packit Service fa4841
Packit Service fa4841
	/* Get total size of file names */
Packit Service fa4841
	for (x = 0; x < count; x++)
Packit Service fa4841
		alloc += _wcsnlen(descriptors[x].cFileName, ARRAYSIZE(descriptors[x].cFileName));
Packit Service fa4841
Packit Service fa4841
	/* Append a prefix file:// and postfix \r\n for each file */
Packit Service fa4841
	alloc += (sizeof("/\r\n") + baseLength) * count;
Packit Service fa4841
	dst = calloc(alloc, sizeof(char));
Packit Service fa4841
Packit Service fa4841
	if (!dst)
Packit Service fa4841
		return NULL;
Packit Service fa4841
Packit Service fa4841
	pos = 0;
Packit Service fa4841
Packit Service fa4841
	for (x = 0; x < count; x++)
Packit Service fa4841
	{
Packit Service fa4841
		int rc;
Packit Service fa4841
		const FILEDESCRIPTOR* cur = &descriptors[x];
Packit Service fa4841
		size_t curLen = _wcsnlen(cur->cFileName, ARRAYSIZE(cur->cFileName));
Packit Service fa4841
		char* curName = NULL;
Packit Service fa4841
		rc = ConvertFromUnicode(CP_UTF8, 0, cur->cFileName, (int)curLen, &curName, 0, NULL, NULL);
Packit Service fa4841
Packit Service fa4841
		if (rc != (int)curLen)
Packit Service fa4841
		{
Packit Service fa4841
			free(curName);
Packit Service fa4841
			free(dst);
Packit Service fa4841
			return NULL;
Packit Service fa4841
		}
Packit Service fa4841
Packit Service fa4841
		rc = _snprintf(&dst[pos], alloc - pos, "%s/%s\r\n", clipboard->delegate.basePath, curName);
Packit Service fa4841
		free(curName);
Packit Service fa4841
Packit Service fa4841
		if (rc < 0)
Packit Service fa4841
		{
Packit Service fa4841
			free(dst);
Packit Service fa4841
			return NULL;
Packit Service fa4841
		}
Packit Service fa4841
Packit Service fa4841
		pos += (size_t)rc;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	*pSize = (UINT32)alloc;
Packit Service fa4841
	clipboard->fileListSequenceNumber = clipboard->sequenceNumber;
Packit Service fa4841
	return dst;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
static BOOL register_file_formats_and_synthesizers(wClipboard* clipboard)
Packit Service fa4841
{
Packit Service fa4841
	UINT32 file_group_format_id;
Packit Service fa4841
	UINT32 local_file_format_id;
Packit Service fa4841
	file_group_format_id = ClipboardRegisterFormat(clipboard, "FileGroupDescriptorW");
Packit Service fa4841
	local_file_format_id = ClipboardRegisterFormat(clipboard, "text/uri-list");
Packit Service fa4841
Packit Service fa4841
	if (!file_group_format_id || !local_file_format_id)
Packit Service fa4841
		goto error;
Packit Service fa4841
Packit Service fa4841
	clipboard->localFiles = ArrayList_New(FALSE);
Packit Service fa4841
Packit Service fa4841
	if (!clipboard->localFiles)
Packit Service fa4841
		goto error;
Packit Service fa4841
Packit Service fa4841
	ArrayList_Object(clipboard->localFiles)->fnObjectFree = free_posix_file;
Packit Service fa4841
Packit Service fa4841
	if (!ClipboardRegisterSynthesizer(clipboard, local_file_format_id, file_group_format_id,
Packit Service fa4841
	                                  convert_uri_list_to_filedescriptors))
Packit Service fa4841
		goto error_free_local_files;
Packit Service fa4841
Packit Service fa4841
	if (!ClipboardRegisterSynthesizer(clipboard, file_group_format_id, local_file_format_id,
Packit Service fa4841
	                                  convert_filedescriptors_to_uri_list))
Packit Service fa4841
		goto error_free_local_files;
Packit Service fa4841
Packit Service fa4841
	return TRUE;
Packit Service fa4841
error_free_local_files:
Packit Service fa4841
	ArrayList_Free(clipboard->localFiles);
Packit Service fa4841
	clipboard->localFiles = NULL;
Packit Service fa4841
error:
Packit Service fa4841
	return FALSE;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
static UINT posix_file_get_size(const struct posix_file* file, INT64* size)
Packit Service fa4841
{
Packit Service fa4841
	struct stat statbuf;
Packit Service fa4841
Packit Service fa4841
	if (stat(file->local_name, &statbuf) < 0)
Packit Service fa4841
	{
Packit Service fa4841
		int err = errno;
Packit Service fa4841
		WLog_ERR(TAG, "failed to stat %s: %s", file->local_name, strerror(err));
Packit Service fa4841
		return ERROR_FILE_INVALID;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	*size = statbuf.st_size;
Packit Service fa4841
	return NO_ERROR;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
static UINT posix_file_request_size(wClipboardDelegate* delegate,
Packit Service fa4841
                                    const wClipboardFileSizeRequest* request)
Packit Service fa4841
{
Packit Service fa4841
	UINT error = NO_ERROR;
Packit Service fa4841
	INT64 size = 0;
Packit Service fa4841
	struct posix_file* file = NULL;
Packit Service fa4841
Packit Service fa4841
	if (!delegate || !delegate->clipboard || !request)
Packit Service fa4841
		return ERROR_BAD_ARGUMENTS;
Packit Service fa4841
Packit Service fa4841
	if (delegate->clipboard->sequenceNumber != delegate->clipboard->fileListSequenceNumber)
Packit Service fa4841
		return ERROR_INVALID_STATE;
Packit Service fa4841
Packit Service fa4841
	file = ArrayList_GetItem(delegate->clipboard->localFiles, request->listIndex);
Packit Service fa4841
Packit Service fa4841
	if (!file)
Packit Service fa4841
		return ERROR_INDEX_ABSENT;
Packit Service fa4841
Packit Service fa4841
	error = posix_file_get_size(file, &size);
Packit Service fa4841
Packit Service fa4841
	if (error)
Packit Service fa4841
		error = delegate->ClipboardFileSizeFailure(delegate, request, error);
Packit Service fa4841
	else
Packit Service fa4841
		error = delegate->ClipboardFileSizeSuccess(delegate, request, size);
Packit Service fa4841
Packit Service fa4841
	if (error)
Packit Service fa4841
		WLog_WARN(TAG, "failed to report file size result: 0x%08X", error);
Packit Service fa4841
Packit Service fa4841
	return NO_ERROR;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
static UINT posix_file_read_open(struct posix_file* file)
Packit Service fa4841
{
Packit Service fa4841
	struct stat statbuf;
Packit Service fa4841
Packit Service fa4841
	if (file->fd >= 0)
Packit Service fa4841
		return NO_ERROR;
Packit Service fa4841
Packit Service fa4841
	file->fd = open(file->local_name, O_RDONLY);
Packit Service fa4841
Packit Service fa4841
	if (file->fd < 0)
Packit Service fa4841
	{
Packit Service fa4841
		int err = errno;
Packit Service fa4841
		WLog_ERR(TAG, "failed to open file %s: %s", file->local_name, strerror(err));
Packit Service fa4841
		return ERROR_FILE_NOT_FOUND;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	if (fstat(file->fd, &statbuf) < 0)
Packit Service fa4841
	{
Packit Service fa4841
		int err = errno;
Packit Service fa4841
		WLog_ERR(TAG, "failed to stat file: %s", strerror(err));
Packit Service fa4841
		return ERROR_FILE_INVALID;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	file->offset = 0;
Packit Service fa4841
	file->size = statbuf.st_size;
Packit Service fa4841
	WLog_VRB(TAG, "open file %d -> %s", file->fd, file->local_name);
Packit Service fa4841
	WLog_VRB(TAG, "file %d size: %" PRIu64 " bytes", file->fd, file->size);
Packit Service fa4841
	return NO_ERROR;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
static UINT posix_file_read_seek(struct posix_file* file, UINT64 offset)
Packit Service fa4841
{
Packit Service fa4841
	/*
Packit Service fa4841
	 * We should avoid seeking when possible as some filesystems (e.g.,
Packit Service fa4841
	 * an FTP server mapped via FUSE) may not support seeking. We keep
Packit Service fa4841
	 * an accurate account of the current file offset and do not call
Packit Service fa4841
	 * lseek() if the client requests file content sequentially.
Packit Service fa4841
	 */
Packit Service fa4841
	if (offset > INT64_MAX)
Packit Service fa4841
		return ERROR_SEEK;
Packit Service fa4841
Packit Service fa4841
	if (file->offset == (INT64)offset)
Packit Service fa4841
		return NO_ERROR;
Packit Service fa4841
Packit Service fa4841
	WLog_VRB(TAG, "file %d force seeking to %" PRIu64 ", current %" PRIu64, file->fd, offset,
Packit Service fa4841
	         file->offset);
Packit Service fa4841
Packit Service fa4841
	if (lseek(file->fd, (off_t)offset, SEEK_SET) < 0)
Packit Service fa4841
	{
Packit Service fa4841
		int err = errno;
Packit Service fa4841
		WLog_ERR(TAG, "failed to seek file: %s", strerror(err));
Packit Service fa4841
		return ERROR_SEEK;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	return NO_ERROR;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
static UINT posix_file_read_perform(struct posix_file* file, UINT32 size, BYTE** actual_data,
Packit Service fa4841
                                    UINT32* actual_size)
Packit Service fa4841
{
Packit Service fa4841
	BYTE* buffer = NULL;
Packit Service fa4841
	ssize_t amount = 0;
Packit Service fa4841
	WLog_VRB(TAG, "file %d request read %" PRIu32 " bytes", file->fd, size);
Packit Service fa4841
	buffer = malloc(size);
Packit Service fa4841
Packit Service fa4841
	if (!buffer)
Packit Service fa4841
	{
Packit Service fa4841
		WLog_ERR(TAG, "failed to allocate %" PRIu32 " buffer bytes", size);
Packit Service fa4841
		return ERROR_NOT_ENOUGH_MEMORY;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	amount = read(file->fd, buffer, size);
Packit Service fa4841
Packit Service fa4841
	if (amount < 0)
Packit Service fa4841
	{
Packit Service fa4841
		int err = errno;
Packit Service fa4841
		WLog_ERR(TAG, "failed to read file: %s", strerror(err));
Packit Service fa4841
		goto error;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	*actual_data = buffer;
Packit Service fa4841
	*actual_size = amount;
Packit Service fa4841
	file->offset += amount;
Packit Service fa4841
	WLog_VRB(TAG, "file %d actual read %" PRIu32 " bytes (offset %" PRIu64 ")", file->fd, amount,
Packit Service fa4841
	         file->offset);
Packit Service fa4841
	return NO_ERROR;
Packit Service fa4841
error:
Packit Service fa4841
	free(buffer);
Packit Service fa4841
	return ERROR_READ_FAULT;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
static UINT posix_file_read_close(struct posix_file* file)
Packit Service fa4841
{
Packit Service fa4841
	if (file->fd < 0)
Packit Service fa4841
		return NO_ERROR;
Packit Service fa4841
Packit Service fa4841
	if (file->offset == file->size)
Packit Service fa4841
	{
Packit Service fa4841
		WLog_VRB(TAG, "close file %d", file->fd);
Packit Service fa4841
Packit Service fa4841
		if (close(file->fd) < 0)
Packit Service fa4841
		{
Packit Service fa4841
			int err = errno;
Packit Service fa4841
			WLog_WARN(TAG, "failed to close fd %d: %s", file->fd, strerror(err));
Packit Service fa4841
		}
Packit Service fa4841
Packit Service fa4841
		file->fd = -1;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	return NO_ERROR;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
static UINT posix_file_get_range(struct posix_file* file, UINT64 offset, UINT32 size,
Packit Service fa4841
                                 BYTE** actual_data, UINT32* actual_size)
Packit Service fa4841
{
Packit Service fa4841
	UINT error = NO_ERROR;
Packit Service fa4841
	error = posix_file_read_open(file);
Packit Service fa4841
Packit Service fa4841
	if (error)
Packit Service fa4841
		goto out;
Packit Service fa4841
Packit Service fa4841
	error = posix_file_read_seek(file, offset);
Packit Service fa4841
Packit Service fa4841
	if (error)
Packit Service fa4841
		goto out;
Packit Service fa4841
Packit Service fa4841
	error = posix_file_read_perform(file, size, actual_data, actual_size);
Packit Service fa4841
Packit Service fa4841
	if (error)
Packit Service fa4841
		goto out;
Packit Service fa4841
Packit Service fa4841
	error = posix_file_read_close(file);
Packit Service fa4841
Packit Service fa4841
	if (error)
Packit Service fa4841
		goto out;
Packit Service fa4841
Packit Service fa4841
out:
Packit Service fa4841
	return error;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
static UINT posix_file_request_range(wClipboardDelegate* delegate,
Packit Service fa4841
                                     const wClipboardFileRangeRequest* request)
Packit Service fa4841
{
Packit Service fa4841
	UINT error = 0;
Packit Service fa4841
	BYTE* data = NULL;
Packit Service fa4841
	UINT32 size = 0;
Packit Service fa4841
	UINT64 offset = 0;
Packit Service fa4841
	struct posix_file* file = NULL;
Packit Service fa4841
Packit Service fa4841
	if (!delegate || !delegate->clipboard || !request)
Packit Service fa4841
		return ERROR_BAD_ARGUMENTS;
Packit Service fa4841
Packit Service fa4841
	if (delegate->clipboard->sequenceNumber != delegate->clipboard->fileListSequenceNumber)
Packit Service fa4841
		return ERROR_INVALID_STATE;
Packit Service fa4841
Packit Service fa4841
	file = ArrayList_GetItem(delegate->clipboard->localFiles, request->listIndex);
Packit Service fa4841
Packit Service fa4841
	if (!file)
Packit Service fa4841
		return ERROR_INDEX_ABSENT;
Packit Service fa4841
Packit Service fa4841
	offset = (((UINT64)request->nPositionHigh) << 32) | ((UINT64)request->nPositionLow);
Packit Service fa4841
	error = posix_file_get_range(file, offset, request->cbRequested, &data, &size);
Packit Service fa4841
Packit Service fa4841
	if (error)
Packit Service fa4841
		error = delegate->ClipboardFileRangeFailure(delegate, request, error);
Packit Service fa4841
	else
Packit Service fa4841
		error = delegate->ClipboardFileRangeSuccess(delegate, request, data, size);
Packit Service fa4841
Packit Service fa4841
	if (error)
Packit Service fa4841
		WLog_WARN(TAG, "failed to report file range result: 0x%08X", error);
Packit Service fa4841
Packit Service fa4841
	free(data);
Packit Service fa4841
	return NO_ERROR;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
static UINT dummy_file_size_success(wClipboardDelegate* delegate,
Packit Service fa4841
                                    const wClipboardFileSizeRequest* request, UINT64 fileSize)
Packit Service fa4841
{
Packit Service fa4841
	return ERROR_NOT_SUPPORTED;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
static UINT dummy_file_size_failure(wClipboardDelegate* delegate,
Packit Service fa4841
                                    const wClipboardFileSizeRequest* request, UINT errorCode)
Packit Service fa4841
{
Packit Service fa4841
	return ERROR_NOT_SUPPORTED;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
static UINT dummy_file_range_success(wClipboardDelegate* delegate,
Packit Service fa4841
                                     const wClipboardFileRangeRequest* request, const BYTE* data,
Packit Service fa4841
                                     UINT32 size)
Packit Service fa4841
{
Packit Service fa4841
	return ERROR_NOT_SUPPORTED;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
static UINT dummy_file_range_failure(wClipboardDelegate* delegate,
Packit Service fa4841
                                     const wClipboardFileRangeRequest* request, UINT errorCode)
Packit Service fa4841
{
Packit Service fa4841
	return ERROR_NOT_SUPPORTED;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
static void setup_delegate(wClipboardDelegate* delegate)
Packit Service fa4841
{
Packit Service fa4841
	delegate->ClientRequestFileSize = posix_file_request_size;
Packit Service fa4841
	delegate->ClipboardFileSizeSuccess = dummy_file_size_success;
Packit Service fa4841
	delegate->ClipboardFileSizeFailure = dummy_file_size_failure;
Packit Service fa4841
	delegate->ClientRequestFileRange = posix_file_request_range;
Packit Service fa4841
	delegate->ClipboardFileRangeSuccess = dummy_file_range_success;
Packit Service fa4841
	delegate->ClipboardFileRangeFailure = dummy_file_range_failure;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
BOOL ClipboardInitPosixFileSubsystem(wClipboard* clipboard)
Packit Service fa4841
{
Packit Service fa4841
	if (!clipboard)
Packit Service fa4841
		return FALSE;
Packit Service fa4841
Packit Service fa4841
	if (!register_file_formats_and_synthesizers(clipboard))
Packit Service fa4841
		return FALSE;
Packit Service fa4841
Packit Service fa4841
	setup_delegate(&clipboard->delegate);
Packit Service fa4841
	return TRUE;
Packit Service fa4841
}