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