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