1/************************************************************
2
3Copyright 1987, 1989, 1998  The Open Group
4
5Permission to use, copy, modify, distribute, and sell this software and its
6documentation for any purpose is hereby granted without fee, provided that
7the above copyright notice appear in all copies and that both that
8copyright notice and this permission notice appear in supporting
9documentation.
10
11The above copyright notice and this permission notice shall be included in
12all copies or substantial portions of the Software.
13
14THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
17OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
18AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20
21Except as contained in this notice, the name of The Open Group shall not be
22used in advertising or otherwise to promote the sale, use or other dealings
23in this Software without prior written authorization from The Open Group.
24
25
26Copyright 1987, 1989 by Digital Equipment Corporation, Maynard, Massachusetts.
27
28                        All Rights Reserved
29
30Permission to use, copy, modify, and distribute this software and its
31documentation for any purpose and without fee is hereby granted,
32provided that the above copyright notice appear in all copies and that
33both that copyright notice and this permission notice appear in
34supporting documentation, and that the name of Digital not be
35used in advertising or publicity pertaining to distribution of the
36software without specific, written prior permission.
37
38DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
39ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
40DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
41ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
42WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
43ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
44SOFTWARE.
45
46********************************************************/
47
48#ifdef HAVE_DIX_CONFIG_H
49#include <dix-config.h>
50#endif
51
52#include "windowstr.h"
53#include "dixstruct.h"
54#include "dispatch.h"
55#include "selection.h"
56#include "xace.h"
57
58/*****************************************************************
59 * Selection Stuff
60 *
61 *    dixLookupSelection
62 *
63 *   Selections are global to the server.  The list of selections should
64 *   not be traversed directly.  Instead, use the functions listed above.
65 *
66 *****************************************************************/
67
68Selection *CurrentSelections;
69CallbackListPtr SelectionCallback;
70
71int
72dixLookupSelection(Selection **result, Atom selectionName,
73		   ClientPtr client, Mask access_mode)
74{
75    Selection *pSel;
76    int rc = BadMatch;
77    client->errorValue = selectionName;
78
79    for (pSel = CurrentSelections; pSel; pSel = pSel->next)
80	if (pSel->selection == selectionName)
81	    break;
82
83    if (pSel)
84	rc = XaceHookSelectionAccess(client, &pSel, access_mode);
85    *result = pSel;
86    return rc;
87}
88
89void
90InitSelections(void)
91{
92    Selection *pSel, *pNextSel;
93
94    pSel = CurrentSelections;
95    while (pSel) {
96	pNextSel = pSel->next;
97	dixFreeObjectWithPrivates(pSel, PRIVATE_SELECTION);
98	pSel = pNextSel;
99    }
100
101    CurrentSelections = NULL;
102}
103
104static _X_INLINE void
105CallSelectionCallback(Selection *pSel, ClientPtr client,
106		      SelectionCallbackKind kind)
107{
108    SelectionInfoRec info = { pSel, client, kind };
109    CallCallbacks(&SelectionCallback, &info);
110}
111
112void
113DeleteWindowFromAnySelections(WindowPtr pWin)
114{
115    Selection *pSel;
116
117    for (pSel = CurrentSelections; pSel; pSel = pSel->next)
118        if (pSel->pWin == pWin) {
119	    CallSelectionCallback(pSel, NULL, SelectionWindowDestroy);
120
121            pSel->pWin = (WindowPtr)NULL;
122            pSel->window = None;
123	    pSel->client = NullClient;
124	}
125}
126
127void
128DeleteClientFromAnySelections(ClientPtr client)
129{
130    Selection *pSel;
131
132    for (pSel = CurrentSelections; pSel; pSel = pSel->next)
133        if (pSel->client == client) {
134	    CallSelectionCallback(pSel, NULL, SelectionClientClose);
135
136            pSel->pWin = (WindowPtr)NULL;
137            pSel->window = None;
138	    pSel->client = NullClient;
139	}
140}
141
142int
143ProcSetSelectionOwner(ClientPtr client)
144{
145    WindowPtr pWin = NULL;
146    TimeStamp time;
147    Selection *pSel;
148    int rc;
149
150    REQUEST(xSetSelectionOwnerReq);
151    REQUEST_SIZE_MATCH(xSetSelectionOwnerReq);
152
153    UpdateCurrentTime();
154    time = ClientTimeToServerTime(stuff->time);
155
156    /* If the client's time stamp is in the future relative to the server's
157	time stamp, do not set the selection, just return success. */
158    if (CompareTimeStamps(time, currentTime) == LATER)
159    	return Success;
160
161    if (stuff->window != None) {
162	rc = dixLookupWindow(&pWin, stuff->window, client, DixSetAttrAccess);
163        if (rc != Success)
164            return rc;
165    }
166    if (!ValidAtom(stuff->selection)) {
167	client->errorValue = stuff->selection;
168        return BadAtom;
169    }
170
171    /*
172     * First, see if the selection is already set...
173     */
174    rc = dixLookupSelection(&pSel, stuff->selection, client, DixSetAttrAccess);
175
176    if (rc == Success) {
177	xEvent event;
178
179	/* If the timestamp in client's request is in the past relative
180	   to the time stamp indicating the last time the owner of the
181	   selection was set, do not set the selection, just return
182	   success. */
183	if (CompareTimeStamps(time, pSel->lastTimeChanged) == EARLIER)
184	    return Success;
185	if (pSel->client && (!pWin || (pSel->client != client)))
186	{
187	    event.u.u.type = SelectionClear;
188	    event.u.selectionClear.time = time.milliseconds;
189	    event.u.selectionClear.window = pSel->window;
190	    event.u.selectionClear.atom = pSel->selection;
191	    WriteEventsToClient(pSel->client, 1, &event);
192	}
193    }
194    else if (rc == BadMatch)
195    {
196	/*
197	 * It doesn't exist, so add it...
198	 */
199	pSel = dixAllocateObjectWithPrivates(Selection, PRIVATE_SELECTION);
200	if (!pSel)
201	    return BadAlloc;
202
203	pSel->selection = stuff->selection;
204
205	/* security creation/labeling check */
206	rc = XaceHookSelectionAccess(client, &pSel,
207				     DixCreateAccess|DixSetAttrAccess);
208	if (rc != Success) {
209	    free(pSel);
210	    return rc;
211	}
212
213	pSel->next = CurrentSelections;
214	CurrentSelections = pSel;
215    }
216    else
217	return rc;
218
219    pSel->lastTimeChanged = time;
220    pSel->window = stuff->window;
221    pSel->pWin = pWin;
222    pSel->client = (pWin ? client : NullClient);
223
224    CallSelectionCallback(pSel, client, SelectionSetOwner);
225    return Success;
226}
227
228int
229ProcGetSelectionOwner(ClientPtr client)
230{
231    int rc;
232    Selection *pSel;
233    xGetSelectionOwnerReply reply;
234
235    REQUEST(xResourceReq);
236    REQUEST_SIZE_MATCH(xResourceReq);
237
238    if (!ValidAtom(stuff->id)) {
239	client->errorValue = stuff->id;
240        return BadAtom;
241    }
242
243    memset(&reply, 0, sizeof(xGetSelectionOwnerReply));
244    reply.type = X_Reply;
245    reply.length = 0;
246    reply.sequenceNumber = client->sequence;
247
248    rc = dixLookupSelection(&pSel, stuff->id, client, DixGetAttrAccess);
249    if (rc == Success)
250	reply.owner = pSel->window;
251    else if (rc == BadMatch)
252	reply.owner = None;
253    else
254	return rc;
255
256    WriteReplyToClient(client, sizeof(xGetSelectionOwnerReply), &reply);
257    return Success;
258}
259
260int
261ProcConvertSelection(ClientPtr client)
262{
263    Bool paramsOkay;
264    xEvent event;
265    WindowPtr pWin;
266    Selection *pSel;
267    int rc;
268
269    REQUEST(xConvertSelectionReq);
270    REQUEST_SIZE_MATCH(xConvertSelectionReq);
271
272    rc = dixLookupWindow(&pWin, stuff->requestor, client, DixSetAttrAccess);
273    if (rc != Success)
274        return rc;
275
276    paramsOkay = ValidAtom(stuff->selection) && ValidAtom(stuff->target);
277    paramsOkay &= (stuff->property == None) || ValidAtom(stuff->property);
278    if (!paramsOkay) {
279	client->errorValue = stuff->property;
280        return BadAtom;
281    }
282
283    rc = dixLookupSelection(&pSel, stuff->selection, client, DixReadAccess);
284
285    memset(&event, 0, sizeof(xEvent));
286    if (rc != Success && rc != BadMatch)
287	return rc;
288    else if (rc == Success && pSel->window != None) {
289	event.u.u.type = SelectionRequest;
290	event.u.selectionRequest.owner = pSel->window;
291	event.u.selectionRequest.time = stuff->time;
292	event.u.selectionRequest.requestor = stuff->requestor;
293	event.u.selectionRequest.selection = stuff->selection;
294	event.u.selectionRequest.target = stuff->target;
295	event.u.selectionRequest.property = stuff->property;
296	if (pSel->client && pSel->client != serverClient && !pSel->client->clientGone)
297	{
298	    WriteEventsToClient(pSel->client, 1, &event);
299	    return Success;
300	}
301    }
302
303    event.u.u.type = SelectionNotify;
304    event.u.selectionNotify.time = stuff->time;
305    event.u.selectionNotify.requestor = stuff->requestor;
306    event.u.selectionNotify.selection = stuff->selection;
307    event.u.selectionNotify.target = stuff->target;
308    event.u.selectionNotify.property = None;
309    WriteEventsToClient(client, 1, &event);
310    return Success;
311}
312