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