winstation.c 20.6 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/*
 * Server-side window stations and desktops handling
 *
 * Copyright (C) 2002, 2005 Alexandre Julliard
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
18
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 20 21 22 23 24 25 26
 */

#include "config.h"
#include "wine/port.h"

#include <stdio.h>
#include <stdarg.h>

27 28
#include "ntstatus.h"
#define WIN32_NO_STATUS
29 30 31
#include "windef.h"
#include "winbase.h"
#include "winuser.h"
32
#include "winternl.h"
33 34 35 36 37

#include "object.h"
#include "handle.h"
#include "request.h"
#include "process.h"
38
#include "user.h"
39
#include "file.h"
40 41 42 43 44 45 46
#include "wine/unicode.h"


static struct list winstation_list = LIST_INIT(winstation_list);
static struct namespace *winstation_namespace;

static void winstation_dump( struct object *obj, int verbose );
47
static int winstation_close_handle( struct object *obj, struct process *process, obj_handle_t handle );
48
static void winstation_destroy( struct object *obj );
49
static unsigned int winstation_map_access( struct object *obj, unsigned int access );
50
static void desktop_dump( struct object *obj, int verbose );
51
static int desktop_close_handle( struct object *obj, struct process *process, obj_handle_t handle );
52
static void desktop_destroy( struct object *obj );
53
static unsigned int desktop_map_access( struct object *obj, unsigned int access );
54 55 56 57 58 59 60 61 62 63 64

static const struct object_ops winstation_ops =
{
    sizeof(struct winstation),    /* size */
    winstation_dump,              /* dump */
    no_add_queue,                 /* add_queue */
    NULL,                         /* remove_queue */
    NULL,                         /* signaled */
    NULL,                         /* satisfied */
    no_signal,                    /* signal */
    no_get_fd,                    /* get_fd */
65
    winstation_map_access,        /* map_access */
66 67
    default_get_sd,               /* get_sd */
    default_set_sd,               /* set_sd */
68
    no_lookup_name,               /* lookup_name */
69
    no_open_file,                 /* open_file */
70
    winstation_close_handle,      /* close_handle */
71 72 73 74 75 76 77 78 79 80 81 82 83 84
    winstation_destroy            /* destroy */
};


static const struct object_ops desktop_ops =
{
    sizeof(struct desktop),       /* size */
    desktop_dump,                 /* dump */
    no_add_queue,                 /* add_queue */
    NULL,                         /* remove_queue */
    NULL,                         /* signaled */
    NULL,                         /* satisfied */
    no_signal,                    /* signal */
    no_get_fd,                    /* get_fd */
85
    desktop_map_access,           /* map_access */
86 87
    default_get_sd,               /* get_sd */
    default_set_sd,               /* set_sd */
88
    no_lookup_name,               /* lookup_name */
89
    no_open_file,                 /* open_file */
90
    desktop_close_handle,         /* close_handle */
91 92 93 94 95 96
    desktop_destroy               /* destroy */
};

#define DESKTOP_ALL_ACCESS 0x01ff

/* create a winstation object */
97 98
static struct winstation *create_winstation( const struct unicode_str *name, unsigned int attr,
                                             unsigned int flags )
99 100 101
{
    struct winstation *winstation;

102
    if (!winstation_namespace && !(winstation_namespace = create_namespace( 7 )))
103 104
        return NULL;

105
    if (memchrW( name->str, '\\', name->len / sizeof(WCHAR) ))  /* no backslash allowed in name */
106 107 108 109 110
    {
        set_error( STATUS_INVALID_PARAMETER );
        return NULL;
    }

111
    if ((winstation = create_named_object( winstation_namespace, &winstation_ops, name, attr )))
112
    {
113
        if (get_error() != STATUS_OBJECT_NAME_EXISTS)
114 115 116
        {
            /* initialize it if it didn't already exist */
            winstation->flags = flags;
117
            winstation->clipboard = NULL;
118
            winstation->atom_table = NULL;
119 120 121 122 123 124 125 126 127 128 129
            list_add_tail( &winstation_list, &winstation->entry );
            list_init( &winstation->desktops );
        }
    }
    return winstation;
}

static void winstation_dump( struct object *obj, int verbose )
{
    struct winstation *winstation = (struct winstation *)obj;

130 131
    fprintf( stderr, "Winstation flags=%x clipboard=%p atoms=%p ",
             winstation->flags, winstation->clipboard, winstation->atom_table );
132 133 134 135
    dump_object_name( &winstation->obj );
    fputc( '\n', stderr );
}

136 137 138 139 140
static int winstation_close_handle( struct object *obj, struct process *process, obj_handle_t handle )
{
    return (process->winstation != handle);
}

141 142 143 144 145
static void winstation_destroy( struct object *obj )
{
    struct winstation *winstation = (struct winstation *)obj;

    list_remove( &winstation->entry );
146
    if (winstation->clipboard) release_object( winstation->clipboard );
147
    if (winstation->atom_table) release_object( winstation->atom_table );
148 149
}

150 151 152 153 154 155 156 157 158 159 160
static unsigned int winstation_map_access( struct object *obj, unsigned int access )
{
    if (access & GENERIC_READ)    access |= STANDARD_RIGHTS_READ | WINSTA_ENUMDESKTOPS | WINSTA_READATTRIBUTES |
                                            WINSTA_ENUMERATE | WINSTA_READSCREEN;
    if (access & GENERIC_WRITE)   access |= STANDARD_RIGHTS_WRITE | WINSTA_ACCESSCLIPBOARD | WINSTA_CREATEDESKTOP |
                                            WINSTA_WRITEATTRIBUTES;
    if (access & GENERIC_EXECUTE) access |= STANDARD_RIGHTS_EXECUTE | WINSTA_ACCESSGLOBALATOMS | WINSTA_EXITWINDOWS;
    if (access & GENERIC_ALL)     access |= STANDARD_RIGHTS_REQUIRED | WINSTA_ALL_ACCESS;
    return access & ~(GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE | GENERIC_ALL);
}

161
/* retrieve the process window station, checking the handle access rights */
162
struct winstation *get_process_winstation( struct process *process, unsigned int access )
163 164 165 166 167 168
{
    return (struct winstation *)get_handle_obj( process, process->winstation,
                                                access, &winstation_ops );
}

/* build the full name of a desktop object */
169 170
static WCHAR *build_desktop_name( const struct unicode_str *name,
                                  struct winstation *winstation, struct unicode_str *res )
171 172 173
{
    const WCHAR *winstation_name;
    WCHAR *full_name;
174
    data_size_t winstation_len;
175

176
    if (memchrW( name->str, '\\', name->len / sizeof(WCHAR) ))
177 178 179 180 181 182 183 184
    {
        set_error( STATUS_INVALID_PARAMETER );
        return NULL;
    }

    if (!(winstation_name = get_object_name( &winstation->obj, &winstation_len )))
        winstation_len = 0;

185 186
    res->len = winstation_len + name->len + sizeof(WCHAR);
    if (!(full_name = mem_alloc( res->len ))) return NULL;
187 188
    memcpy( full_name, winstation_name, winstation_len );
    full_name[winstation_len / sizeof(WCHAR)] = '\\';
189 190
    memcpy( full_name + winstation_len / sizeof(WCHAR) + 1, name->str, name->len );
    res->str = full_name;
191 192 193
    return full_name;
}

194
/* retrieve a pointer to a desktop object */
195
static inline struct desktop *get_desktop_obj( struct process *process, obj_handle_t handle,
196 197 198 199 200
                                               unsigned int access )
{
    return (struct desktop *)get_handle_obj( process, handle, access, &desktop_ops );
}

201
/* create a desktop object */
202 203
static struct desktop *create_desktop( const struct unicode_str *name, unsigned int attr,
                                       unsigned int flags, struct winstation *winstation )
204 205
{
    struct desktop *desktop;
206
    struct unicode_str full_str;
207 208
    WCHAR *full_name;

209
    if (!(full_name = build_desktop_name( name, winstation, &full_str ))) return NULL;
210

211
    if ((desktop = create_named_object( winstation_namespace, &desktop_ops, &full_str, attr )))
212
    {
213
        if (get_error() != STATUS_OBJECT_NAME_EXISTS)
214 215 216 217
        {
            /* initialize it if it didn't already exist */
            desktop->flags = flags;
            desktop->winstation = (struct winstation *)grab_object( winstation );
218
            desktop->top_window = NULL;
219
            desktop->global_hooks = NULL;
220 221
            desktop->close_timeout = NULL;
            desktop->users = 0;
222 223 224 225 226 227 228 229 230 231 232
            list_add_tail( &winstation->desktops, &desktop->entry );
        }
    }
    free( full_name );
    return desktop;
}

static void desktop_dump( struct object *obj, int verbose )
{
    struct desktop *desktop = (struct desktop *)obj;

233 234
    fprintf( stderr, "Desktop flags=%x winstation=%p top_win=%p hooks=%p ",
             desktop->flags, desktop->winstation, desktop->top_window, desktop->global_hooks );
235 236 237 238
    dump_object_name( &desktop->obj );
    fputc( '\n', stderr );
}

239
static int desktop_close_handle( struct object *obj, struct process *process, obj_handle_t handle )
240
{
241
    struct thread *thread;
242

243 244 245 246 247
    /* check if the handle is currently used by the process or one of its threads */
    if (process->desktop == handle) return 0;
    LIST_FOR_EACH_ENTRY( thread, &process->thread_list, struct thread, proc_entry )
        if (thread->desktop == handle) return 0;
    return 1;
248 249
}

250
static void desktop_destroy( struct object *obj )
251
{
252
    struct desktop *desktop = (struct desktop *)obj;
253

254
    if (desktop->top_window) destroy_window( desktop->top_window );
255
    if (desktop->global_hooks) release_object( desktop->global_hooks );
256
    if (desktop->close_timeout) remove_timeout_user( desktop->close_timeout );
257 258
    list_remove( &desktop->entry );
    release_object( desktop->winstation );
259 260
}

261 262 263 264 265 266 267 268 269 270 271
static unsigned int desktop_map_access( struct object *obj, unsigned int access )
{
    if (access & GENERIC_READ)    access |= STANDARD_RIGHTS_READ | DESKTOP_READOBJECTS | DESKTOP_ENUMERATE;
    if (access & GENERIC_WRITE)   access |= STANDARD_RIGHTS_WRITE | DESKTOP_CREATEMENU | DESKTOP_CREATEWINDOW |
                                            DESKTOP_HOOKCONTROL | DESKTOP_JOURNALRECORD | DESKTOP_JOURNALPLAYBACK |
                                            DESKTOP_WRITEOBJECTS;
    if (access & GENERIC_EXECUTE) access |= STANDARD_RIGHTS_EXECUTE | DESKTOP_SWITCHDESKTOP;
    if (access & GENERIC_ALL)     access |= STANDARD_RIGHTS_REQUIRED | DESKTOP_ALL_ACCESS;
    return access & ~(GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE | GENERIC_ALL);
}

272 273 274 275 276 277
/* retrieve the thread desktop, checking the handle access rights */
struct desktop *get_thread_desktop( struct thread *thread, unsigned int access )
{
    return get_desktop_obj( thread->process, thread->desktop, access );
}

278 279 280
/* set the process default desktop handle */
void set_process_default_desktop( struct process *process, struct desktop *desktop,
                                  obj_handle_t handle )
281
{
282 283
    struct thread *thread;
    struct desktop *old_desktop;
284

285
    if (process->desktop == handle) return;  /* nothing to do */
286

287 288
    if (!(old_desktop = get_desktop_obj( process, process->desktop, 0 ))) clear_error();
    process->desktop = handle;
289

290 291 292 293 294 295
    /* set desktop for threads that don't have one yet */
    LIST_FOR_EACH_ENTRY( thread, &process->thread_list, struct thread, proc_entry )
        if (!thread->desktop) thread->desktop = handle;

    desktop->users++;
    if (desktop->close_timeout)
296
    {
297 298
        remove_timeout_user( desktop->close_timeout );
        desktop->close_timeout = NULL;
299
    }
300
    if (old_desktop)
301
    {
302 303
        old_desktop->users--;
        release_object( old_desktop );
304 305 306
    }
}

307 308
/* connect a process to its window station */
void connect_process_winstation( struct process *process, struct thread *parent )
309
{
310 311 312
    struct winstation *winstation = NULL;
    struct desktop *desktop = NULL;
    obj_handle_t handle;
313

314 315 316 317 318 319
    /* check for an inherited winstation handle (don't ask...) */
    if ((handle = find_inherited_handle( process, &winstation_ops )))
    {
        winstation = (struct winstation *)get_handle_obj( process, handle, 0, &winstation_ops );
    }
    else if (parent && parent->process->winstation)
320
    {
321 322 323 324 325 326
        handle = duplicate_handle( parent->process, parent->process->winstation,
                                   process, 0, 0, DUP_HANDLE_SAME_ACCESS );
        winstation = (struct winstation *)get_handle_obj( process, handle, 0, &winstation_ops );
    }
    if (!winstation) goto done;
    process->winstation = handle;
327

328 329 330 331
    if ((handle = find_inherited_handle( process, &desktop_ops )))
    {
        desktop = get_desktop_obj( process, handle, 0 );
        if (!desktop || desktop->winstation != winstation) goto done;
332
    }
333 334 335 336 337 338 339 340 341 342 343 344 345 346
    else if (parent && parent->desktop)
    {
        desktop = get_desktop_obj( parent->process, parent->desktop, 0 );
        if (!desktop || desktop->winstation != winstation) goto done;
        handle = duplicate_handle( parent->process, parent->desktop,
                                   process, 0, 0, DUP_HANDLE_SAME_ACCESS );
    }

    if (handle) set_process_default_desktop( process, desktop, handle );

done:
    if (desktop) release_object( desktop );
    if (winstation) release_object( winstation );
    clear_error();
347 348
}

349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369
static void close_desktop_timeout( void *private )
{
    struct desktop *desktop = private;

    desktop->close_timeout = NULL;
    unlink_named_object( &desktop->obj );  /* make sure no other process can open it */
    close_desktop_window( desktop );  /* and signal the owner to quit */
}

/* close the desktop of a given process */
void close_process_desktop( struct process *process )
{
    struct desktop *desktop;

    if (process->desktop && (desktop = get_desktop_obj( process, process->desktop, 0 )))
    {
        assert( desktop->users > 0 );
        desktop->users--;
        /* if we have one remaining user, it has to be the manager of the desktop window */
        if (desktop->users == 1 && get_top_window_owner( desktop ))
        {
370
            assert( !desktop->close_timeout );
371
            desktop->close_timeout = add_timeout_user( -TICKS_PER_SEC, close_desktop_timeout, desktop );
372 373 374 375 376 377
        }
        release_object( desktop );
    }
    clear_error();  /* ignore errors */
}

378 379 380 381 382 383
/* close the desktop of a given thread */
void close_thread_desktop( struct thread *thread )
{
    obj_handle_t handle = thread->desktop;

    thread->desktop = 0;
384
    if (handle) close_handle( thread->process, handle );
385
    clear_error();  /* ignore errors */
386 387 388 389 390 391 392
}


/* create a window station */
DECL_HANDLER(create_winstation)
{
    struct winstation *winstation;
393
    struct unicode_str name;
394 395

    reply->handle = 0;
396
    get_req_unicode_str( &name );
397
    if ((winstation = create_winstation( &name, req->attributes, req->flags )))
398
    {
399
        reply->handle = alloc_handle( current->process, winstation, req->access, req->attributes );
400 401 402 403 404 405 406
        release_object( winstation );
    }
}

/* open a handle to a window station */
DECL_HANDLER(open_winstation)
{
407 408 409
    struct unicode_str name;

    get_req_unicode_str( &name );
410
    if (winstation_namespace)
411
        reply->handle = open_object( winstation_namespace, &name, &winstation_ops, req->access,
412
                                     req->attributes );
413 414 415 416 417 418 419 420 421 422 423 424 425
    else
        set_error( STATUS_OBJECT_NAME_NOT_FOUND );
}


/* close a window station */
DECL_HANDLER(close_winstation)
{
    struct winstation *winstation;

    if ((winstation = (struct winstation *)get_handle_obj( current->process, req->handle,
                                                           0, &winstation_ops )))
    {
426
        if (!close_handle( current->process, req->handle )) set_error( STATUS_ACCESS_DENIED );
427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457
        release_object( winstation );
    }
}


/* get the process current window station */
DECL_HANDLER(get_process_winstation)
{
    reply->handle = current->process->winstation;
}


/* set the process current window station */
DECL_HANDLER(set_process_winstation)
{
    struct winstation *winstation;

    if ((winstation = (struct winstation *)get_handle_obj( current->process, req->handle,
                                                           0, &winstation_ops )))
    {
        /* FIXME: should we close the old one? */
        current->process->winstation = req->handle;
        release_object( winstation );
    }
}

/* create a desktop */
DECL_HANDLER(create_desktop)
{
    struct desktop *desktop;
    struct winstation *winstation;
458
    struct unicode_str name;
459 460

    reply->handle = 0;
461
    get_req_unicode_str( &name );
462 463
    if ((winstation = get_process_winstation( current->process, WINSTA_CREATEDESKTOP )))
    {
464
        if ((desktop = create_desktop( &name, req->attributes, req->flags, winstation )))
465
        {
466
            reply->handle = alloc_handle( current->process, desktop, req->access, req->attributes );
467 468 469 470 471 472 473 474 475 476
            release_object( desktop );
        }
        release_object( winstation );
    }
}

/* open a handle to a desktop */
DECL_HANDLER(open_desktop)
{
    struct winstation *winstation;
477
    struct unicode_str name;
478

479
    get_req_unicode_str( &name );
480 481
    if ((winstation = get_process_winstation( current->process, 0 /* FIXME: access rights? */ )))
    {
482
        struct unicode_str full_str;
483 484
        WCHAR *full_name;

485
        if ((full_name = build_desktop_name( &name, winstation, &full_str )))
486
        {
487
            reply->handle = open_object( winstation_namespace, &full_str, &desktop_ops, req->access,
488
                                         req->attributes );
489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504
            free( full_name );
        }
        release_object( winstation );
    }
}


/* close a desktop */
DECL_HANDLER(close_desktop)
{
    struct desktop *desktop;

    /* make sure it is a desktop handle */
    if ((desktop = (struct desktop *)get_handle_obj( current->process, req->handle,
                                                     0, &desktop_ops )))
    {
505
        if (!close_handle( current->process, req->handle )) set_error( STATUS_DEVICE_BUSY );
506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524
        release_object( desktop );
    }
}


/* get the thread current desktop */
DECL_HANDLER(get_thread_desktop)
{
    struct thread *thread;

    if (!(thread = get_thread_from_id( req->tid ))) return;
    reply->handle = thread->desktop;
    release_object( thread );
}


/* set the thread current desktop */
DECL_HANDLER(set_thread_desktop)
{
525 526
    struct desktop *old_desktop, *new_desktop;
    struct winstation *winstation;
527

528 529 530 531
    if (!(winstation = get_process_winstation( current->process, 0 /* FIXME: access rights? */ )))
        return;

    if (!(new_desktop = get_desktop_obj( current->process, req->handle, 0 )))
532
    {
533 534
        release_object( winstation );
        return;
535
    }
536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554
    if (new_desktop->winstation != winstation)
    {
        set_error( STATUS_ACCESS_DENIED );
        release_object( new_desktop );
        release_object( winstation );
        return;
    }

    /* check if we are changing to a new desktop */

    if (!(old_desktop = get_desktop_obj( current->process, current->desktop, 0)))
        clear_error();  /* ignore error */

    /* when changing desktop, we can't have any users on the current one */
    if (old_desktop != new_desktop && current->desktop_users > 0)
        set_error( STATUS_DEVICE_BUSY );
    else
        current->desktop = req->handle;  /* FIXME: should we close the old one? */

555 556 557
    if (!current->process->desktop)
        set_process_default_desktop( current->process, new_desktop, req->handle );

558
    if (old_desktop != new_desktop && current->queue) detach_thread_input( current );
559

560 561 562
    if (old_desktop) release_object( old_desktop );
    release_object( new_desktop );
    release_object( winstation );
563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594
}


/* get/set information about a user object (window station or desktop) */
DECL_HANDLER(set_user_object_info)
{
    struct object *obj;

    if (!(obj = get_handle_obj( current->process, req->handle, 0, NULL ))) return;

    if (obj->ops == &desktop_ops)
    {
        struct desktop *desktop = (struct desktop *)obj;
        reply->is_desktop = 1;
        reply->old_obj_flags = desktop->flags;
        if (req->flags & SET_USER_OBJECT_FLAGS) desktop->flags = req->obj_flags;
    }
    else if (obj->ops == &winstation_ops)
    {
        struct winstation *winstation = (struct winstation *)obj;
        reply->is_desktop = 0;
        reply->old_obj_flags = winstation->flags;
        if (req->flags & SET_USER_OBJECT_FLAGS) winstation->flags = req->obj_flags;
    }
    else
    {
        set_error( STATUS_OBJECT_TYPE_MISMATCH );
        release_object( obj );
        return;
    }
    if (get_reply_max_size())
    {
595
        data_size_t len;
596 597 598 599 600
        const WCHAR *ptr, *name = get_object_name( obj, &len );

        /* if there is a backslash return the part of the name after it */
        if (name && (ptr = memchrW( name, '\\', len )))
        {
601
            len -= (ptr + 1 - name) * sizeof(WCHAR);
602 603 604 605 606 607
            name = ptr + 1;
        }
        if (name) set_reply_data( name, min( len, get_reply_max_size() ));
    }
    release_object( obj );
}