main neuswc / libswc / data.c
  1/* swc: data.c
  2 *
  3 * Copyright (c) 2013-2020 Michael Forney
  4 *
  5 * Permission is hereby granted, free of charge, to any person obtaining a copy
  6 * of this software and associated documentation files (the "Software"), to deal
  7 * in the Software without restriction, including without limitation the rights
  8 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9 * copies of the Software, and to permit persons to whom the Software is
 10 * furnished to do so, subject to the following conditions:
 11 *
 12 * The above copyright notice and this permission notice shall be included in
 13 * all copies or substantial portions of the Software.
 14 *
 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 18 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 21 * SOFTWARE.
 22 */
 23
 24#include "data.h"
 25#include "util.h"
 26
 27#include <stdlib.h>
 28#include <string.h>
 29#include <unistd.h>
 30#include <wayland-server.h>
 31
 32struct data {
 33	struct wl_array mime_types;
 34	struct wl_resource *source;
 35	struct wl_list offers;
 36};
 37
 38static void
 39offer_accept(struct wl_client *client, struct wl_resource *offer,
 40             uint32_t serial, const char *mime_type)
 41{
 42	struct data *data = wl_resource_get_user_data(offer);
 43
 44	/* Protect against expired data_offers being used. */
 45	if (!data) {
 46		return;
 47	}
 48
 49	wl_data_source_send_target(data->source, mime_type);
 50}
 51
 52static void
 53offer_receive(struct wl_client *client, struct wl_resource *offer,
 54              const char *mime_type, int fd)
 55{
 56	struct data *data = wl_resource_get_user_data(offer);
 57
 58	/* Protect against expired data_offers being used. */
 59	if (!data) {
 60		return;
 61	}
 62
 63	wl_data_source_send_send(data->source, mime_type, fd);
 64	close(fd);
 65}
 66
 67static void
 68offer_finish(struct wl_client *client, struct wl_resource *offer)
 69{
 70	(void)client;
 71	(void)offer;
 72	/* TODO: Implement */
 73}
 74
 75static void
 76offer_set_actions(struct wl_client *client, struct wl_resource *offer, uint32_t dnd_actions, uint32_t preferred_action)
 77{
 78	(void)client;
 79	(void)offer;
 80	(void)dnd_actions;
 81	(void)preferred_action;
 82	/* TODO: Implement */
 83}
 84
 85static const struct wl_data_offer_interface data_offer_impl = {
 86	.accept = offer_accept,
 87	.receive = offer_receive,
 88	.destroy = destroy_resource,
 89	.finish = offer_finish,
 90	.set_actions = offer_set_actions,
 91};
 92
 93static void
 94source_offer(struct wl_client *client, struct wl_resource *source,
 95             const char *mime_type)
 96{
 97	struct data *data = wl_resource_get_user_data(source);
 98	char *s, **dst;
 99
100	s = strdup(mime_type);
101	if (!s) {
102		goto error0;
103	}
104	dst = wl_array_add(&data->mime_types, sizeof(*dst));
105	if (!dst) {
106		goto error1;
107	}
108	*dst = s;
109	return;
110
111error1:
112	free(s);
113error0:
114	wl_resource_post_no_memory(source);
115}
116
117static void
118source_set_actions(struct wl_client *client, struct wl_resource *resource, uint32_t dnd_actions)
119{
120	(void)client;
121	(void)resource;
122	(void)dnd_actions;
123	/* TODO: Implement */
124}
125
126static const struct wl_data_source_interface data_source_impl = {
127	.offer = source_offer,
128	.destroy = destroy_resource,
129	.set_actions = source_set_actions,
130};
131
132static void
133data_destroy(struct wl_resource *source)
134{
135	struct data *data = wl_resource_get_user_data(source);
136	struct wl_resource *offer;
137	char **mime_type;
138
139	wl_array_for_each(mime_type, &data->mime_types) free(*mime_type);
140	wl_array_release(&data->mime_types);
141
142	/* After this data_source is destroyed, each of the data_offer objects
143	 * associated with the data_source has a pointer to a free'd struct. We
144	 * can't destroy the resources because this results in a segfault on the
145	 * client when it correctly tries to call data_source.destroy. However, a
146	 * misbehaving client could still attempt to call accept or receive on the
147	 * data_offer, which would crash the server.
148	 *
149	 * So, we clear the user data on each of the offers to protect us. */
150	wl_resource_for_each(offer, &data->offers)
151	{
152		wl_resource_set_user_data(offer, NULL);
153		wl_resource_set_destructor(offer, NULL);
154	}
155
156	free(data);
157}
158
159struct wl_resource *
160data_source_new(struct wl_client *client, uint32_t version, uint32_t id)
161{
162	struct data *data;
163
164	data = malloc(sizeof(*data));
165	if (!data) {
166		goto error0;
167	}
168	wl_array_init(&data->mime_types);
169	wl_list_init(&data->offers);
170
171	data->source =
172	    wl_resource_create(client, &wl_data_source_interface, version, id);
173	if (!data->source) {
174		goto error1;
175	}
176	wl_resource_set_implementation(data->source, &data_source_impl, data,
177	                               &data_destroy);
178
179	return data->source;
180
181error1:
182	free(data);
183error0:
184	return NULL;
185}
186
187struct wl_resource *
188data_offer_new(struct wl_client *client, struct wl_resource *source,
189               uint32_t version)
190{
191	struct data *data = wl_resource_get_user_data(source);
192	struct wl_resource *offer;
193
194	offer = wl_resource_create(client, &wl_data_offer_interface, version, 0);
195	if (!offer) {
196		return NULL;
197	}
198	wl_resource_set_implementation(offer, &data_offer_impl, data,
199	                               &remove_resource);
200	wl_list_insert(&data->offers, wl_resource_get_link(offer));
201
202	return offer;
203}
204
205void
206data_send_mime_types(struct wl_resource *source, struct wl_resource *offer)
207{
208	struct data *data = wl_resource_get_user_data(source);
209	char **mime_type;
210
211	wl_array_for_each(mime_type, &data->mime_types)
212	    wl_data_offer_send_offer(offer, *mime_type);
213}