static int guac_rdp_handle_connection()

in src/protocols/rdp/rdp.c [473:675]


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));

    pthread_rwlock_wrlock(&(rdp_client->lock));

    /* Create display */
    rdp_client->display = guac_common_display_alloc(client,
            rdp_client->settings->width,
            rdp_client->settings->height);

    /* Use lossless compression only if requested (otherwise, use default
     * heuristics) */
    guac_common_display_set_lossless(rdp_client->display, settings->lossless);

    rdp_client->current_surface = rdp_client->display->default_surface;

    rdp_client->available_svc = guac_common_list_alloc();

    /* Init client */
    freerdp* rdp_inst = freerdp_new();
    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*) rdp_inst->context)->client = client;

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

    /* Set default pointer */
    guac_common_cursor_set_pointer(rdp_client->display->cursor);

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

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

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

    pthread_rwlock_unlock(&(rdp_client->lock));

    /* 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_FRAME_START_TIMEOUT);
        if (wait_result > 0) {

            int processing_lag = guac_client_get_processing_lag(client);

            /* Read server messages until frame is built */
            do {

                guac_timestamp frame_end;
                int frame_remaining;

                /* Handle any queued FreeRDP events (this may result in RDP
                 * messages being sent) */
                pthread_mutex_lock(&(rdp_client->message_lock));
                int event_result = freerdp_check_event_handles(rdp_inst->context);
                pthread_mutex_unlock(&(rdp_client->message_lock));

                /* Abort if FreeRDP event handling fails */
                if (!event_result) {
                    wait_result = -1;
                    break;
                }

                /* Continue handling inbound data if we are in the middle of an RDP frame */
                if (rdp_client->in_frame) {
                    wait_result = rdp_guac_client_wait_for_messages(client, GUAC_RDP_FRAME_START_TIMEOUT);
                    if (wait_result >= 0)
                        continue;
                }

                /* Calculate time remaining in frame */
                guac_timestamp frame_start = client->last_sent_timestamp;
                frame_end = guac_timestamp_current();
                frame_remaining = frame_start + GUAC_RDP_FRAME_DURATION
                                - frame_end;

                /* Calculate time that client needs to catch up */
                int time_elapsed = frame_end - frame_start;
                int required_wait = processing_lag - time_elapsed;

                /* Increase the duration of this frame if client is lagging */
                if (required_wait > GUAC_RDP_FRAME_TIMEOUT)
                    wait_result = rdp_guac_client_wait_for_messages(client,
                            required_wait);

                /* Wait again if frame remaining */
                else if (frame_remaining > 0)
                    wait_result = rdp_guac_client_wait_for_messages(client,
                            GUAC_RDP_FRAME_TIMEOUT);
                else
                    break;

            } while (wait_result > 0);

        }

        /* Test whether the RDP server is closing the connection */
        int connection_closing = freerdp_shall_disconnect(rdp_inst);

        /* 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.");

        /* Flush frame only if successful and an RDP frame is not known to be
         * in progress */
        else if (!rdp_client->frames_supported || rdp_client->frames_received) {
            guac_common_display_flush(rdp_client->display);
            guac_client_end_multiple_frames(client, rdp_client->frames_received);
            guac_socket_flush(client->socket);
            rdp_client->frames_received = 0;
        }

    }

    pthread_rwlock_wrlock(&(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));

    /* Clean up FreeRDP internal GDI implementation */
    gdi_free(rdp_inst);

    /* 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);
    rdp_client->available_svc = NULL;

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

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

    pthread_rwlock_unlock(&(rdp_client->lock));

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

    return 0;

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

}