static int guac_rdp_handle_connection()

in src/protocols/rdp/rdp.c [511:698]


static int guac_rdp_handle_connection(guac_client* client) {

    guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
    guac_rdp_settings* settings = rdp_client->settings;

    /* Init random number generator */
    srandom(time(NULL));

    guac_rwlock_acquire_write_lock(&(rdp_client->lock));

    /* Create display */
    rdp_client->display = guac_display_alloc(client);

    guac_display_layer* default_layer = guac_display_default_layer(rdp_client->display);
    guac_display_layer_resize(default_layer, rdp_client->settings->width, rdp_client->settings->height);

    /* Use lossless compression only if requested (otherwise, use default
     * heuristics) */
    guac_display_layer_set_lossless(default_layer, settings->lossless);

    rdp_client->current_surface = default_layer;

    rdp_client->available_svc = guac_common_list_alloc();

    /* Init client */
    freerdp* rdp_inst = freerdp_new();

    /*
     * If the freerdp instance has a LoadChannels callback for loading plugins
     * we use that instead of the PreConnect callback to load plugins.
     */
    #ifdef RDP_INST_HAS_LOAD_CHANNELS
        rdp_inst->LoadChannels = rdp_freerdp_load_channels;
    #endif
    rdp_inst->PreConnect = rdp_freerdp_pre_connect;
    rdp_inst->Authenticate = rdp_freerdp_authenticate;

#ifdef HAVE_FREERDP_VERIFYCERTIFICATEEX
    rdp_inst->VerifyCertificateEx = rdp_freerdp_verify_certificate;
#else
    rdp_inst->VerifyCertificate = rdp_freerdp_verify_certificate;
#endif

    /* Allocate FreeRDP context */
    rdp_inst->ContextSize = sizeof(rdp_freerdp_context);

    if (!freerdp_context_new(rdp_inst)) {
        guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR,
                "FreeRDP initialization failed before connecting. Please "
                "check for errors earlier in the logs and/or enable "
                "debug-level logging for guacd.");
        goto fail;
    }

    ((rdp_freerdp_context*) GUAC_RDP_CONTEXT(rdp_inst))->client = client;

    /* Load keymap into client */
    rdp_client->keyboard = guac_rdp_keyboard_alloc(client,
            settings->server_layout);

    /* Set default pointer */
    guac_display_set_cursor(rdp_client->display, GUAC_DISPLAY_CURSOR_POINTER);

    /* 
     * Downgrade the lock to allow for concurrent read access.
     * Access to read locks needs to be made available for other processes such
     * as the join_pending_handler to use while we await credentials from the user.
     */
    guac_rwlock_release_lock(&(rdp_client->lock));
    guac_rwlock_acquire_read_lock(&(rdp_client->lock));

    /* Connect to RDP server */
    if (!freerdp_connect(rdp_inst)) {
        guac_rdp_client_abort(client, rdp_inst);
        goto fail;
    }

    /* Upgrade to write lock again for further exclusive operations */
    guac_rwlock_release_lock(&(rdp_client->lock));
    guac_rwlock_acquire_write_lock(&(rdp_client->lock));

    /* Connection complete */
    rdp_client->rdp_inst = rdp_inst;

    /* Signal that reconnect has been completed */
    guac_rdp_disp_reconnect_complete(rdp_client->disp);

    guac_rwlock_release_lock(&(rdp_client->lock));

    rdp_client->render_thread = guac_display_render_thread_create(rdp_client->display);

    /* Handle messages from RDP server while client is running */
    while (client->state == GUAC_CLIENT_RUNNING
            && !guac_rdp_disp_reconnect_needed(rdp_client->disp)) {

        /* Update remote display size */
        guac_rdp_disp_update_size(rdp_client->disp, settings, rdp_inst);

        /* Wait for data and construct a reasonable frame */

        int wait_result = rdp_guac_client_wait_for_messages(client, GUAC_RDP_MESSAGE_CHECK_INTERVAL);
        if (wait_result < 0)
            break;

        /* Handle any queued FreeRDP events (this may result in RDP messages
         * being sent), aborting later if FreeRDP event handling fails */
        if (!guac_rdp_handle_events(rdp_client))
            wait_result = -1;

        /* Test whether the RDP server is closing the connection */
        int connection_closing;
#ifdef HAVE_DISCONNECT_CONTEXT
        connection_closing = freerdp_shall_disconnect_context(rdp_inst->context);
#else
        connection_closing = freerdp_shall_disconnect(rdp_inst);
#endif

        /* Close connection cleanly if server is disconnecting */
        if (connection_closing)
            guac_rdp_client_abort(client, rdp_inst);

        /* If a low-level connection error occurred, fail */
        else if (wait_result < 0)
            guac_client_abort(client, GUAC_PROTOCOL_STATUS_UPSTREAM_UNAVAILABLE,
                    "Connection closed.");

    }

    guac_rwlock_acquire_write_lock(&(rdp_client->lock));

    /* Clean up print job, if active */
    if (rdp_client->active_job != NULL) {
        guac_rdp_print_job_kill(rdp_client->active_job);
        guac_rdp_print_job_free(rdp_client->active_job);
    }

    /* Disconnect client and channels */
    pthread_mutex_lock(&(rdp_client->message_lock));
    freerdp_disconnect(rdp_inst);
    pthread_mutex_unlock(&(rdp_client->message_lock));

    /* Stop render loop */
    guac_display_render_thread_destroy(rdp_client->render_thread);
    rdp_client->render_thread = NULL;

    /* Remove reference to FreeRDP's GDI buffer so that it can be safely freed
     * prior to freeing the guac_display */
    guac_display_layer_raw_context* context = guac_display_layer_open_raw(default_layer);
    context->buffer = NULL;
    guac_display_layer_close_raw(default_layer, context);

    /* Clean up FreeRDP internal GDI implementation (this must be done BEFORE
     * freeing the guac_display, as freeing the GDI will free objects like
     * rdpPointer that will attempt to free associated guac_display_layer
     * instances during cleanup) */
    gdi_free(rdp_inst);

    /* Free display */
    guac_display_free(rdp_client->display);
    rdp_client->display = NULL;

    /* Clean up RDP client context */
    freerdp_context_free(rdp_inst);

    /* Clean up RDP client */
    freerdp_free(rdp_inst);
    rdp_client->rdp_inst = NULL;

    /* Free SVC list */
    guac_common_list_free(rdp_client->available_svc, NULL);
    rdp_client->available_svc = NULL;

    /* Free RDP keyboard state */
    guac_rdp_keyboard_free(rdp_client->keyboard);
    rdp_client->keyboard = NULL;

    guac_rwlock_release_lock(&(rdp_client->lock));

    /* Client is now disconnected */
    guac_client_log(client, GUAC_LOG_INFO, "Internal RDP client disconnected");

    return 0;

fail:
    guac_rwlock_release_lock(&(rdp_client->lock));
    return 1;

}