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}