property.c revision c82838c1
11.62Smrg/***********************************************************
21.48Scgd
31.21SmycroftCopyright 1987, 1998  The Open Group
41.1Scgd
51.1ScgdPermission to use, copy, modify, distribute, and sell this software and its
61.56Sjtcdocumentation for any purpose is hereby granted without fee, provided that
71.58Scgdthe above copyright notice appear in all copies and that both that
81.58Scgdcopyright notice and this permission notice appear in supporting
91.56Sjtcdocumentation.
101.60Sjtc
111.60SjtcThe above copyright notice and this permission notice shall be included in
121.60Sjtcall copies or substantial portions of the Software.
131.60Sjtc
141.58ScgdTHE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
151.58ScgdIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
161.58ScgdFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
171.58ScgdOPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
181.1ScgdAN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
191.26ScgdCONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
201.1Scgd
211.1ScgdExcept as contained in this notice, the name of The Open Group shall not be
221.1Scgdused in advertising or otherwise to promote the sale, use or other dealings
231.62Smrgin this Software without prior written authorization from The Open Group.
241.1Scgd
251.57SjtcCopyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts.
261.1Scgd
271.1Scgd                        All Rights Reserved
281.1Scgd
291.34ScgdPermission to use, copy, modify, and distribute this software and its
301.1Scgddocumentation for any purpose and without fee is hereby granted,
311.1Scgdprovided that the above copyright notice appear in all copies and that
321.1Scgdboth that copyright notice and this permission notice appear in
331.51Sjtcsupporting documentation, and that the name of Digital not be
341.50Sjtcused in advertising or publicity pertaining to distribution of the
351.1Scgdsoftware without specific, written prior permission.
361.1Scgd
371.34ScgdDIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
381.1ScgdALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
391.1ScgdDIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
401.34ScgdANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
411.34ScgdWHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
421.45ScgdARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
431.45ScgdSOFTWARE.
441.45Scgd
451.57Sjtc******************************************************************/
461.14Sproven
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 (!data && len) {
280            dixFreeObjectWithPrivates(pProp, PRIVATE_PROPERTY);
281            return BadAlloc;
282        }
283        memcpy(data, value, totalSize);
284        pProp->propertyName = property;
285        pProp->type = type;
286        pProp->format = format;
287        pProp->data = data;
288        pProp->size = len;
289        rc = XaceHookPropertyAccess(pClient, pWin, &pProp,
290                                    DixCreateAccess | DixWriteAccess);
291        if (rc != Success) {
292            free(data);
293            dixFreeObjectWithPrivates(pProp, PRIVATE_PROPERTY);
294            pClient->errorValue = property;
295            return rc;
296        }
297        pProp->next = pWin->optional->userProps;
298        pWin->optional->userProps = pProp;
299    }
300    else if (rc == Success) {
301        /* To append or prepend to a property the request format and type
302           must match those of the already defined property.  The
303           existing format and type are irrelevant when using the mode
304           "PropModeReplace" since they will be written over. */
305
306        if ((format != pProp->format) && (mode != PropModeReplace))
307            return BadMatch;
308        if ((pProp->type != type) && (mode != PropModeReplace))
309            return BadMatch;
310
311        /* save the old values for later */
312        savedProp = *pProp;
313
314        if (mode == PropModeReplace) {
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            /* do nothing */
326        }
327        else if (mode == PropModeAppend) {
328            data = xallocarray(pProp->size + len, sizeInBytes);
329            if (!data)
330                return BadAlloc;
331            memcpy(data, pProp->data, pProp->size * sizeInBytes);
332            memcpy(data + pProp->size * sizeInBytes, value, totalSize);
333            pProp->data = data;
334            pProp->size += len;
335        }
336        else if (mode == PropModePrepend) {
337            data = xallocarray(len + pProp->size, sizeInBytes);
338            if (!data)
339                return BadAlloc;
340            memcpy(data + totalSize, pProp->data, pProp->size * sizeInBytes);
341            memcpy(data, value, totalSize);
342            pProp->data = data;
343            pProp->size += len;
344        }
345
346        /* Allow security modules to check the new content */
347        access_mode |= DixPostAccess;
348        rc = XaceHookPropertyAccess(pClient, pWin, &pProp, access_mode);
349        if (rc == Success) {
350            if (savedProp.data != pProp->data)
351                free(savedProp.data);
352        }
353        else {
354            if (savedProp.data != pProp->data)
355                free(pProp->data);
356            *pProp = savedProp;
357            return rc;
358        }
359    }
360    else
361        return rc;
362
363    if (sendevent)
364        deliverPropertyNotifyEvent(pWin, PropertyNewValue, pProp);
365
366    return Success;
367}
368
369int
370DeleteProperty(ClientPtr client, WindowPtr pWin, Atom propName)
371{
372    PropertyPtr pProp, prevProp;
373    int rc;
374
375    rc = dixLookupProperty(&pProp, pWin, propName, client, DixDestroyAccess);
376    if (rc == BadMatch)
377        return Success;         /* Succeed if property does not exist */
378
379    if (rc == Success) {
380        if (pWin->optional->userProps == pProp) {
381            /* Takes care of head */
382            if (!(pWin->optional->userProps = pProp->next))
383                CheckWindowOptionalNeed(pWin);
384        }
385        else {
386            /* Need to traverse to find the previous element */
387            prevProp = pWin->optional->userProps;
388            while (prevProp->next != pProp)
389                prevProp = prevProp->next;
390            prevProp->next = pProp->next;
391        }
392
393        deliverPropertyNotifyEvent(pWin, PropertyDelete, pProp);
394        free(pProp->data);
395        dixFreeObjectWithPrivates(pProp, PRIVATE_PROPERTY);
396    }
397    return rc;
398}
399
400void
401DeleteAllWindowProperties(WindowPtr pWin)
402{
403    PropertyPtr pProp, pNextProp;
404
405    pProp = wUserProps(pWin);
406    while (pProp) {
407        deliverPropertyNotifyEvent(pWin, PropertyDelete, pProp);
408        pNextProp = pProp->next;
409        free(pProp->data);
410        dixFreeObjectWithPrivates(pProp, PRIVATE_PROPERTY);
411        pProp = pNextProp;
412    }
413
414    if (pWin->optional)
415        pWin->optional->userProps = NULL;
416}
417
418static int
419NullPropertyReply(ClientPtr client, ATOM propertyType, int format)
420{
421    xGetPropertyReply reply = {
422        .type = X_Reply,
423        .format = format,
424        .sequenceNumber = client->sequence,
425        .length = 0,
426        .propertyType = propertyType,
427        .bytesAfter = 0,
428        .nItems = 0
429    };
430    WriteReplyToClient(client, sizeof(xGenericReply), &reply);
431    return Success;
432}
433
434/*****************
435 * GetProperty
436 *    If type Any is specified, returns the property from the specified
437 *    window regardless of its type.  If a type is specified, returns the
438 *    property only if its type equals the specified type.
439 *    If delete is True and a property is returned, the property is also
440 *    deleted from the window and a PropertyNotify event is generated on the
441 *    window.
442 *****************/
443
444int
445ProcGetProperty(ClientPtr client)
446{
447    PropertyPtr pProp, prevProp;
448    unsigned long n, len, ind;
449    int rc;
450    WindowPtr pWin;
451    xGetPropertyReply reply;
452    Mask win_mode = DixGetPropAccess, prop_mode = DixReadAccess;
453
454    REQUEST(xGetPropertyReq);
455
456    REQUEST_SIZE_MATCH(xGetPropertyReq);
457    if (stuff->delete) {
458        UpdateCurrentTime();
459        win_mode |= DixSetPropAccess;
460        prop_mode |= DixDestroyAccess;
461    }
462    rc = dixLookupWindow(&pWin, stuff->window, client, win_mode);
463    if (rc != Success)
464        return rc;
465
466    if (!ValidAtom(stuff->property)) {
467        client->errorValue = stuff->property;
468        return BadAtom;
469    }
470    if ((stuff->delete != xTrue) && (stuff->delete != xFalse)) {
471        client->errorValue = stuff->delete;
472        return BadValue;
473    }
474    if ((stuff->type != AnyPropertyType) && !ValidAtom(stuff->type)) {
475        client->errorValue = stuff->type;
476        return BadAtom;
477    }
478
479    rc = dixLookupProperty(&pProp, pWin, stuff->property, client, prop_mode);
480    if (rc == BadMatch)
481        return NullPropertyReply(client, None, 0);
482    else if (rc != Success)
483        return rc;
484
485    /* If the request type and actual type don't match. Return the
486       property information, but not the data. */
487
488    if (((stuff->type != pProp->type) && (stuff->type != AnyPropertyType))
489        ) {
490        reply = (xGetPropertyReply) {
491            .type = X_Reply,
492            .sequenceNumber = client->sequence,
493            .bytesAfter = pProp->size,
494            .format = pProp->format,
495            .length = 0,
496            .nItems = 0,
497            .propertyType = pProp->type
498        };
499        WriteReplyToClient(client, sizeof(xGenericReply), &reply);
500        return Success;
501    }
502
503/*
504 *  Return type, format, value to client
505 */
506    n = (pProp->format / 8) * pProp->size;      /* size (bytes) of prop */
507    ind = stuff->longOffset << 2;
508
509    /* If longOffset is invalid such that it causes "len" to
510       be negative, it's a value error. */
511
512    if (n < ind) {
513        client->errorValue = stuff->longOffset;
514        return BadValue;
515    }
516
517    len = min(n - ind, 4 * stuff->longLength);
518
519    reply = (xGetPropertyReply) {
520        .type = X_Reply,
521        .sequenceNumber = client->sequence,
522        .bytesAfter = n - (ind + len),
523        .format = pProp->format,
524        .length = bytes_to_int32(len),
525        .nItems = len / (pProp->format / 8),
526        .propertyType = pProp->type
527    };
528
529    if (stuff->delete && (reply.bytesAfter == 0))
530        deliverPropertyNotifyEvent(pWin, PropertyDelete, pProp);
531
532    WriteReplyToClient(client, sizeof(xGenericReply), &reply);
533    if (len) {
534        switch (reply.format) {
535        case 32:
536            client->pSwapReplyFunc = (ReplySwapPtr) CopySwap32Write;
537            break;
538        case 16:
539            client->pSwapReplyFunc = (ReplySwapPtr) CopySwap16Write;
540            break;
541        default:
542            client->pSwapReplyFunc = (ReplySwapPtr) WriteToClient;
543            break;
544        }
545        WriteSwappedDataToClient(client, len, (char *) pProp->data + ind);
546    }
547
548    if (stuff->delete && (reply.bytesAfter == 0)) {
549        /* Delete the Property */
550        if (pWin->optional->userProps == pProp) {
551            /* Takes care of head */
552            if (!(pWin->optional->userProps = pProp->next))
553                CheckWindowOptionalNeed(pWin);
554        }
555        else {
556            /* Need to traverse to find the previous element */
557            prevProp = pWin->optional->userProps;
558            while (prevProp->next != pProp)
559                prevProp = prevProp->next;
560            prevProp->next = pProp->next;
561        }
562
563        free(pProp->data);
564        dixFreeObjectWithPrivates(pProp, PRIVATE_PROPERTY);
565    }
566    return Success;
567}
568
569int
570ProcListProperties(ClientPtr client)
571{
572    Atom *pAtoms = NULL, *temppAtoms;
573    xListPropertiesReply xlpr;
574    int rc, numProps = 0;
575    WindowPtr pWin;
576    PropertyPtr pProp, realProp;
577
578    REQUEST(xResourceReq);
579
580    REQUEST_SIZE_MATCH(xResourceReq);
581    rc = dixLookupWindow(&pWin, stuff->id, client, DixListPropAccess);
582    if (rc != Success)
583        return rc;
584
585    for (pProp = wUserProps(pWin); pProp; pProp = pProp->next)
586        numProps++;
587
588    if (numProps && !(pAtoms = xallocarray(numProps, sizeof(Atom))))
589        return BadAlloc;
590
591    numProps = 0;
592    temppAtoms = pAtoms;
593    for (pProp = wUserProps(pWin); pProp; pProp = pProp->next) {
594        realProp = pProp;
595        rc = XaceHookPropertyAccess(client, pWin, &realProp, DixGetAttrAccess);
596        if (rc == Success && realProp == pProp) {
597            *temppAtoms++ = pProp->propertyName;
598            numProps++;
599        }
600    }
601
602    xlpr = (xListPropertiesReply) {
603        .type = X_Reply,
604        .sequenceNumber = client->sequence,
605        .length = bytes_to_int32(numProps * sizeof(Atom)),
606        .nProperties = numProps
607    };
608    WriteReplyToClient(client, sizeof(xGenericReply), &xlpr);
609    if (numProps) {
610        client->pSwapReplyFunc = (ReplySwapPtr) Swap32Write;
611        WriteSwappedDataToClient(client, numProps * sizeof(Atom), pAtoms);
612    }
613    free(pAtoms);
614    return Success;
615}
616
617int
618ProcDeleteProperty(ClientPtr client)
619{
620    WindowPtr pWin;
621
622    REQUEST(xDeletePropertyReq);
623    int result;
624
625    REQUEST_SIZE_MATCH(xDeletePropertyReq);
626    UpdateCurrentTime();
627    result = dixLookupWindow(&pWin, stuff->window, client, DixSetPropAccess);
628    if (result != Success)
629        return result;
630    if (!ValidAtom(stuff->property)) {
631        client->errorValue = stuff->property;
632        return BadAtom;
633    }
634
635    return DeleteProperty(client, pWin, stuff->property);
636}
637