property.c revision 9ace9065
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, totalSize, err;
203    REQUEST(xChangePropertyReq);
204
205    REQUEST_AT_LEAST_SIZE(xChangePropertyReq);
206    UpdateCurrentTime();
207    format = stuff->format;
208    mode = stuff->mode;
209    if ((mode != PropModeReplace) && (mode != PropModeAppend) &&
210	(mode != PropModePrepend))
211    {
212	client->errorValue = mode;
213	return BadValue;
214    }
215    if ((format != 8) && (format != 16) && (format != 32))
216    {
217	client->errorValue = format;
218        return BadValue;
219    }
220    len = stuff->nUnits;
221    if (len > bytes_to_int32(0xffffffff - sizeof(xChangePropertyReq)))
222	return BadLength;
223    sizeInBytes = format>>3;
224    totalSize = len * sizeInBytes;
225    REQUEST_FIXED_SIZE(xChangePropertyReq, totalSize);
226
227    err = dixLookupWindow(&pWin, stuff->window, client, DixSetPropAccess);
228    if (err != Success)
229	return err;
230    if (!ValidAtom(stuff->property))
231    {
232	client->errorValue = stuff->property;
233	return BadAtom;
234    }
235    if (!ValidAtom(stuff->type))
236    {
237	client->errorValue = stuff->type;
238	return BadAtom;
239    }
240
241    err = dixChangeWindowProperty(client, pWin, stuff->property, stuff->type,
242				  (int)format, (int)mode, len, &stuff[1],
243				  TRUE);
244    if (err != Success)
245	return err;
246    else
247	return Success;
248}
249
250int
251dixChangeWindowProperty(ClientPtr pClient, WindowPtr pWin, Atom property,
252			Atom type, int format, int mode, unsigned long len,
253			pointer value, Bool sendevent)
254{
255    PropertyPtr pProp;
256    PropertyRec savedProp;
257    int sizeInBytes, totalSize, rc;
258    unsigned char *data;
259    Mask access_mode;
260
261    sizeInBytes = format>>3;
262    totalSize = len * sizeInBytes;
263    access_mode = (mode == PropModeReplace) ? DixWriteAccess : DixBlendAccess;
264
265    /* first see if property already exists */
266    rc = dixLookupProperty(&pProp, pWin, property, pClient, access_mode);
267
268    if (rc == BadMatch)   /* just add to list */
269    {
270	if (!pWin->optional && !MakeWindowOptional (pWin))
271	    return BadAlloc;
272	pProp = dixAllocateObjectWithPrivates(PropertyRec, PRIVATE_PROPERTY);
273	if (!pProp)
274	    return BadAlloc;
275        data = malloc(totalSize);
276	if (!data && len)
277	{
278	    dixFreeObjectWithPrivates(pProp, PRIVATE_PROPERTY);
279	    return BadAlloc;
280	}
281        memcpy(data, value, totalSize);
282        pProp->propertyName = property;
283        pProp->type = type;
284        pProp->format = format;
285        pProp->data = data;
286	pProp->size = len;
287	rc = XaceHookPropertyAccess(pClient, pWin, &pProp,
288				    DixCreateAccess|DixWriteAccess);
289	if (rc != Success) {
290	    free(data);
291	    dixFreeObjectWithPrivates(pProp, PRIVATE_PROPERTY);
292	    pClient->errorValue = property;
293	    return rc;
294	}
295        pProp->next = pWin->optional->userProps;
296        pWin->optional->userProps = pProp;
297    }
298    else if (rc == Success)
299    {
300	/* To append or prepend to a property the request format and type
301		must match those of the already defined property.  The
302		existing format and type are irrelevant when using the mode
303		"PropModeReplace" since they will be written over. */
304
305        if ((format != pProp->format) && (mode != PropModeReplace))
306	    return BadMatch;
307        if ((pProp->type != type) && (mode != PropModeReplace))
308            return BadMatch;
309
310	/* save the old values for later */
311	savedProp = *pProp;
312
313        if (mode == PropModeReplace)
314        {
315	    data = malloc(totalSize);
316	    if (!data && len)
317		return BadAlloc;
318	    memcpy(data, value, totalSize);
319	    pProp->data = data;
320	    pProp->size = len;
321    	    pProp->type = type;
322	    pProp->format = format;
323	}
324	else if (len == 0)
325	{
326	    /* do nothing */
327	}
328        else if (mode == PropModeAppend)
329        {
330	    data = malloc((pProp->size + len) * sizeInBytes);
331	    if (!data)
332		return BadAlloc;
333	    memcpy(data, pProp->data, pProp->size * sizeInBytes);
334	    memcpy(data + pProp->size * sizeInBytes, value, totalSize);
335            pProp->data = data;
336            pProp->size += len;
337	}
338        else if (mode == PropModePrepend)
339        {
340            data = malloc(sizeInBytes * (len + pProp->size));
341	    if (!data)
342		return BadAlloc;
343            memcpy(data + totalSize, pProp->data, pProp->size * sizeInBytes);
344            memcpy(data, value, totalSize);
345            pProp->data = data;
346            pProp->size += len;
347	}
348
349	/* Allow security modules to check the new content */
350	access_mode |= DixPostAccess;
351	rc = XaceHookPropertyAccess(pClient, pWin, &pProp, access_mode);
352	if (rc == Success)
353	{
354	    if (savedProp.data != pProp->data)
355		free(savedProp.data);
356	}
357	else
358	{
359	    if (savedProp.data != pProp->data)
360		free(pProp->data);
361	    *pProp = savedProp;
362	    return rc;
363	}
364    }
365    else
366	return rc;
367
368    if (sendevent)
369	deliverPropertyNotifyEvent(pWin, PropertyNewValue, pProp->propertyName);
370
371    return Success;
372}
373
374int
375ChangeWindowProperty(WindowPtr pWin, Atom property, Atom type, int format,
376		     int mode, unsigned long len, pointer value,
377		     Bool sendevent)
378{
379    return dixChangeWindowProperty(serverClient, pWin, property, type, format,
380				   mode, len, value, sendevent);
381}
382
383int
384DeleteProperty(ClientPtr client, WindowPtr pWin, Atom propName)
385{
386    PropertyPtr pProp, prevProp;
387    int rc;
388
389    rc = dixLookupProperty(&pProp, pWin, propName, client, DixDestroyAccess);
390    if (rc == BadMatch)
391	return Success; /* Succeed if property does not exist */
392
393    if (rc == Success) {
394	if (pWin->optional->userProps == pProp) {
395	    /* Takes care of head */
396            if (!(pWin->optional->userProps = pProp->next))
397		CheckWindowOptionalNeed (pWin);
398	} else {
399	    /* Need to traverse to find the previous element */
400	    prevProp = pWin->optional->userProps;
401	    while (prevProp->next != pProp)
402		prevProp = prevProp->next;
403	    prevProp->next = pProp->next;
404	}
405
406	deliverPropertyNotifyEvent(pWin, PropertyDelete, pProp->propertyName);
407	free(pProp->data);
408	dixFreeObjectWithPrivates(pProp, PRIVATE_PROPERTY);
409    }
410    return rc;
411}
412
413void
414DeleteAllWindowProperties(WindowPtr pWin)
415{
416    PropertyPtr pProp, pNextProp;
417
418    pProp = wUserProps (pWin);
419    while (pProp)
420    {
421	deliverPropertyNotifyEvent(pWin, PropertyDelete, pProp->propertyName);
422	pNextProp = pProp->next;
423        free(pProp->data);
424	dixFreeObjectWithPrivates(pProp, PRIVATE_PROPERTY);
425	pProp = pNextProp;
426    }
427
428    if (pWin->optional)
429        pWin->optional->userProps = NULL;
430}
431
432static int
433NullPropertyReply(
434    ClientPtr client,
435    ATOM propertyType,
436    int format,
437    xGetPropertyReply *reply)
438{
439    reply->nItems = 0;
440    reply->length = 0;
441    reply->bytesAfter = 0;
442    reply->propertyType = propertyType;
443    reply->format = format;
444    WriteReplyToClient(client, sizeof(xGenericReply), reply);
445    return Success;
446}
447
448/*****************
449 * GetProperty
450 *    If type Any is specified, returns the property from the specified
451 *    window regardless of its type.  If a type is specified, returns the
452 *    property only if its type equals the specified type.
453 *    If delete is True and a property is returned, the property is also
454 *    deleted from the window and a PropertyNotify event is generated on the
455 *    window.
456 *****************/
457
458int
459ProcGetProperty(ClientPtr client)
460{
461    PropertyPtr pProp, prevProp;
462    unsigned long n, len, ind;
463    int rc;
464    WindowPtr pWin;
465    xGetPropertyReply reply;
466    Mask win_mode = DixGetPropAccess, prop_mode = DixReadAccess;
467    REQUEST(xGetPropertyReq);
468
469    REQUEST_SIZE_MATCH(xGetPropertyReq);
470    if (stuff->delete) {
471	UpdateCurrentTime();
472	win_mode |= DixSetPropAccess;
473	prop_mode |= DixDestroyAccess;
474    }
475    rc = dixLookupWindow(&pWin, stuff->window, client, win_mode);
476    if (rc != Success)
477	return (rc == BadMatch) ? BadWindow : rc;
478
479    if (!ValidAtom(stuff->property))
480    {
481	client->errorValue = stuff->property;
482	return BadAtom;
483    }
484    if ((stuff->delete != xTrue) && (stuff->delete != xFalse))
485    {
486	client->errorValue = stuff->delete;
487	return BadValue;
488    }
489    if ((stuff->type != AnyPropertyType) && !ValidAtom(stuff->type))
490    {
491	client->errorValue = stuff->type;
492	return BadAtom;
493    }
494
495    memset(&reply, 0, sizeof(xGetPropertyReply));
496    reply.type = X_Reply;
497    reply.sequenceNumber = client->sequence;
498
499    rc = dixLookupProperty(&pProp, pWin, stuff->property, client, prop_mode);
500    if (rc == BadMatch)
501	return NullPropertyReply(client, None, 0, &reply);
502    else if (rc != Success)
503	return rc;
504
505    /* If the request type and actual type don't match. Return the
506    property information, but not the data. */
507
508    if (((stuff->type != pProp->type) &&
509	 (stuff->type != AnyPropertyType))
510       )
511    {
512	reply.bytesAfter = pProp->size;
513	reply.format = pProp->format;
514	reply.length = 0;
515	reply.nItems = 0;
516	reply.propertyType = pProp->type;
517	WriteReplyToClient(client, sizeof(xGenericReply), &reply);
518	return Success;
519    }
520
521/*
522 *  Return type, format, value to client
523 */
524    n = (pProp->format/8) * pProp->size; /* size (bytes) of prop */
525    ind = stuff->longOffset << 2;
526
527   /* If longOffset is invalid such that it causes "len" to
528	    be negative, it's a value error. */
529
530    if (n < ind)
531    {
532	client->errorValue = stuff->longOffset;
533	return BadValue;
534    }
535
536    len = min(n - ind, 4 * stuff->longLength);
537
538    reply.bytesAfter = n - (ind + len);
539    reply.format = pProp->format;
540    reply.length = bytes_to_int32(len);
541    reply.nItems = len / (pProp->format / 8 );
542    reply.propertyType = pProp->type;
543
544    if (stuff->delete && (reply.bytesAfter == 0))
545	deliverPropertyNotifyEvent(pWin, PropertyDelete, pProp->propertyName);
546
547    WriteReplyToClient(client, sizeof(xGenericReply), &reply);
548    if (len)
549    {
550	switch (reply.format) {
551	case 32: client->pSwapReplyFunc = (ReplySwapPtr)CopySwap32Write; break;
552	case 16: client->pSwapReplyFunc = (ReplySwapPtr)CopySwap16Write; break;
553	default: client->pSwapReplyFunc = (ReplySwapPtr)WriteToClient; break;
554	}
555	WriteSwappedDataToClient(client, len,
556				 (char *)pProp->data + ind);
557    }
558
559    if (stuff->delete && (reply.bytesAfter == 0)) {
560	/* Delete the Property */
561	if (pWin->optional->userProps == pProp) {
562	    /* Takes care of head */
563            if (!(pWin->optional->userProps = pProp->next))
564		CheckWindowOptionalNeed (pWin);
565	} else {
566	    /* Need to traverse to find the previous element */
567	    prevProp = pWin->optional->userProps;
568	    while (prevProp->next != pProp)
569		prevProp = prevProp->next;
570	    prevProp->next = pProp->next;
571	}
572
573	free(pProp->data);
574	dixFreeObjectWithPrivates(pProp, PRIVATE_PROPERTY);
575    }
576    return Success;
577}
578
579int
580ProcListProperties(ClientPtr client)
581{
582    Atom *pAtoms = NULL, *temppAtoms;
583    xListPropertiesReply xlpr;
584    int	rc, numProps = 0;
585    WindowPtr pWin;
586    PropertyPtr pProp, realProp;
587    REQUEST(xResourceReq);
588
589    REQUEST_SIZE_MATCH(xResourceReq);
590    rc = dixLookupWindow(&pWin, stuff->id, client, DixListPropAccess);
591    if (rc != Success)
592        return rc;
593
594    for (pProp = wUserProps(pWin); pProp; pProp = pProp->next)
595	numProps++;
596
597    if (numProps && !(pAtoms = malloc(numProps * sizeof(Atom))))
598	return BadAlloc;
599
600    numProps = 0;
601    temppAtoms = pAtoms;
602    for (pProp = wUserProps(pWin); pProp; pProp = pProp->next) {
603	realProp = pProp;
604	rc = XaceHookPropertyAccess(client, pWin, &realProp, DixGetAttrAccess);
605	if (rc == Success && realProp == pProp) {
606	    *temppAtoms++ = pProp->propertyName;
607	    numProps++;
608	}
609    }
610
611    xlpr.type = X_Reply;
612    xlpr.nProperties = numProps;
613    xlpr.length = bytes_to_int32(numProps * sizeof(Atom));
614    xlpr.sequenceNumber = client->sequence;
615    WriteReplyToClient(client, sizeof(xGenericReply), &xlpr);
616    if (numProps)
617    {
618        client->pSwapReplyFunc = (ReplySwapPtr)Swap32Write;
619        WriteSwappedDataToClient(client, numProps * sizeof(Atom), pAtoms);
620    }
621    free(pAtoms);
622    return Success;
623}
624
625int
626ProcDeleteProperty(ClientPtr client)
627{
628    WindowPtr pWin;
629    REQUEST(xDeletePropertyReq);
630    int result;
631
632    REQUEST_SIZE_MATCH(xDeletePropertyReq);
633    UpdateCurrentTime();
634    result = dixLookupWindow(&pWin, stuff->window, client, DixSetPropAccess);
635    if (result != Success)
636        return result;
637    if (!ValidAtom(stuff->property))
638    {
639	client->errorValue = stuff->property;
640	return BadAtom;
641    }
642
643    return DeleteProperty(client, pWin, stuff->property);
644}
645