1/***********************************************************
2
3Copyright 1987, 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 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 <X11/X.h>
53#include <X11/Xproto.h>
54#include "windowstr.h"
55#include "propertyst.h"
56#include "dixstruct.h"
57#include "dispatch.h"
58#include "swaprep.h"
59#include "xace.h"
60
61/*****************************************************************
62 * Property Stuff
63 *
64 *    dixLookupProperty, dixChangeProperty, DeleteProperty
65 *
66 *   Properties belong to windows.  The list of properties should not be
67 *   traversed directly.  Instead, use the three functions listed above.
68 *
69 *****************************************************************/
70
71#ifdef notdef
72static void
73PrintPropertys(WindowPtr pWin)
74{
75    PropertyPtr pProp;
76    int j;
77
78    pProp = pWin->userProps;
79    while (pProp)
80    {
81        ErrorF("[dix] %x %x\n", pProp->propertyName, pProp->type);
82        ErrorF("[dix] property format: %d\n", pProp->format);
83        ErrorF("[dix] property data: \n");
84        for (j=0; j<(pProp->format/8)*pProp->size; j++)
85           ErrorF("[dix] %c\n", pProp->data[j]);
86        pProp = pProp->next;
87    }
88}
89#endif
90
91int
92dixLookupProperty(PropertyPtr *result, WindowPtr pWin, Atom propertyName,
93		  ClientPtr client, Mask access_mode)
94{
95    PropertyPtr pProp;
96    int rc = BadMatch;
97    client->errorValue = propertyName;
98
99    for (pProp = wUserProps(pWin); pProp; pProp = pProp->next)
100	if (pProp->propertyName == propertyName)
101	    break;
102
103    if (pProp)
104	rc = XaceHookPropertyAccess(client, pWin, &pProp, access_mode);
105    *result = pProp;
106    return rc;
107}
108
109static void
110deliverPropertyNotifyEvent(WindowPtr pWin, int state, Atom atom)
111{
112    xEvent event;
113
114    memset(&event, 0, sizeof(xEvent));
115    event.u.u.type = PropertyNotify;
116    event.u.property.window = pWin->drawable.id;
117    event.u.property.state = state;
118    event.u.property.atom = atom;
119    event.u.property.time = currentTime.milliseconds;
120    DeliverEvents(pWin, &event, 1, (WindowPtr)NULL);
121}
122
123int
124ProcRotateProperties(ClientPtr client)
125{
126    int     i, j, delta, rc;
127    REQUEST(xRotatePropertiesReq);
128    WindowPtr pWin;
129    Atom * atoms;
130    PropertyPtr * props;               /* array of pointer */
131    PropertyPtr pProp, saved;
132
133    REQUEST_FIXED_SIZE(xRotatePropertiesReq, stuff->nAtoms << 2);
134    UpdateCurrentTime();
135    rc = dixLookupWindow(&pWin, stuff->window, client, DixSetPropAccess);
136    if (rc != Success || stuff->nAtoms <= 0)
137        return rc;
138
139    atoms = (Atom *) & stuff[1];
140    props = malloc(stuff->nAtoms * sizeof(PropertyPtr));
141    saved = malloc(stuff->nAtoms * sizeof(PropertyRec));
142    if (!props || !saved) {
143	rc = BadAlloc;
144	goto out;
145    }
146
147    for (i = 0; i < stuff->nAtoms; i++)
148    {
149        if (!ValidAtom(atoms[i])) {
150	    rc = BadAtom;
151	    client->errorValue = atoms[i];
152	    goto out;
153        }
154        for (j = i + 1; j < stuff->nAtoms; j++)
155            if (atoms[j] == atoms[i])
156            {
157		rc = BadMatch;
158		goto out;
159            }
160
161	rc = dixLookupProperty(&pProp, pWin, atoms[i], client,
162			       DixReadAccess|DixWriteAccess);
163	if (rc != Success)
164	    goto out;
165
166        props[i] = pProp;
167	saved[i] = *pProp;
168    }
169    delta = stuff->nPositions;
170
171    /* If the rotation is a complete 360 degrees, then moving the properties
172	around and generating PropertyNotify events should be skipped. */
173
174    if (abs(delta) % stuff->nAtoms)
175    {
176	while (delta < 0)                  /* faster if abs value is small */
177            delta += stuff->nAtoms;
178    	for (i = 0; i < stuff->nAtoms; i++)
179 	{
180	    j = (i + delta) % stuff->nAtoms;
181	    deliverPropertyNotifyEvent(pWin, PropertyNewValue, atoms[i]);
182
183	    /* Preserve name and devPrivates */
184	    props[j]->type = saved[i].type;
185	    props[j]->format = saved[i].format;
186	    props[j]->size = saved[i].size;
187	    props[j]->data = saved[i].data;
188	}
189    }
190out:
191    free(saved);
192    free(props);
193    return rc;
194}
195
196int
197ProcChangeProperty(ClientPtr client)
198{
199    WindowPtr pWin;
200    char format, mode;
201    unsigned long len;
202    int sizeInBytes, err;
203    uint64_t totalSize;
204    REQUEST(xChangePropertyReq);
205
206    REQUEST_AT_LEAST_SIZE(xChangePropertyReq);
207    UpdateCurrentTime();
208    format = stuff->format;
209    mode = stuff->mode;
210    if ((mode != PropModeReplace) && (mode != PropModeAppend) &&
211	(mode != PropModePrepend))
212    {
213	client->errorValue = mode;
214	return BadValue;
215    }
216    if ((format != 8) && (format != 16) && (format != 32))
217    {
218	client->errorValue = format;
219        return BadValue;
220    }
221    len = stuff->nUnits;
222    if (len > bytes_to_int32(0xffffffff - sizeof(xChangePropertyReq)))
223	return BadLength;
224    sizeInBytes = format>>3;
225    totalSize = len * sizeInBytes;
226    REQUEST_FIXED_SIZE(xChangePropertyReq, totalSize);
227
228    err = dixLookupWindow(&pWin, stuff->window, client, DixSetPropAccess);
229    if (err != Success)
230	return err;
231    if (!ValidAtom(stuff->property))
232    {
233	client->errorValue = stuff->property;
234	return BadAtom;
235    }
236    if (!ValidAtom(stuff->type))
237    {
238	client->errorValue = stuff->type;
239	return BadAtom;
240    }
241
242    err = dixChangeWindowProperty(client, pWin, stuff->property, stuff->type,
243				  (int)format, (int)mode, len, &stuff[1],
244				  TRUE);
245    if (err != Success)
246	return err;
247    else
248	return Success;
249}
250
251int
252dixChangeWindowProperty(ClientPtr pClient, WindowPtr pWin, Atom property,
253			Atom type, int format, int mode, unsigned long len,
254			pointer value, Bool sendevent)
255{
256    PropertyPtr pProp;
257    PropertyRec savedProp;
258    int sizeInBytes, totalSize, rc;
259    unsigned char *data;
260    Mask access_mode;
261
262    sizeInBytes = format>>3;
263    totalSize = len * sizeInBytes;
264    access_mode = (mode == PropModeReplace) ? DixWriteAccess : DixBlendAccess;
265
266    /* first see if property already exists */
267    rc = dixLookupProperty(&pProp, pWin, property, pClient, access_mode);
268
269    if (rc == BadMatch)   /* just add to list */
270    {
271	if (!pWin->optional && !MakeWindowOptional (pWin))
272	    return BadAlloc;
273	pProp = dixAllocateObjectWithPrivates(PropertyRec, PRIVATE_PROPERTY);
274	if (!pProp)
275	    return BadAlloc;
276        data = malloc(totalSize);
277	if (!data && len)
278	{
279	    dixFreeObjectWithPrivates(pProp, PRIVATE_PROPERTY);
280	    return BadAlloc;
281	}
282        memcpy(data, value, totalSize);
283        pProp->propertyName = property;
284        pProp->type = type;
285        pProp->format = format;
286        pProp->data = data;
287	pProp->size = len;
288	rc = XaceHookPropertyAccess(pClient, pWin, &pProp,
289				    DixCreateAccess|DixWriteAccess);
290	if (rc != Success) {
291	    free(data);
292	    dixFreeObjectWithPrivates(pProp, PRIVATE_PROPERTY);
293	    pClient->errorValue = property;
294	    return rc;
295	}
296        pProp->next = pWin->optional->userProps;
297        pWin->optional->userProps = pProp;
298    }
299    else if (rc == Success)
300    {
301	/* To append or prepend to a property the request format and type
302		must match those of the already defined property.  The
303		existing format and type are irrelevant when using the mode
304		"PropModeReplace" since they will be written over. */
305
306        if ((format != pProp->format) && (mode != PropModeReplace))
307	    return BadMatch;
308        if ((pProp->type != type) && (mode != PropModeReplace))
309            return BadMatch;
310
311	/* save the old values for later */
312	savedProp = *pProp;
313
314        if (mode == PropModeReplace)
315        {
316	    data = malloc(totalSize);
317	    if (!data && len)
318		return BadAlloc;
319	    memcpy(data, value, totalSize);
320	    pProp->data = data;
321	    pProp->size = len;
322    	    pProp->type = type;
323	    pProp->format = format;
324	}
325	else if (len == 0)
326	{
327	    /* do nothing */
328	}
329        else if (mode == PropModeAppend)
330        {
331	    data = malloc((pProp->size + len) * sizeInBytes);
332	    if (!data)
333		return BadAlloc;
334	    memcpy(data, pProp->data, pProp->size * sizeInBytes);
335	    memcpy(data + pProp->size * sizeInBytes, value, totalSize);
336            pProp->data = data;
337            pProp->size += len;
338	}
339        else if (mode == PropModePrepend)
340        {
341            data = malloc(sizeInBytes * (len + pProp->size));
342	    if (!data)
343		return BadAlloc;
344            memcpy(data + totalSize, pProp->data, pProp->size * sizeInBytes);
345            memcpy(data, value, totalSize);
346            pProp->data = data;
347            pProp->size += len;
348	}
349
350	/* Allow security modules to check the new content */
351	access_mode |= DixPostAccess;
352	rc = XaceHookPropertyAccess(pClient, pWin, &pProp, access_mode);
353	if (rc == Success)
354	{
355	    if (savedProp.data != pProp->data)
356		free(savedProp.data);
357	}
358	else
359	{
360	    if (savedProp.data != pProp->data)
361		free(pProp->data);
362	    *pProp = savedProp;
363	    return rc;
364	}
365    }
366    else
367	return rc;
368
369    if (sendevent)
370	deliverPropertyNotifyEvent(pWin, PropertyNewValue, pProp->propertyName);
371
372    return Success;
373}
374
375int
376ChangeWindowProperty(WindowPtr pWin, Atom property, Atom type, int format,
377		     int mode, unsigned long len, pointer value,
378		     Bool sendevent)
379{
380    return dixChangeWindowProperty(serverClient, pWin, property, type, format,
381				   mode, len, value, sendevent);
382}
383
384int
385DeleteProperty(ClientPtr client, WindowPtr pWin, Atom propName)
386{
387    PropertyPtr pProp, prevProp;
388    int rc;
389
390    rc = dixLookupProperty(&pProp, pWin, propName, client, DixDestroyAccess);
391    if (rc == BadMatch)
392	return Success; /* Succeed if property does not exist */
393
394    if (rc == Success) {
395	if (pWin->optional->userProps == pProp) {
396	    /* Takes care of head */
397            if (!(pWin->optional->userProps = pProp->next))
398		CheckWindowOptionalNeed (pWin);
399	} else {
400	    /* Need to traverse to find the previous element */
401	    prevProp = pWin->optional->userProps;
402	    while (prevProp->next != pProp)
403		prevProp = prevProp->next;
404	    prevProp->next = pProp->next;
405	}
406
407	deliverPropertyNotifyEvent(pWin, PropertyDelete, pProp->propertyName);
408	free(pProp->data);
409	dixFreeObjectWithPrivates(pProp, PRIVATE_PROPERTY);
410    }
411    return rc;
412}
413
414void
415DeleteAllWindowProperties(WindowPtr pWin)
416{
417    PropertyPtr pProp, pNextProp;
418
419    pProp = wUserProps (pWin);
420    while (pProp)
421    {
422	deliverPropertyNotifyEvent(pWin, PropertyDelete, pProp->propertyName);
423	pNextProp = pProp->next;
424        free(pProp->data);
425	dixFreeObjectWithPrivates(pProp, PRIVATE_PROPERTY);
426	pProp = pNextProp;
427    }
428
429    if (pWin->optional)
430        pWin->optional->userProps = NULL;
431}
432
433static int
434NullPropertyReply(
435    ClientPtr client,
436    ATOM propertyType,
437    int format,
438    xGetPropertyReply *reply)
439{
440    reply->nItems = 0;
441    reply->length = 0;
442    reply->bytesAfter = 0;
443    reply->propertyType = propertyType;
444    reply->format = format;
445    WriteReplyToClient(client, sizeof(xGenericReply), reply);
446    return Success;
447}
448
449/*****************
450 * GetProperty
451 *    If type Any is specified, returns the property from the specified
452 *    window regardless of its type.  If a type is specified, returns the
453 *    property only if its type equals the specified type.
454 *    If delete is True and a property is returned, the property is also
455 *    deleted from the window and a PropertyNotify event is generated on the
456 *    window.
457 *****************/
458
459int
460ProcGetProperty(ClientPtr client)
461{
462    PropertyPtr pProp, prevProp;
463    unsigned long n, len, ind;
464    int rc;
465    WindowPtr pWin;
466    xGetPropertyReply reply;
467    Mask win_mode = DixGetPropAccess, prop_mode = DixReadAccess;
468    REQUEST(xGetPropertyReq);
469
470    REQUEST_SIZE_MATCH(xGetPropertyReq);
471    if (stuff->delete) {
472	UpdateCurrentTime();
473	win_mode |= DixSetPropAccess;
474	prop_mode |= DixDestroyAccess;
475    }
476    rc = dixLookupWindow(&pWin, stuff->window, client, win_mode);
477    if (rc != Success)
478	return (rc == BadMatch) ? BadWindow : rc;
479
480    if (!ValidAtom(stuff->property))
481    {
482	client->errorValue = stuff->property;
483	return BadAtom;
484    }
485    if ((stuff->delete != xTrue) && (stuff->delete != xFalse))
486    {
487	client->errorValue = stuff->delete;
488	return BadValue;
489    }
490    if ((stuff->type != AnyPropertyType) && !ValidAtom(stuff->type))
491    {
492	client->errorValue = stuff->type;
493	return BadAtom;
494    }
495
496    memset(&reply, 0, sizeof(xGetPropertyReply));
497    reply.type = X_Reply;
498    reply.sequenceNumber = client->sequence;
499
500    rc = dixLookupProperty(&pProp, pWin, stuff->property, client, prop_mode);
501    if (rc == BadMatch)
502	return NullPropertyReply(client, None, 0, &reply);
503    else if (rc != Success)
504	return rc;
505
506    /* If the request type and actual type don't match. Return the
507    property information, but not the data. */
508
509    if (((stuff->type != pProp->type) &&
510	 (stuff->type != AnyPropertyType))
511       )
512    {
513	reply.bytesAfter = pProp->size;
514	reply.format = pProp->format;
515	reply.length = 0;
516	reply.nItems = 0;
517	reply.propertyType = pProp->type;
518	WriteReplyToClient(client, sizeof(xGenericReply), &reply);
519	return Success;
520    }
521
522/*
523 *  Return type, format, value to client
524 */
525    n = (pProp->format/8) * pProp->size; /* size (bytes) of prop */
526    ind = stuff->longOffset << 2;
527
528   /* If longOffset is invalid such that it causes "len" to
529	    be negative, it's a value error. */
530
531    if (n < ind)
532    {
533	client->errorValue = stuff->longOffset;
534	return BadValue;
535    }
536
537    len = min(n - ind, 4 * stuff->longLength);
538
539    reply.bytesAfter = n - (ind + len);
540    reply.format = pProp->format;
541    reply.length = bytes_to_int32(len);
542    reply.nItems = len / (pProp->format / 8 );
543    reply.propertyType = pProp->type;
544
545    if (stuff->delete && (reply.bytesAfter == 0))
546	deliverPropertyNotifyEvent(pWin, PropertyDelete, pProp->propertyName);
547
548    WriteReplyToClient(client, sizeof(xGenericReply), &reply);
549    if (len)
550    {
551	switch (reply.format) {
552	case 32: client->pSwapReplyFunc = (ReplySwapPtr)CopySwap32Write; break;
553	case 16: client->pSwapReplyFunc = (ReplySwapPtr)CopySwap16Write; break;
554	default: client->pSwapReplyFunc = (ReplySwapPtr)WriteToClient; break;
555	}
556	WriteSwappedDataToClient(client, len,
557				 (char *)pProp->data + ind);
558    }
559
560    if (stuff->delete && (reply.bytesAfter == 0)) {
561	/* Delete the Property */
562	if (pWin->optional->userProps == pProp) {
563	    /* Takes care of head */
564            if (!(pWin->optional->userProps = pProp->next))
565		CheckWindowOptionalNeed (pWin);
566	} else {
567	    /* Need to traverse to find the previous element */
568	    prevProp = pWin->optional->userProps;
569	    while (prevProp->next != pProp)
570		prevProp = prevProp->next;
571	    prevProp->next = pProp->next;
572	}
573
574	free(pProp->data);
575	dixFreeObjectWithPrivates(pProp, PRIVATE_PROPERTY);
576    }
577    return Success;
578}
579
580int
581ProcListProperties(ClientPtr client)
582{
583    Atom *pAtoms = NULL, *temppAtoms;
584    xListPropertiesReply xlpr;
585    int	rc, numProps = 0;
586    WindowPtr pWin;
587    PropertyPtr pProp, realProp;
588    REQUEST(xResourceReq);
589
590    REQUEST_SIZE_MATCH(xResourceReq);
591    rc = dixLookupWindow(&pWin, stuff->id, client, DixListPropAccess);
592    if (rc != Success)
593        return rc;
594
595    for (pProp = wUserProps(pWin); pProp; pProp = pProp->next)
596	numProps++;
597
598    if (numProps && !(pAtoms = malloc(numProps * sizeof(Atom))))
599	return BadAlloc;
600
601    numProps = 0;
602    temppAtoms = pAtoms;
603    for (pProp = wUserProps(pWin); pProp; pProp = pProp->next) {
604	realProp = pProp;
605	rc = XaceHookPropertyAccess(client, pWin, &realProp, DixGetAttrAccess);
606	if (rc == Success && realProp == pProp) {
607	    *temppAtoms++ = pProp->propertyName;
608	    numProps++;
609	}
610    }
611
612    xlpr.type = X_Reply;
613    xlpr.nProperties = numProps;
614    xlpr.length = bytes_to_int32(numProps * sizeof(Atom));
615    xlpr.sequenceNumber = client->sequence;
616    WriteReplyToClient(client, sizeof(xGenericReply), &xlpr);
617    if (numProps)
618    {
619        client->pSwapReplyFunc = (ReplySwapPtr)Swap32Write;
620        WriteSwappedDataToClient(client, numProps * sizeof(Atom), pAtoms);
621    }
622    free(pAtoms);
623    return Success;
624}
625
626int
627ProcDeleteProperty(ClientPtr client)
628{
629    WindowPtr pWin;
630    REQUEST(xDeletePropertyReq);
631    int result;
632
633    REQUEST_SIZE_MATCH(xDeletePropertyReq);
634    UpdateCurrentTime();
635    result = dixLookupWindow(&pWin, stuff->window, client, DixSetPropAccess);
636    if (result != Success)
637        return result;
638    if (!ValidAtom(stuff->property))
639    {
640	client->errorValue = stuff->property;
641	return BadAtom;
642    }
643
644    return DeleteProperty(client, pWin, stuff->property);
645}
646