1 /***********************************************************
2 
3 Copyright 1987, 1998  The Open Group
4 
5 Permission to use, copy, modify, distribute, and sell this software and its
6 documentation for any purpose is hereby granted without fee, provided that
7 the above copyright notice appear in all copies and that both that
8 copyright notice and this permission notice appear in supporting
9 documentation.
10 
11 The above copyright notice and this permission notice shall be included in
12 all copies or substantial portions of the Software.
13 
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
17 OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
18 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20 
21 Except as contained in this notice, the name of The Open Group shall not be
22 used in advertising or otherwise to promote the sale, use or other dealings
23 in this Software without prior written authorization from The Open Group.
24 
25 Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts.
26 
27                         All Rights Reserved
28 
29 Permission to use, copy, modify, and distribute this software and its
30 documentation for any purpose and without fee is hereby granted,
31 provided that the above copyright notice appear in all copies and that
32 both that copyright notice and this permission notice appear in
33 supporting documentation, and that the name of Digital not be
34 used in advertising or publicity pertaining to distribution of the
35 software without specific, written prior permission.
36 
37 DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
38 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
39 DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
40 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
41 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
42 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
43 SOFTWARE.
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
71 static void
PrintPropertys(WindowPtr pWin)72 PrintPropertys(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 
89 int
dixLookupProperty(PropertyPtr * result,WindowPtr pWin,Atom propertyName,ClientPtr client,Mask access_mode)90 dixLookupProperty(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 
108 CallbackListPtr PropertyStateCallback;
109 
110 static void
deliverPropertyNotifyEvent(WindowPtr pWin,int state,PropertyPtr pProp)111 deliverPropertyNotifyEvent(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 
132 int
ProcRotateProperties(ClientPtr client)133 ProcRotateProperties(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 
202 int
ProcChangeProperty(ClientPtr client)203 ProcChangeProperty(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 
253 int
dixChangeWindowProperty(ClientPtr pClient,WindowPtr pWin,Atom property,Atom type,int format,int mode,unsigned long len,void * value,Bool sendevent)254 dixChangeWindowProperty(ClientPtr pClient, WindowPtr pWin, Atom property,
255                         Atom type, int format, int mode, unsigned long len,
256                         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 
368 int
DeleteProperty(ClientPtr client,WindowPtr pWin,Atom propName)369 DeleteProperty(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 
399 void
DeleteAllWindowProperties(WindowPtr pWin)400 DeleteAllWindowProperties(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 
417 static int
NullPropertyReply(ClientPtr client,ATOM propertyType,int format)418 NullPropertyReply(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 
443 int
ProcGetProperty(ClientPtr client)444 ProcGetProperty(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 
568 int
ProcListProperties(ClientPtr client)569 ProcListProperties(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 
616 int
ProcDeleteProperty(ClientPtr client)617 ProcDeleteProperty(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