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