property.c revision 6747b715
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
429static int
430NullPropertyReply(
431    ClientPtr client,
432    ATOM propertyType,
433    int format,
434    xGetPropertyReply *reply)
435{
436    reply->nItems = 0;
437    reply->length = 0;
438    reply->bytesAfter = 0;
439    reply->propertyType = propertyType;
440    reply->format = format;
441    WriteReplyToClient(client, sizeof(xGenericReply), reply);
442    return Success;
443}
444
445/*****************
446 * GetProperty
447 *    If type Any is specified, returns the property from the specified
448 *    window regardless of its type.  If a type is specified, returns the
449 *    property only if its type equals the specified type.
450 *    If delete is True and a property is returned, the property is also
451 *    deleted from the window and a PropertyNotify event is generated on the
452 *    window.
453 *****************/
454
455int
456ProcGetProperty(ClientPtr client)
457{
458    PropertyPtr pProp, prevProp;
459    unsigned long n, len, ind;
460    int rc;
461    WindowPtr pWin;
462    xGetPropertyReply reply;
463    Mask win_mode = DixGetPropAccess, prop_mode = DixReadAccess;
464    REQUEST(xGetPropertyReq);
465
466    REQUEST_SIZE_MATCH(xGetPropertyReq);
467    if (stuff->delete) {
468	UpdateCurrentTime();
469	win_mode |= DixSetPropAccess;
470	prop_mode |= DixDestroyAccess;
471    }
472    rc = dixLookupWindow(&pWin, stuff->window, client, win_mode);
473    if (rc != Success)
474	return (rc == BadMatch) ? BadWindow : rc;
475
476    if (!ValidAtom(stuff->property))
477    {
478	client->errorValue = stuff->property;
479	return BadAtom;
480    }
481    if ((stuff->delete != xTrue) && (stuff->delete != xFalse))
482    {
483	client->errorValue = stuff->delete;
484	return BadValue;
485    }
486    if ((stuff->type != AnyPropertyType) && !ValidAtom(stuff->type))
487    {
488	client->errorValue = stuff->type;
489	return BadAtom;
490    }
491
492    memset(&reply, 0, sizeof(xGetPropertyReply));
493    reply.type = X_Reply;
494    reply.sequenceNumber = client->sequence;
495
496    rc = dixLookupProperty(&pProp, pWin, stuff->property, client, prop_mode);
497    if (rc == BadMatch)
498	return NullPropertyReply(client, None, 0, &reply);
499    else if (rc != Success)
500	return rc;
501
502    /* If the request type and actual type don't match. Return the
503    property information, but not the data. */
504
505    if (((stuff->type != pProp->type) &&
506	 (stuff->type != AnyPropertyType))
507       )
508    {
509	reply.bytesAfter = pProp->size;
510	reply.format = pProp->format;
511	reply.length = 0;
512	reply.nItems = 0;
513	reply.propertyType = pProp->type;
514	WriteReplyToClient(client, sizeof(xGenericReply), &reply);
515	return Success;
516    }
517
518/*
519 *  Return type, format, value to client
520 */
521    n = (pProp->format/8) * pProp->size; /* size (bytes) of prop */
522    ind = stuff->longOffset << 2;
523
524   /* If longOffset is invalid such that it causes "len" to
525	    be negative, it's a value error. */
526
527    if (n < ind)
528    {
529	client->errorValue = stuff->longOffset;
530	return BadValue;
531    }
532
533    len = min(n - ind, 4 * stuff->longLength);
534
535    reply.bytesAfter = n - (ind + len);
536    reply.format = pProp->format;
537    reply.length = bytes_to_int32(len);
538    reply.nItems = len / (pProp->format / 8 );
539    reply.propertyType = pProp->type;
540
541    if (stuff->delete && (reply.bytesAfter == 0))
542	deliverPropertyNotifyEvent(pWin, PropertyDelete, pProp->propertyName);
543
544    WriteReplyToClient(client, sizeof(xGenericReply), &reply);
545    if (len)
546    {
547	switch (reply.format) {
548	case 32: client->pSwapReplyFunc = (ReplySwapPtr)CopySwap32Write; break;
549	case 16: client->pSwapReplyFunc = (ReplySwapPtr)CopySwap16Write; break;
550	default: client->pSwapReplyFunc = (ReplySwapPtr)WriteToClient; break;
551	}
552	WriteSwappedDataToClient(client, len,
553				 (char *)pProp->data + ind);
554    }
555
556    if (stuff->delete && (reply.bytesAfter == 0)) {
557	/* Delete the Property */
558	if (pWin->optional->userProps == pProp) {
559	    /* Takes care of head */
560            if (!(pWin->optional->userProps = pProp->next))
561		CheckWindowOptionalNeed (pWin);
562	} else {
563	    /* Need to traverse to find the previous element */
564	    prevProp = pWin->optional->userProps;
565	    while (prevProp->next != pProp)
566		prevProp = prevProp->next;
567	    prevProp->next = pProp->next;
568	}
569
570	free(pProp->data);
571	dixFreeObjectWithPrivates(pProp, PRIVATE_PROPERTY);
572    }
573    return Success;
574}
575
576int
577ProcListProperties(ClientPtr client)
578{
579    Atom *pAtoms = NULL, *temppAtoms;
580    xListPropertiesReply xlpr;
581    int	rc, numProps = 0;
582    WindowPtr pWin;
583    PropertyPtr pProp, realProp;
584    REQUEST(xResourceReq);
585
586    REQUEST_SIZE_MATCH(xResourceReq);
587    rc = dixLookupWindow(&pWin, stuff->id, client, DixListPropAccess);
588    if (rc != Success)
589        return rc;
590
591    for (pProp = wUserProps(pWin); pProp; pProp = pProp->next)
592	numProps++;
593
594    if (numProps && !(pAtoms = malloc(numProps * sizeof(Atom))))
595	return BadAlloc;
596
597    numProps = 0;
598    temppAtoms = pAtoms;
599    for (pProp = wUserProps(pWin); pProp; pProp = pProp->next) {
600	realProp = pProp;
601	rc = XaceHookPropertyAccess(client, pWin, &realProp, DixGetAttrAccess);
602	if (rc == Success && realProp == pProp) {
603	    *temppAtoms++ = pProp->propertyName;
604	    numProps++;
605	}
606    }
607
608    xlpr.type = X_Reply;
609    xlpr.nProperties = numProps;
610    xlpr.length = bytes_to_int32(numProps * sizeof(Atom));
611    xlpr.sequenceNumber = client->sequence;
612    WriteReplyToClient(client, sizeof(xGenericReply), &xlpr);
613    if (numProps)
614    {
615        client->pSwapReplyFunc = (ReplySwapPtr)Swap32Write;
616        WriteSwappedDataToClient(client, numProps * sizeof(Atom), pAtoms);
617    }
618    free(pAtoms);
619    return Success;
620}
621
622int
623ProcDeleteProperty(ClientPtr client)
624{
625    WindowPtr pWin;
626    REQUEST(xDeletePropertyReq);
627    int result;
628
629    REQUEST_SIZE_MATCH(xDeletePropertyReq);
630    UpdateCurrentTime();
631    result = dixLookupWindow(&pWin, stuff->window, client, DixSetPropAccess);
632    if (result != Success)
633        return result;
634    if (!ValidAtom(stuff->property))
635    {
636	client->errorValue = stuff->property;
637	return BadAtom;
638    }
639
640    return DeleteProperty(client, pWin, stuff->property);
641}
642