Beispiel #1
0
int guac_client_init(guac_client* client, int argc, char** argv) {

    rdp_guac_client_data* guac_client_data;
    guac_rdp_settings* settings;

    freerdp* rdp_inst;

    /* Validate number of arguments received */
    if (argc != RDP_ARGS_COUNT) {
        guac_client_abort(client, GUAC_PROTOCOL_STATUS_SERVER_ERROR, "Wrong argument count received.");
        return 1;
    }

    /* Allocate client data */
    guac_client_data = malloc(sizeof(rdp_guac_client_data));

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

    /* Init client */
#ifdef HAVE_FREERDP_CHANNELS_GLOBAL_INIT
    freerdp_channels_global_init();
#endif
    rdp_inst = freerdp_new();
    rdp_inst->PreConnect = rdp_freerdp_pre_connect;
    rdp_inst->PostConnect = rdp_freerdp_post_connect;
    rdp_inst->Authenticate = rdp_freerdp_authenticate;
    rdp_inst->VerifyCertificate = rdp_freerdp_verify_certificate;
    rdp_inst->ReceiveChannelData = __guac_receive_channel_data;

    /* Allocate FreeRDP context */
#ifdef LEGACY_FREERDP
    rdp_inst->context_size = sizeof(rdp_freerdp_context);
#else
    rdp_inst->ContextSize = sizeof(rdp_freerdp_context);
#endif
    rdp_inst->ContextNew  = (pContextNew) rdp_freerdp_context_new;
    rdp_inst->ContextFree = (pContextFree) rdp_freerdp_context_free;
    freerdp_context_new(rdp_inst);

    /* Set settings */
    settings = &(guac_client_data->settings);

    /* Console */
    settings->console         = (strcmp(argv[IDX_CONSOLE], "true") == 0);
    settings->console_audio   = (strcmp(argv[IDX_CONSOLE_AUDIO], "true") == 0);

    /* Certificate and auth */
    settings->ignore_certificate = (strcmp(argv[IDX_IGNORE_CERT], "true") == 0);
    settings->disable_authentication = (strcmp(argv[IDX_DISABLE_AUTH], "true") == 0);

    /* NLA security */
    if (strcmp(argv[IDX_SECURITY], "nla") == 0) {
        guac_client_log(client, GUAC_LOG_INFO, "Security mode: NLA");
        settings->security_mode = GUAC_SECURITY_NLA;
    }

    /* TLS security */
    else if (strcmp(argv[IDX_SECURITY], "tls") == 0) {
        guac_client_log(client, GUAC_LOG_INFO, "Security mode: TLS");
        settings->security_mode = GUAC_SECURITY_TLS;
    }

    /* RDP security */
    else if (strcmp(argv[IDX_SECURITY], "rdp") == 0) {
        guac_client_log(client, GUAC_LOG_INFO, "Security mode: RDP");
        settings->security_mode = GUAC_SECURITY_RDP;
    }

    /* ANY security (allow server to choose) */
    else if (strcmp(argv[IDX_SECURITY], "any") == 0) {
        guac_client_log(client, GUAC_LOG_INFO, "Security mode: ANY");
        settings->security_mode = GUAC_SECURITY_ANY;
    }

    /* If nothing given, default to RDP */
    else {
        guac_client_log(client, GUAC_LOG_INFO, "No security mode specified. Defaulting to RDP.");
        settings->security_mode = GUAC_SECURITY_RDP;
    }

    /* Set hostname */
    settings->hostname = strdup(argv[IDX_HOSTNAME]);

    /* If port specified, use it */
    settings->port = RDP_DEFAULT_PORT;
    if (argv[IDX_PORT][0] != '\0')
        settings->port = atoi(argv[IDX_PORT]);

    guac_client_log(client, GUAC_LOG_DEBUG,
            "Client resolution is %ix%i at %i DPI",
            client->info.optimal_width,
            client->info.optimal_height,
            client->info.optimal_resolution);

    /* Use suggested resolution unless overridden */
    settings->resolution = guac_rdp_suggest_resolution(client);
    if (argv[IDX_DPI][0] != '\0')
        settings->resolution = atoi(argv[IDX_DPI]);

    /* Use optimal width unless overridden */
    settings->width = client->info.optimal_width
                    * settings->resolution
                    / client->info.optimal_resolution;

    if (argv[IDX_WIDTH][0] != '\0')
        settings->width = atoi(argv[IDX_WIDTH]);

    /* Use default width if given width is invalid. */
    if (settings->width <= 0) {
        settings->width = RDP_DEFAULT_WIDTH;
        guac_client_log(client, GUAC_LOG_ERROR,
                "Invalid width: \"%s\". Using default of %i.",
                argv[IDX_WIDTH], settings->width);
    }

    /* Round width down to nearest multiple of 4 */
    settings->width = settings->width & ~0x3;

    /* Use optimal height unless overridden */
    settings->height = client->info.optimal_height
                     * settings->resolution
                     / client->info.optimal_resolution;

    if (argv[IDX_HEIGHT][0] != '\0')
        settings->height = atoi(argv[IDX_HEIGHT]);

    /* Use default height if given height is invalid. */
    if (settings->height <= 0) {
        settings->height = RDP_DEFAULT_HEIGHT;
        guac_client_log(client, GUAC_LOG_ERROR,
                "Invalid height: \"%s\". Using default of %i.",
                argv[IDX_WIDTH], settings->height);
    }

    guac_client_log(client, GUAC_LOG_DEBUG,
            "Using resolution of %ix%i at %i DPI",
            settings->width,
            settings->height,
            settings->resolution);

    /* Domain */
    settings->domain = NULL;
    if (argv[IDX_DOMAIN][0] != '\0')
        settings->domain = strdup(argv[IDX_DOMAIN]);

    /* Username */
    settings->username = NULL;
    if (argv[IDX_USERNAME][0] != '\0')
        settings->username = strdup(argv[IDX_USERNAME]);

    /* Password */
    settings->password = NULL;
    if (argv[IDX_PASSWORD][0] != '\0')
        settings->password = strdup(argv[IDX_PASSWORD]);

    /* Client name */
    settings->client_name = NULL;
    if (argv[IDX_CLIENT_NAME][0] != '\0')
        settings->client_name = strdup(argv[IDX_CLIENT_NAME]);

    /* Initial program */
    settings->initial_program = NULL;
    if (argv[IDX_INITIAL_PROGRAM][0] != '\0')
        settings->initial_program = strdup(argv[IDX_INITIAL_PROGRAM]);

    /* RemoteApp program */
    settings->remote_app = NULL;
    if (argv[IDX_REMOTE_APP][0] != '\0')
        settings->remote_app = strdup(argv[IDX_REMOTE_APP]);

    /* RemoteApp working directory */
    settings->remote_app_dir = NULL;
    if (argv[IDX_REMOTE_APP_DIR][0] != '\0')
        settings->remote_app_dir = strdup(argv[IDX_REMOTE_APP_DIR]);

    /* RemoteApp arguments */
    settings->remote_app_args = NULL;
    if (argv[IDX_REMOTE_APP_ARGS][0] != '\0')
        settings->remote_app_args = strdup(argv[IDX_REMOTE_APP_ARGS]);

    /* Static virtual channels */
    settings->svc_names = NULL;
    if (argv[IDX_STATIC_CHANNELS][0] != '\0')
        settings->svc_names = guac_split(argv[IDX_STATIC_CHANNELS], ',');

    /* Performance flags */
    settings->wallpaper_enabled           = (strcmp(argv[IDX_ENABLE_WALLPAPER],           "true") == 0);
    settings->theming_enabled             = (strcmp(argv[IDX_ENABLE_THEMING],             "true") == 0);
    settings->font_smoothing_enabled      = (strcmp(argv[IDX_ENABLE_FONT_SMOOTHING],      "true") == 0);
    settings->full_window_drag_enabled    = (strcmp(argv[IDX_ENABLE_FULL_WINDOW_DRAG],    "true") == 0);
    settings->desktop_composition_enabled = (strcmp(argv[IDX_ENABLE_DESKTOP_COMPOSITION], "true") == 0);
    settings->menu_animations_enabled     = (strcmp(argv[IDX_ENABLE_MENU_ANIMATIONS],     "true") == 0);

    /* Session color depth */
    settings->color_depth = RDP_DEFAULT_DEPTH;
    if (argv[IDX_COLOR_DEPTH][0] != '\0')
        settings->color_depth = atoi(argv[IDX_COLOR_DEPTH]);

    /* Use default depth if given depth is invalid. */
    if (settings->color_depth == 0) {
        settings->color_depth = RDP_DEFAULT_DEPTH;
        guac_client_log(client, GUAC_LOG_ERROR,
                "Invalid color-depth: \"%s\". Using default of %i.",
                argv[IDX_WIDTH], settings->color_depth);
    }

    /* Audio enable/disable */
    guac_client_data->settings.audio_enabled =
        (strcmp(argv[IDX_DISABLE_AUDIO], "true") != 0);

    /* Printing enable/disable */
    guac_client_data->settings.printing_enabled =
        (strcmp(argv[IDX_ENABLE_PRINTING], "true") == 0);

    /* Drive enable/disable */
    guac_client_data->settings.drive_enabled =
        (strcmp(argv[IDX_ENABLE_DRIVE], "true") == 0);

    guac_client_data->settings.drive_path = strdup(argv[IDX_DRIVE_PATH]);

    guac_client_data->settings.create_drive_path =
        (strcmp(argv[IDX_CREATE_DRIVE_PATH], "true") == 0);

    /* Store client data */
    guac_client_data->rdp_inst = rdp_inst;
    guac_client_data->mouse_button_mask = 0;
    guac_client_data->clipboard = guac_common_clipboard_alloc(GUAC_RDP_CLIPBOARD_MAX_LENGTH);
    guac_client_data->requested_clipboard_format = CB_FORMAT_TEXT;
    guac_client_data->audio = NULL;
    guac_client_data->filesystem = NULL;
    guac_client_data->available_svc = guac_common_list_alloc();

    /* Main socket needs to be threadsafe */
    guac_socket_require_threadsafe(client->socket);

    /* Recursive attribute for locks */
    pthread_mutexattr_init(&(guac_client_data->attributes));
    pthread_mutexattr_settype(&(guac_client_data->attributes),
            PTHREAD_MUTEX_RECURSIVE);

    /* Init RDP lock */
    pthread_mutex_init(&(guac_client_data->rdp_lock),
           &(guac_client_data->attributes));

    /* Clear keysym state mapping and keymap */
    memset(guac_client_data->keysym_state, 0,
            sizeof(guac_rdp_keysym_state_map));

    memset(guac_client_data->keymap, 0,
            sizeof(guac_rdp_static_keymap));

    client->data = guac_client_data;
    ((rdp_freerdp_context*) rdp_inst->context)->client = client;

    /* Pick keymap based on argument */
    settings->server_layout = NULL;
    if (argv[IDX_SERVER_LAYOUT][0] != '\0')
        settings->server_layout =
            guac_rdp_keymap_find(argv[IDX_SERVER_LAYOUT]);

    /* If no keymap requested, use default */
    if (settings->server_layout == NULL)
        settings->server_layout = guac_rdp_keymap_find(GUAC_DEFAULT_KEYMAP);

    /* Load keymap into client */
    __guac_rdp_client_load_keymap(client, settings->server_layout);

#ifdef ENABLE_COMMON_SSH
    guac_common_ssh_init(client);

    /* Connect via SSH if SFTP is enabled */
    if (strcmp(argv[IDX_ENABLE_SFTP], "true") == 0) {

        guac_client_log(client, GUAC_LOG_DEBUG,
                "Connecting via SSH for SFTP filesystem access.");

        /* Parse username - use RDP username by default */
        const char* sftp_username = argv[IDX_SFTP_USERNAME];
        if (sftp_username[0] == '\0' && settings->username != NULL)
            sftp_username = settings->username;

        guac_client_data->sftp_user =
            guac_common_ssh_create_user(sftp_username);

        /* Import private key, if given */
        if (argv[IDX_SFTP_PRIVATE_KEY][0] != '\0') {

            guac_client_log(client, GUAC_LOG_DEBUG,
                    "Authenticating with private key.");

            /* Abort if private key cannot be read */
            if (guac_common_ssh_user_import_key(guac_client_data->sftp_user,
                        argv[IDX_SFTP_PRIVATE_KEY],
                        argv[IDX_SFTP_PASSPHRASE])) {
                guac_common_ssh_destroy_user(guac_client_data->sftp_user);
                return 1;
            }

        }

        /* Otherwise, use specified password */
        else {

            guac_client_log(client, GUAC_LOG_DEBUG,
                    "Authenticating with password.");

            /* Parse password - use RDP password by default */
            const char* sftp_password = argv[IDX_SFTP_PASSWORD];
            if (sftp_password[0] == '\0' && settings->password != NULL)
                sftp_password = settings->password;

            guac_common_ssh_user_set_password(guac_client_data->sftp_user,
                    sftp_password);

        }

        /* Parse hostname - use RDP hostname by default */
        const char* sftp_hostname = argv[IDX_SFTP_HOSTNAME];
        if (sftp_hostname[0] == '\0')
            sftp_hostname = settings->hostname;

        /* Parse port, defaulting to standard SSH port */
        const char* sftp_port = argv[IDX_SFTP_PORT];
        if (sftp_port[0] == '\0')
            sftp_port = "22";

        /* Attempt SSH connection */
        guac_client_data->sftp_session =
            guac_common_ssh_create_session(client, sftp_hostname, sftp_port,
                    guac_client_data->sftp_user);

        /* Fail if SSH connection does not succeed */
        if (guac_client_data->sftp_session == NULL) {
            /* Already aborted within guac_common_ssh_create_session() */
            guac_common_ssh_destroy_user(guac_client_data->sftp_user);
            return 1;
        }

        /* Load and expose filesystem */
        guac_client_data->sftp_filesystem =
            guac_common_ssh_create_sftp_filesystem(
                    guac_client_data->sftp_session, "/");

        /* Abort if SFTP connection fails */
        if (guac_client_data->sftp_filesystem == NULL) {
            guac_common_ssh_destroy_session(guac_client_data->sftp_session);
            guac_common_ssh_destroy_user(guac_client_data->sftp_user);
            return 1;
        }

        /* Configure destination for basic uploads, if specified */
        if (argv[IDX_SFTP_DIRECTORY][0] != '\0') {
            client->file_handler = guac_rdp_sftp_file_handler;
            guac_common_ssh_sftp_set_upload_path(
                    guac_client_data->sftp_filesystem,
                    argv[IDX_SFTP_DIRECTORY]);
        }

        /* Otherwise, use SFTP for basic uploads only if drive not enabled */
        else if (!settings->drive_enabled)
            client->file_handler = guac_rdp_sftp_file_handler;

        guac_client_log(client, GUAC_LOG_DEBUG,
                "SFTP connection succeeded.");

    }
#endif

    /* Create default surface */
    guac_client_data->default_surface = guac_common_surface_alloc(client->socket, GUAC_DEFAULT_LAYER,
                                                                  settings->width, settings->height);
    guac_client_data->current_surface = guac_client_data->default_surface;

    /* Send connection name */
    guac_protocol_send_name(client->socket, settings->hostname);

    /* Set default pointer */
    guac_common_set_pointer_cursor(client);

    /* Push desired settings to FreeRDP */
    guac_rdp_push_settings(settings, rdp_inst);

    /* Connect to RDP server */
    if (!freerdp_connect(rdp_inst)) {
        guac_client_abort(client, GUAC_PROTOCOL_STATUS_UPSTREAM_ERROR, "Error connecting to RDP server");
        return 1;
    }

    /* Success */
    return 0;

}
guac_rdp_settings* guac_rdp_parse_args(guac_user* user,
        int argc, const char** argv) {

    /* Validate arg count */
    if (argc != RDP_ARGS_COUNT) {
        guac_user_log(user, GUAC_LOG_WARNING, "Incorrect number of connection "
                "parameters provided: expected %i, got %i.",
                RDP_ARGS_COUNT, argc);
        return NULL;
    }

    guac_rdp_settings* settings = calloc(1, sizeof(guac_rdp_settings));

    /* Use console */
    settings->console =
        guac_user_parse_args_boolean(user, GUAC_RDP_CLIENT_ARGS, argv,
                IDX_CONSOLE, 0);

    /* Enable/disable console audio */
    settings->console_audio =
        guac_user_parse_args_boolean(user, GUAC_RDP_CLIENT_ARGS, argv,
                IDX_CONSOLE_AUDIO, 0);

    /* Ignore SSL/TLS certificate */
    settings->ignore_certificate =
        guac_user_parse_args_boolean(user, GUAC_RDP_CLIENT_ARGS, argv,
                IDX_IGNORE_CERT, 0);

    /* Disable authentication */
    settings->disable_authentication =
        guac_user_parse_args_boolean(user, GUAC_RDP_CLIENT_ARGS, argv,
                IDX_DISABLE_AUTH, 0);

    /* NLA security */
    if (strcmp(argv[IDX_SECURITY], "nla") == 0) {
        guac_user_log(user, GUAC_LOG_INFO, "Security mode: NLA");
        settings->security_mode = GUAC_SECURITY_NLA;
    }

    /* TLS security */
    else if (strcmp(argv[IDX_SECURITY], "tls") == 0) {
        guac_user_log(user, GUAC_LOG_INFO, "Security mode: TLS");
        settings->security_mode = GUAC_SECURITY_TLS;
    }

    /* RDP security */
    else if (strcmp(argv[IDX_SECURITY], "rdp") == 0) {
        guac_user_log(user, GUAC_LOG_INFO, "Security mode: RDP");
        settings->security_mode = GUAC_SECURITY_RDP;
    }

    /* ANY security (allow server to choose) */
    else if (strcmp(argv[IDX_SECURITY], "any") == 0) {
        guac_user_log(user, GUAC_LOG_INFO, "Security mode: ANY");
        settings->security_mode = GUAC_SECURITY_ANY;
    }

    /* If nothing given, default to RDP */
    else {
        guac_user_log(user, GUAC_LOG_INFO, "No security mode specified. Defaulting to RDP.");
        settings->security_mode = GUAC_SECURITY_RDP;
    }

    /* Set hostname */
    settings->hostname =
        guac_user_parse_args_string(user, GUAC_RDP_CLIENT_ARGS, argv,
                IDX_HOSTNAME, "");

    /* If port specified, use it */
    settings->port =
        guac_user_parse_args_int(user, GUAC_RDP_CLIENT_ARGS, argv,
                IDX_PORT, RDP_DEFAULT_PORT);

    guac_user_log(user, GUAC_LOG_DEBUG,
            "User resolution is %ix%i at %i DPI",
            user->info.optimal_width,
            user->info.optimal_height,
            user->info.optimal_resolution);

    /* Use suggested resolution unless overridden */
    settings->resolution =
        guac_user_parse_args_int(user, GUAC_RDP_CLIENT_ARGS, argv,
                IDX_DPI, guac_rdp_suggest_resolution(user));

    /* Use optimal width unless overridden */
    settings->width = user->info.optimal_width
                    * settings->resolution
                    / user->info.optimal_resolution;

    if (argv[IDX_WIDTH][0] != '\0')
        settings->width = atoi(argv[IDX_WIDTH]);

    /* Use default width if given width is invalid. */
    if (settings->width <= 0) {
        settings->width = RDP_DEFAULT_WIDTH;
        guac_user_log(user, GUAC_LOG_ERROR,
                "Invalid width: \"%s\". Using default of %i.",
                argv[IDX_WIDTH], settings->width);
    }

    /* Round width down to nearest multiple of 4 */
    settings->width = settings->width & ~0x3;

    /* Use optimal height unless overridden */
    settings->height = user->info.optimal_height
                     * settings->resolution
                     / user->info.optimal_resolution;

    if (argv[IDX_HEIGHT][0] != '\0')
        settings->height = atoi(argv[IDX_HEIGHT]);

    /* Use default height if given height is invalid. */
    if (settings->height <= 0) {
        settings->height = RDP_DEFAULT_HEIGHT;
        guac_user_log(user, GUAC_LOG_ERROR,
                "Invalid height: \"%s\". Using default of %i.",
                argv[IDX_WIDTH], settings->height);
    }

    guac_user_log(user, GUAC_LOG_DEBUG,
            "Using resolution of %ix%i at %i DPI",
            settings->width,
            settings->height,
            settings->resolution);

    /* Domain */
    settings->domain =
        guac_user_parse_args_string(user, GUAC_RDP_CLIENT_ARGS, argv,
                IDX_DOMAIN, NULL);

    /* Username */
    settings->username =
        guac_user_parse_args_string(user, GUAC_RDP_CLIENT_ARGS, argv,
                IDX_USERNAME, NULL);

    /* Password */
    settings->password =
        guac_user_parse_args_string(user, GUAC_RDP_CLIENT_ARGS, argv,
                IDX_PASSWORD, NULL);

    /* Read-only mode */
    settings->read_only =
        guac_user_parse_args_boolean(user, GUAC_RDP_CLIENT_ARGS, argv,
                IDX_READ_ONLY, 0);

    /* Client name */
    settings->client_name =
        guac_user_parse_args_string(user, GUAC_RDP_CLIENT_ARGS, argv,
                IDX_CLIENT_NAME, NULL);

    /* Initial program */
    settings->initial_program =
        guac_user_parse_args_string(user, GUAC_RDP_CLIENT_ARGS, argv,
                IDX_INITIAL_PROGRAM, NULL);

    /* RemoteApp program */
    settings->remote_app =
        guac_user_parse_args_string(user, GUAC_RDP_CLIENT_ARGS, argv,
                IDX_REMOTE_APP, NULL);

    /* RemoteApp working directory */
    settings->remote_app_dir =
        guac_user_parse_args_string(user, GUAC_RDP_CLIENT_ARGS, argv,
                IDX_REMOTE_APP_DIR, NULL);

    /* RemoteApp arguments */
    settings->remote_app_args =
        guac_user_parse_args_string(user, GUAC_RDP_CLIENT_ARGS, argv,
                IDX_REMOTE_APP_ARGS, NULL);

    /* Static virtual channels */
    settings->svc_names = NULL;
    if (argv[IDX_STATIC_CHANNELS][0] != '\0')
        settings->svc_names = guac_split(argv[IDX_STATIC_CHANNELS], ',');

    /*
     * Performance flags
     */

    settings->wallpaper_enabled =
        guac_user_parse_args_boolean(user, GUAC_RDP_CLIENT_ARGS, argv,
                IDX_ENABLE_WALLPAPER, 0);

    settings->theming_enabled =
        guac_user_parse_args_boolean(user, GUAC_RDP_CLIENT_ARGS, argv,
                IDX_ENABLE_THEMING, 0);

    settings->font_smoothing_enabled =
        guac_user_parse_args_boolean(user, GUAC_RDP_CLIENT_ARGS, argv,
                IDX_ENABLE_FONT_SMOOTHING, 0);

    settings->full_window_drag_enabled =
        guac_user_parse_args_boolean(user, GUAC_RDP_CLIENT_ARGS, argv,
                IDX_ENABLE_FULL_WINDOW_DRAG, 0);

    settings->desktop_composition_enabled =
        guac_user_parse_args_boolean(user, GUAC_RDP_CLIENT_ARGS, argv,
                IDX_ENABLE_DESKTOP_COMPOSITION, 0);

    settings->menu_animations_enabled =
        guac_user_parse_args_boolean(user, GUAC_RDP_CLIENT_ARGS, argv,
                IDX_ENABLE_MENU_ANIMATIONS, 0);

    /* Session color depth */
    settings->color_depth = 
        guac_user_parse_args_int(user, GUAC_RDP_CLIENT_ARGS, argv,
                IDX_COLOR_DEPTH, RDP_DEFAULT_DEPTH);

    /* Preconnection ID */
    settings->preconnection_id = -1;
    if (argv[IDX_PRECONNECTION_ID][0] != '\0') {

        /* Parse preconnection ID, warn if invalid */
        int preconnection_id = atoi(argv[IDX_PRECONNECTION_ID]);
        if (preconnection_id < 0)
            guac_user_log(user, GUAC_LOG_WARNING,
                    "Ignoring invalid preconnection ID: %i",
                    preconnection_id);

        /* Otherwise, assign specified ID */
        else {
            settings->preconnection_id = preconnection_id;
            guac_user_log(user, GUAC_LOG_DEBUG,
                    "Preconnection ID: %i", settings->preconnection_id);
        }

    }

    /* Preconnection BLOB */
    settings->preconnection_blob = NULL;
    if (argv[IDX_PRECONNECTION_BLOB][0] != '\0') {
        settings->preconnection_blob = strdup(argv[IDX_PRECONNECTION_BLOB]);
        guac_user_log(user, GUAC_LOG_DEBUG,
                "Preconnection BLOB: \"%s\"", settings->preconnection_blob);
    }

#ifndef HAVE_RDPSETTINGS_SENDPRECONNECTIONPDU
    /* Warn if support for the preconnection BLOB / ID is absent */
    if (settings->preconnection_blob != NULL
            || settings->preconnection_id != -1) {
        guac_user_log(user, GUAC_LOG_WARNING,
                "Installed version of FreeRDP lacks support for the "
                "preconnection PDU. The specified preconnection BLOB and/or "
                "ID will be ignored.");
    }
#endif

    /* Audio enable/disable */
    settings->audio_enabled =
        !guac_user_parse_args_boolean(user, GUAC_RDP_CLIENT_ARGS, argv,
                IDX_DISABLE_AUDIO, 0);

    /* Printing enable/disable */
    settings->printing_enabled =
        guac_user_parse_args_boolean(user, GUAC_RDP_CLIENT_ARGS, argv,
                IDX_ENABLE_PRINTING, 0);

    /* Drive enable/disable */
    settings->drive_enabled =
        guac_user_parse_args_boolean(user, GUAC_RDP_CLIENT_ARGS, argv,
                IDX_ENABLE_DRIVE, 0);

    settings->drive_path =
        guac_user_parse_args_string(user, GUAC_RDP_CLIENT_ARGS, argv,
                IDX_DRIVE_PATH, "");

    settings->create_drive_path =
        guac_user_parse_args_boolean(user, GUAC_RDP_CLIENT_ARGS, argv,
                IDX_CREATE_DRIVE_PATH, 0);

    /* Pick keymap based on argument */
    settings->server_layout = NULL;
    if (argv[IDX_SERVER_LAYOUT][0] != '\0')
        settings->server_layout =
            guac_rdp_keymap_find(argv[IDX_SERVER_LAYOUT]);

    /* If no keymap requested, use default */
    if (settings->server_layout == NULL)
        settings->server_layout = guac_rdp_keymap_find(GUAC_DEFAULT_KEYMAP);

#ifdef ENABLE_COMMON_SSH
    /* SFTP enable/disable */
    settings->enable_sftp =
        guac_user_parse_args_boolean(user, GUAC_RDP_CLIENT_ARGS, argv,
                IDX_ENABLE_SFTP, 0);

    /* Hostname for SFTP connection */
    settings->sftp_hostname =
        guac_user_parse_args_string(user, GUAC_RDP_CLIENT_ARGS, argv,
                IDX_SFTP_HOSTNAME, settings->hostname);

    /* Port for SFTP connection */
    settings->sftp_port =
        guac_user_parse_args_string(user, GUAC_RDP_CLIENT_ARGS, argv,
                IDX_SFTP_PORT, "22");

    /* Username for SSH/SFTP authentication */
    settings->sftp_username =
        guac_user_parse_args_string(user, GUAC_RDP_CLIENT_ARGS, argv,
                IDX_SFTP_USERNAME,
                settings->username != NULL ? settings->username : "");

    /* Password for SFTP (if not using private key) */
    settings->sftp_password =
        guac_user_parse_args_string(user, GUAC_RDP_CLIENT_ARGS, argv,
                IDX_SFTP_PASSWORD, "");

    /* Private key for SFTP (if not using password) */
    settings->sftp_private_key =
        guac_user_parse_args_string(user, GUAC_RDP_CLIENT_ARGS, argv,
                IDX_SFTP_PRIVATE_KEY, NULL);

    /* Passphrase for decrypting the SFTP private key (if applicable */
    settings->sftp_passphrase =
        guac_user_parse_args_string(user, GUAC_RDP_CLIENT_ARGS, argv,
                IDX_SFTP_PASSPHRASE, "");

    /* Default upload directory */
    settings->sftp_directory =
        guac_user_parse_args_string(user, GUAC_RDP_CLIENT_ARGS, argv,
                IDX_SFTP_DIRECTORY, NULL);

    /* SFTP root directory */
    settings->sftp_root_directory =
        guac_user_parse_args_string(user, GUAC_RDP_CLIENT_ARGS, argv,
                IDX_SFTP_ROOT_DIRECTORY, "/");

    /* Default keepalive value */
    settings->sftp_server_alive_interval =
        guac_user_parse_args_int(user, GUAC_RDP_CLIENT_ARGS, argv,
                IDX_SFTP_SERVER_ALIVE_INTERVAL, 0);
#endif

    /* Read recording path */
    settings->recording_path =
        guac_user_parse_args_string(user, GUAC_RDP_CLIENT_ARGS, argv,
                IDX_RECORDING_PATH, NULL);

    /* Read recording name */
    settings->recording_name =
        guac_user_parse_args_string(user, GUAC_RDP_CLIENT_ARGS, argv,
                IDX_RECORDING_NAME, GUAC_RDP_DEFAULT_RECORDING_NAME);

    /* Parse path creation flag */
    settings->create_recording_path =
        guac_user_parse_args_boolean(user, GUAC_RDP_CLIENT_ARGS, argv,
                IDX_CREATE_RECORDING_PATH, 0);

    /* No resize method */
    if (strcmp(argv[IDX_RESIZE_METHOD], "") == 0) {
        guac_user_log(user, GUAC_LOG_INFO, "Resize method: none");
        settings->resize_method = GUAC_RESIZE_NONE;
    }

    /* Resize method: "reconnect" */
    else if (strcmp(argv[IDX_RESIZE_METHOD], "reconnect") == 0) {
        guac_user_log(user, GUAC_LOG_INFO, "Resize method: reconnect");
        settings->resize_method = GUAC_RESIZE_RECONNECT;
    }

    /* Resize method: "display-update" */
    else if (strcmp(argv[IDX_RESIZE_METHOD], "display-update") == 0) {
        guac_user_log(user, GUAC_LOG_INFO, "Resize method: display-update");
        settings->resize_method = GUAC_RESIZE_DISPLAY_UPDATE;
    }

    /* Default to no resize method if invalid */
    else {
        guac_user_log(user, GUAC_LOG_INFO, "Resize method \"%s\" invalid. ",
                "Defaulting to no resize method.", argv[IDX_RESIZE_METHOD]);
        settings->resize_method = GUAC_RESIZE_NONE;
    }

    /* Audio input enable/disable */
    settings->enable_audio_input =
        guac_user_parse_args_boolean(user, GUAC_RDP_CLIENT_ARGS, argv,
                IDX_ENABLE_AUDIO_INPUT, 0);

#ifdef HAVE_FREERDP_GATEWAY_SUPPORT
    /* Set gateway hostname */
    settings->gateway_hostname =
        guac_user_parse_args_string(user, GUAC_RDP_CLIENT_ARGS, argv,
                IDX_GATEWAY_HOSTNAME, NULL);

    /* If gateway port specified, use it */
    settings->gateway_port =
        guac_user_parse_args_int(user, GUAC_RDP_CLIENT_ARGS, argv,
                IDX_GATEWAY_PORT, 443);

    /* Set gateway domain */
    settings->gateway_domain =
        guac_user_parse_args_string(user, GUAC_RDP_CLIENT_ARGS, argv,
                IDX_GATEWAY_DOMAIN, NULL);

    /* Set gateway username */
    settings->gateway_username =
        guac_user_parse_args_string(user, GUAC_RDP_CLIENT_ARGS, argv,
                IDX_GATEWAY_USERNAME, NULL);

    /* Set gateway password */
    settings->gateway_password =
        guac_user_parse_args_string(user, GUAC_RDP_CLIENT_ARGS, argv,
                IDX_GATEWAY_PASSWORD, NULL);
#endif

#ifdef HAVE_FREERDP_LOAD_BALANCER_SUPPORT
    /* Set load balance info */
    settings->load_balance_info =
        guac_user_parse_args_string(user, GUAC_RDP_CLIENT_ARGS, argv,
                IDX_LOAD_BALANCE_INFO, NULL);
#endif

    /* Success */
    return settings;

}