/**
 * xrdp: A Remote Desktop Protocol server.
 *
 * Copyright (C) Jay Sorg 2004-2014
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * main login window and login help window
 */

#if defined(HAVE_CONFIG_H)
#include <config_ac.h>
#endif

#include "base64.h"
#include "xrdp.h"
#include "log.h"
#include "string_calls.h"

#define ASK "ask"
#define ASK_LEN g_strlen(ASK)
#define BASE64PREFIX "{base64}"
#define BASE64PREFIX_LEN g_strlen(BASE64PREFIX)

/*****************************************************************************/
/* all login help screen events go here */
static int
xrdp_wm_login_help_notify(struct xrdp_bitmap *wnd,
                          struct xrdp_bitmap *sender,
                          int msg, long param1, long param2)
{
    struct xrdp_painter *p;

    if (wnd == 0)
    {
        return 0;
    }

    if (sender == 0)
    {
        return 0;
    }

    if (wnd->owner == 0)
    {
        return 0;
    }

    if (msg == 1) /* click */
    {
        if (sender->id == 1) /* ok button */
        {
            if (sender->owner->notify != 0)
            {
                wnd->owner->notify(wnd->owner, wnd, 100, 1, 0); /* ok */
            }
        }
    }
    else if (msg == WM_PAINT) /* 3 */
    {
        p = (struct xrdp_painter *)param1;

        if (p != 0)
        {
            p->fg_color = wnd->wm->black;
            xrdp_painter_draw_text(p, wnd, 10, 30, "You must be authenticated \
before using this");
            xrdp_painter_draw_text(p, wnd, 10, 46, "session.");
            xrdp_painter_draw_text(p, wnd, 10, 78, "Enter a valid username in \
the username edit box.");
            xrdp_painter_draw_text(p, wnd, 10, 94, "Enter the password in \
the password edit box.");
            xrdp_painter_draw_text(p, wnd, 10, 110, "Both the username and \
password are case");
            xrdp_painter_draw_text(p, wnd, 10, 126, "sensitive.");
            xrdp_painter_draw_text(p, wnd, 10, 158, "Contact your system \
administrator if you are");
            xrdp_painter_draw_text(p, wnd, 10, 174, "having problems \
logging on.");
        }
    }

    return 0;
}

#if 0
/*****************************************************************************/
static int
xrdp_wm_popup_notify(struct xrdp_bitmap *wnd,
                     struct xrdp_bitmap *sender,
                     int msg, int param1, int param2)
{
    return 0;
}
#endif

/*****************************************************************************/
int
xrdp_wm_delete_all_children(struct xrdp_wm *self)
{
    int index;
    struct xrdp_bitmap *b;
    struct xrdp_rect rect;

    for (index = self->screen->child_list->count - 1; index >= 0; index--)
    {
        b = (struct xrdp_bitmap *)list_get_item(self->screen->child_list, index);
        MAKERECT(rect, b->left, b->top, b->width, b->height);
        xrdp_bitmap_delete(b);
        xrdp_bitmap_invalidate(self->screen, &rect);
    }

    return 0;
}

/*****************************************************************************/
static int
set_mod_data_item(struct xrdp_mod_data *mod, char *name, char *value)
{
    int index;

    for (index = 0; index < mod->names->count; index++)
    {
        if (g_strncmp(name, (char *)list_get_item(mod->names, index), 255) == 0)
        {
            list_remove_item(mod->values, index);
            list_insert_item(mod->values, index, (long)g_strdup(value));
        }
    }

    return 0;
}

/*****************************************************************************/
static int
xrdp_wm_help_clicked(struct xrdp_bitmap *wnd)
{
    struct xrdp_bitmap *help;
    struct xrdp_bitmap *but;

    /* create help screen */
    help = xrdp_bitmap_create(DEFAULT_WND_HELP_W, DEFAULT_WND_HELP_H, wnd->wm->screen->bpp,
                              WND_TYPE_WND, wnd->wm);
    list_insert_item(wnd->wm->screen->child_list, 0, (long)help);
    help->parent = wnd->wm->screen;
    help->owner = wnd;
    wnd->modal_dialog = help;
    help->bg_color = wnd->wm->grey;
    help->left = wnd->wm->screen->width / 2 - help->width / 2;
    help->top = wnd->wm->screen->height / 2 - help->height / 2;
    help->notify = xrdp_wm_login_help_notify;
    set_string(&help->caption1, "Login help");
    /* ok button */
    but = xrdp_bitmap_create(DEFAULT_BUTTON_W, DEFAULT_BUTTON_H, wnd->wm->screen->bpp,
                             WND_TYPE_BUTTON, wnd->wm);
    list_insert_item(help->child_list, 0, (long)but);
    but->parent = help;
    but->owner = help;
    but->left = ((DEFAULT_WND_HELP_W / 2) - (DEFAULT_BUTTON_W / 2)); /* center */
    but->top = DEFAULT_WND_HELP_H - DEFAULT_BUTTON_H - 15;
    but->id = 1;
    but->tab_stop = 1;
    set_string(&but->caption1, "OK");
    /* draw it */
    help->focused_control = but;
    help->default_button = but;
    help->esc_button = but;
    xrdp_bitmap_invalidate(help, 0);
    xrdp_wm_set_focused(wnd->wm, help);
    return 0;
}

/*****************************************************************************/
static int
xrdp_wm_cancel_clicked(struct xrdp_bitmap *wnd)
{
    if (wnd != 0)
    {
        if (wnd->wm != 0)
        {
            if (wnd->wm->pro_layer != 0)
            {
                g_set_wait_obj(wnd->wm->pro_layer->self_term_event);
            }
        }
    }

    return 0;
}

/*****************************************************************************/
static int
xrdp_wm_ok_clicked(struct xrdp_bitmap *wnd)
{
    struct xrdp_bitmap *combo;
    struct xrdp_bitmap *label;
    struct xrdp_bitmap *edit;
    struct xrdp_wm *wm;
    struct xrdp_mod_data *mod_data;
    int i;

    wm = wnd->wm;
    combo = xrdp_bitmap_get_child_by_id(wnd, 6);

    if (combo != 0)
    {
        mod_data = (struct xrdp_mod_data *)
                   list_get_item(combo->data_list, combo->item_index);

        if (mod_data != 0)
        {
            /* get the user typed values */
            i = 100;
            label = xrdp_bitmap_get_child_by_id(wnd, i);
            edit = xrdp_bitmap_get_child_by_id(wnd, i + 1);

            while (label != 0 && edit != 0)
            {
                set_mod_data_item(mod_data, label->caption1, edit->caption1);
                i += 2;
                label = xrdp_bitmap_get_child_by_id(wnd, i);
                edit = xrdp_bitmap_get_child_by_id(wnd, i + 1);
            }

            list_delete(wm->mm->login_names);
            list_delete(wm->mm->login_values);
            wm->mm->login_names = list_create();
            wm->mm->login_names->auto_free = 1;
            wm->mm->login_values = list_create();
            wm->mm->login_values->auto_free = 1;
            /* will copy these cause dialog gets freed */
            list_append_list_strdup(mod_data->names, wm->mm->login_names, 0);
            list_append_list_strdup(mod_data->values, wm->mm->login_values, 0);
            xrdp_wm_set_login_state(wm, WMLS_START_CONNECT);
        }
    }
    else
    {
        LOG(LOG_LEVEL_WARNING, "Combo is NULL - potential programming error");
    }

    return 0;
}

/*****************************************************************************/
/**
* This is an internal function in this file used to parse the domain
* information sent from the client. If the information starts
* with '_' the domain field contains the IP/DNS to connect to.
* If the domain field contains an additional '__' the char that
* follows this '__' is an index number of a preferred combo choice.
* Valid values for this choice is 0-9. But this function will only return
* index numbers between 0 and the max number of combo items -1.
* Example: _192.168.1.2__1 result in a resultbuffer containing
* 192.168.1.2  and the return value will be 1. Meaning that
* index 1 is the preferred combo choice.
*
* Users can create shortcuts where this information is configured. These
* shortcuts simplifies login.
* @param originalDomainInfo indata to this function
* @param comboMax the max number of combo choices
* @param decode if true then we perform decoding of combo choice
* @param resultBuffer must be pre allocated before calling this function.
* Holds the IP. The size of this buffer must be 256 bytes
* @return the index number of the combobox that the user prefer.
* 0 if the user does not prefer any choice.
*/
static int
xrdp_wm_parse_domain_information(char *originalDomainInfo, int comboMax,
                                 int decode, char *resultBuffer)
{
    int ret;
    int pos;
    int comboxindex;
    char index[2];

    /* If the first char in the domain name is '_' we use the domain
       name as IP*/
    ret = 0; /* default return value */
    /* resultBuffer assumed to be 256 chars */
    g_memset(resultBuffer, 0, 256);
    if (originalDomainInfo[0] == '_')
    {
        /* we try to locate a number indicating what combobox index the user
         * prefer the information is loaded from domain field, from the client
         * We must use valid chars in the domain name.
         * Underscore is a valid name in the domain.
         * Invalid chars are ignored in microsoft client therefore we use '_'
         * again. this sec '__' contains the split for index.*/
        pos = g_pos(&originalDomainInfo[1], "__");
        if (pos > 0)
        {
            /* an index is found we try to use it */
            LOG(LOG_LEVEL_DEBUG, "domain contains index char __");
            if (decode)
            {
                g_memset(index, 0, 2);
                /* we just accept values 0-9  (one figure) */
                g_strncpy(index, &originalDomainInfo[pos + 3], 1);
                comboxindex = g_htoi(index);
                LOG(LOG_LEVEL_DEBUG,
                    "index value as string: %s, as int: %d, max: %d",
                    index, comboxindex, comboMax - 1);
                /* limit to max number of items in combo box */
                if ((comboxindex > 0) && (comboxindex < comboMax))
                {
                    LOG(LOG_LEVEL_DEBUG, "domain contains a valid "
                        "index number");
                    ret = comboxindex; /* preferred index for combo box. */
                }
            }
            /* pos limit the String to only contain the IP */
            g_strncpy(resultBuffer, &originalDomainInfo[1], pos);
        }
        else
        {
            LOG(LOG_LEVEL_DEBUG, "domain does not contain _");
            g_strncpy(resultBuffer, &originalDomainInfo[1], 255);
        }
    }
    return ret;
}

/******************************************************************************/
static int
xrdp_wm_show_edits(struct xrdp_wm *self, struct xrdp_bitmap *combo)
{
    int count;
    int index;
    int insert_index;
    int username_set;
    char *name;
    char *value;
    struct xrdp_mod_data *mod;
    struct xrdp_bitmap *b;
    struct xrdp_cfg_globals *globals;
    char resultIP[256];
    char *plain; /* base64 decoded string */
    size_t plain_length; /* length of decoded base64 string */
    size_t base64_length; /* length of base64 string */

    globals = &self->xrdp_config->cfg_globals;

    username_set = 0;

    /* free labels and edits, cause we will create them */
    /* creation or combo changed */
    for (index = 100; index < 200; index++)
    {
        b = xrdp_bitmap_get_child_by_id(combo->parent, index);
        xrdp_bitmap_delete(b);
    }

    insert_index = list_index_of(self->login_window->child_list,
                                 (long)combo); /* find combo in the list */
    insert_index++;
    mod = (struct xrdp_mod_data *)
          list_get_item(combo->data_list, combo->item_index);

    if (mod != 0)
    {
        count = 0;

        for (index = 0; index < mod->names->count; index++)
        {
            value = (char *)list_get_item(mod->values, index);

            /* if the value begins with "{base64}", decode the string following it */
            if (g_strncmp(BASE64PREFIX, value, BASE64PREFIX_LEN) == 0)
            {
                base64_length = g_strlen(value + BASE64PREFIX_LEN);
                plain = (char *)g_malloc(base64_length, 0);
                base64_decode(value + BASE64PREFIX_LEN,
                              plain, base64_length, &plain_length);
                g_strncpy(value, plain, plain_length);
                g_free(plain);
            }
            else if (g_strncmp(ASK, value, ASK_LEN) == 0)
            {
                /* label */
                b = xrdp_bitmap_create(globals->ls_label_width, DEFAULT_EDIT_H, self->screen->bpp,
                                       WND_TYPE_LABEL, self);
                list_insert_item(self->login_window->child_list, insert_index,
                                 (long)b);
                insert_index++;
                b->parent = self->login_window;
                b->owner = self->login_window;
                b->left = globals->ls_label_x_pos;

                b->top = globals->ls_input_y_pos + DEFAULT_COMBO_H + 5 + (DEFAULT_EDIT_H + 5) * count;
                b->id = 100 + 2 * count;
                name = (char *)list_get_item(mod->names, index);
                set_string(&b->caption1, name);

                /* edit */
                b = xrdp_bitmap_create(globals->ls_input_width, DEFAULT_EDIT_H, self->screen->bpp,
                                       WND_TYPE_EDIT, self);
                list_insert_item(self->login_window->child_list, insert_index,
                                 (long)b);
                insert_index++;
                b->parent = self->login_window;
                b->owner = self->login_window;
                b->left = globals->ls_input_x_pos;

                b->top = globals->ls_input_y_pos + DEFAULT_COMBO_H + 5 + (DEFAULT_EDIT_H + 5) * count;

                b->id = 100 + 2 * count + 1;
                b->pointer = 1;
                b->tab_stop = 1;
                b->caption1 = (char *)g_malloc(256, 1);
                /* ask{base64}... 3 for "ask", 8 for "{base64}" */
                if (g_strncmp(BASE64PREFIX, value + ASK_LEN, BASE64PREFIX_LEN) == 0)
                {
                    base64_length = g_strlen(value + ASK_LEN + BASE64PREFIX_LEN);
                    plain = (char *)g_malloc(base64_length, 0);
                    base64_decode(value + ASK_LEN + BASE64PREFIX_LEN,
                                  plain, base64_length, &plain_length);
                    plain[plain_length] = '\0';
                    g_strncpy(b->caption1, plain, 255);
                    g_free(plain);
                }
                else
                {
                    g_strncpy(b->caption1, value + ASK_LEN, 255);
                }
                b->edit_pos = g_mbstowcs(0, b->caption1, 0);

                if (self->login_window->focused_control == 0)
                {
                    self->login_window->focused_control = b;
                }

                /* Use the domain name as the destination IP/DNS
                   This is useful in a gateway setup. */
                if (g_strncasecmp(name, "ip", 255) == 0)
                {
                    /* If the first char in the domain name is '_' we use the
                       domain name as IP */
                    if (self->session->client_info->domain[0] == '_')
                    {
                        xrdp_wm_parse_domain_information(
                            self->session->client_info->domain,
                            combo->data_list->count, 0, resultIP);
                        g_strncpy(b->caption1, resultIP, 255);
                        b->edit_pos = g_mbstowcs(0, b->caption1, 0);
                    }

                }

                if (g_strncasecmp(name, "username", 255) == 0 &&
                        self->session->client_info->username[0])
                {
                    g_strncpy(b->caption1, self->session->client_info->username, 255);
                    b->edit_pos = g_mbstowcs(0, b->caption1, 0);

                    if (b->edit_pos > 0)
                    {
                        username_set = 1;
                    }
                }

                if ((g_strncasecmp(name, "password", 255) == 0) ||
                        (g_strncasecmp(name, "pampassword", 255) == 0))
                {
                    b->password_char = '*';

                    if (username_set)
                    {
                        if (b->parent != 0)
                        {
                            b->parent->focused_control = b;
                        }
                    }
                }

                count++;
            }
        }
    }

    return 0;
}

/*****************************************************************************/
/* all login screen events go here */
static int
xrdp_wm_login_notify(struct xrdp_bitmap *wnd,
                     struct xrdp_bitmap *sender,
                     int msg, long param1, long param2)
{
    struct xrdp_bitmap *b;
    struct xrdp_rect rect;
    int i;

    if (wnd->modal_dialog != 0 && msg != 100)
    {
        return 0;
    }

    if (msg == 1) /* click */
    {
        if (sender->id == 1) /* help button */
        {
            xrdp_wm_help_clicked(wnd);
        }
        else if (sender->id == 2) /* cancel button */
        {
            xrdp_wm_cancel_clicked(wnd);
        }
        else if (sender->id == 3) /* ok button */
        {
            xrdp_wm_ok_clicked(wnd);
        }
    }
    else if (msg == 2) /* mouse move */
    {
    }
    else if (msg == 100) /* modal result is done */
    {
        i = list_index_of(wnd->wm->screen->child_list, (long)sender);

        if (i >= 0)
        {
            b = (struct xrdp_bitmap *)
                list_get_item(wnd->wm->screen->child_list, i);
            list_remove_item(sender->wm->screen->child_list, i);
            MAKERECT(rect, b->left, b->top, b->width, b->height);
            xrdp_bitmap_invalidate(wnd->wm->screen, &rect);
            xrdp_bitmap_delete(sender);
            wnd->modal_dialog = 0;
            xrdp_wm_set_focused(wnd->wm, wnd);
        }
    }
    else if (msg == CB_ITEMCHANGE) /* combo box change */
    {
        xrdp_wm_show_edits(wnd->wm, sender);
        xrdp_bitmap_invalidate(wnd, 0); /* invalidate the whole dialog for now */
    }

    return 0;
}

/******************************************************************************/
static int
xrdp_wm_login_fill_in_combo(struct xrdp_wm *self, struct xrdp_bitmap *b)
{
    struct list *sections;
    struct list *section_names;
    struct list *section_values;
    int fd;
    int i;
    int j;
    char *p;
    char *q;
    char *r;
    char name[256];
    struct xrdp_mod_data *mod_data;
    const char *xrdp_ini = self->session->xrdp_ini;

    sections = list_create();
    sections->auto_free = 1;
    section_names = list_create();
    section_names->auto_free = 1;
    section_values = list_create();
    section_values->auto_free = 1;
    fd = g_file_open(xrdp_ini);

    if (fd < 0)
    {
        LOG(LOG_LEVEL_ERROR, "Could not read xrdp ini file %s",
            xrdp_ini);
        list_delete(sections);
        list_delete(section_names);
        list_delete(section_values);
        return 1;
    }

    file_read_sections(fd, sections);

    for (i = 0; i < sections->count; i++)
    {
        p = (char *)list_get_item(sections, i);
        file_read_section(fd, p, section_names, section_values);

        if ((g_strncasecmp(p, "globals", 255) == 0)
                || (g_strncasecmp(p, "channels", 255) == 0)
                || (g_strncasecmp(p, "Logging", 255) == 0)
                || (g_strncasecmp(p, "LoggingPerLogger", 255) == 0))
        {
        }
        else
        {
            g_strncpy(name, p, 255);
            mod_data = (struct xrdp_mod_data *)
                       g_malloc(sizeof(struct xrdp_mod_data), 1);
            mod_data->names = list_create();
            mod_data->names->auto_free = 1;
            mod_data->values = list_create();
            mod_data->values->auto_free = 1;

            for (j = 0; j < section_names->count; j++)
            {
                q = (char *)list_get_item(section_names, j);
                r = (char *)list_get_item(section_values, j);

                if (g_strncmp("name", q, 255) == 0)
                {
                    g_strncpy(name, r, 255);
                }

                list_add_item(mod_data->names, (long)g_strdup(q));
                list_add_item(mod_data->values, (long)g_strdup(r));
            }

            list_add_item(b->string_list, (long)g_strdup(name));
            list_add_item(b->data_list, (long)mod_data);
        }
    }

    g_file_close(fd);
    list_delete(sections);
    list_delete(section_names);
    list_delete(section_values);
    return 0;
}

/******************************************************************************/
int
xrdp_login_wnd_create(struct xrdp_wm *self)
{
    struct xrdp_bitmap      *but;
    struct xrdp_bitmap      *combo;
    struct xrdp_cfg_globals *globals;

    char buf[256];
    char buf1[256];
    char resultIP[256];
    int log_width;
    int log_height;
    int regular;
    int primary_width;     /* Dimensions of primary screen */
    int primary_height;
    int primary_x_offset;  /* Offset of centre of primary screen */
    int primary_y_offset;
    int index;
    int x;
    int y;
    int cx;
    int cy;

    globals = &self->xrdp_config->cfg_globals;

    primary_width = self->screen->width;
    primary_height = self->screen->height;
    primary_x_offset = primary_width / 2;
    primary_y_offset = primary_height / 2;

    log_width = globals->ls_width;
    log_height = globals->ls_height;
    regular = 1;

    if (self->screen->width < log_width)
    {
        if (self->screen->width < 240)
        {
            log_width = self->screen->width - 4;
        }
        else
        {
            log_width = 240;
        }

        regular = 0;
    }

    /* multimon scenario, draw login window on primary monitor */
    if (self->client_info->monitorCount > 1)
    {
        for (index = 0; index < self->client_info->monitorCount; index++)
        {
            if (self->client_info->minfo_wm[index].is_primary)
            {
                x = self->client_info->minfo_wm[index].left;
                y = self->client_info->minfo_wm[index].top;
                cx = self->client_info->minfo_wm[index].right;
                cy = self->client_info->minfo_wm[index].bottom;

                primary_width = cx - x;
                primary_height = cy - y;
                primary_x_offset = x + (primary_width / 2);
                primary_y_offset = y + (primary_height / 2);
                break;
            }
        }
    }

    /* draw login window */
    self->login_window = xrdp_bitmap_create(log_width, log_height, self->screen->bpp,
                                            WND_TYPE_WND, self);
    list_add_item(self->screen->child_list, (long)self->login_window);
    self->login_window->parent = self->screen;
    self->login_window->owner = self->screen;
    self->login_window->bg_color = globals->ls_bg_color;

    self->login_window->left = primary_x_offset - self->login_window->width / 2;
    self->login_window->top = primary_y_offset - self->login_window->height / 2;


    self->login_window->notify = xrdp_wm_login_notify;

    /* if window title not specified, use hostname as default */
    if (globals->ls_title[0] == 0)
    {
        g_gethostname(buf1, 256);
        g_snprintf(buf, sizeof(buf), "Login to %s", buf1);
        set_string(&self->login_window->caption1, buf);
    }
    else
    {
        /*self->login_window->caption1 = globals->ls_title[0];*/
        g_snprintf(buf, sizeof(buf), "%s", globals->ls_title);
        set_string(&self->login_window->caption1, buf);
    }

    if (regular)
    {
        /* Load the background image. */
        /* If no file is specified no default image will be loaded. */
        /* We only load the image if bpp > 8, and if the user hasn't
         * disabled wallpaper in the performance settings */
        if (globals->ls_background_image[0] != 0)
        {
            if (self->screen->bpp <= 8)
            {
                LOG(LOG_LEVEL_INFO, "Login background not loaded for bpp=%d",
                    self->screen->bpp);
            }
            else if ((self->client_info->rdp5_performanceflags &
                      RDP5_NO_WALLPAPER) != 0)
            {
                LOG(LOG_LEVEL_INFO, "Login background not loaded as client "
                    "has requested PERF_DISABLE_WALLPAPER");
            }
            else
            {
                char fileName[256] ;
                but = xrdp_bitmap_create(4, 4, self->screen->bpp,
                                         WND_TYPE_IMAGE, self);
                if (globals->ls_background_image[0] == '/')
                {
                    g_snprintf(fileName, 255, "%s",
                               globals->ls_background_image);
                }
                else
                {
                    g_snprintf(fileName, 255, "%s/%s",
                               XRDP_SHARE_PATH, globals->ls_background_image);
                }
                LOG(LOG_LEVEL_DEBUG, "We try to load the following background file: %s", fileName);
                if (globals->ls_background_transform == XBLT_NONE)
                {
                    xrdp_bitmap_load(but, fileName, self->palette,
                                     globals->ls_top_window_bg_color,
                                     globals->ls_background_transform,
                                     0, 0);
                    /* Place the background in the bottom right corner */
                    but->left = primary_x_offset + (primary_width / 2) -
                                but->width;
                    but->top = primary_y_offset + (primary_height / 2) -
                               but->height;
                }
                else
                {
                    xrdp_bitmap_load(but, fileName, self->palette,
                                     globals->ls_top_window_bg_color,
                                     globals->ls_background_transform,
                                     primary_width, primary_height);
                    but->left = primary_x_offset - (primary_width / 2);
                    but->top = primary_y_offset - (primary_height / 2);
                }
                but->parent = self->screen;
                but->owner = self->screen;
                list_add_item(self->screen->child_list, (long)but);
            }
        }

        /* if logo image not specified, use default */
        if (globals->ls_logo_filename[0] == 0)
        {
            g_snprintf(globals->ls_logo_filename, 255, "%s/xrdp_logo.bmp", XRDP_SHARE_PATH);
        }

        /* logo image */
        but = xrdp_bitmap_create(4, 4, self->screen->bpp, WND_TYPE_IMAGE, self);

        if (self->screen->bpp <= 8)
        {
            g_snprintf(globals->ls_logo_filename, 255, "%s/ad256.bmp", XRDP_SHARE_PATH);
        }

        xrdp_bitmap_load(but, globals->ls_logo_filename, self->palette,
                         globals->ls_bg_color,
                         globals->ls_logo_transform,
                         globals->ls_logo_width,
                         globals->ls_logo_height);
        but->parent = self->login_window;
        but->owner = self->login_window;
        but->left = globals->ls_logo_x_pos;
        but->top = globals->ls_logo_y_pos;
        list_add_item(self->login_window->child_list, (long)but);
    }

    /* label */
    but = xrdp_bitmap_create(globals->ls_label_width, DEFAULT_EDIT_H, self->screen->bpp, WND_TYPE_LABEL, self);
    list_add_item(self->login_window->child_list, (long)but);
    but->parent = self->login_window;
    but->owner = self->login_window;
    but->left = globals->ls_label_x_pos;
    but->top = globals->ls_input_y_pos;
    set_string(&but->caption1, "Session");

    /* combo */
    combo = xrdp_bitmap_create(globals->ls_input_width, DEFAULT_COMBO_H,
                               self->screen->bpp, WND_TYPE_COMBO, self);
    list_add_item(self->login_window->child_list, (long)combo);
    combo->parent = self->login_window;
    combo->owner = self->login_window;
    combo->left = globals->ls_input_x_pos;
    combo->top = globals->ls_input_y_pos;
    combo->id = 6;
    combo->tab_stop = 1;
    xrdp_wm_login_fill_in_combo(self, combo);

    /* OK button */
    but = xrdp_bitmap_create(globals->ls_btn_ok_width, globals->ls_btn_ok_height,
                             self->screen->bpp, WND_TYPE_BUTTON, self);
    list_add_item(self->login_window->child_list, (long)but);
    but->parent = self->login_window;
    but->owner = self->login_window;
    but->left = globals->ls_btn_ok_x_pos;
    but->top = globals->ls_btn_ok_y_pos;
    but->id = 3;
    set_string(&but->caption1, "OK");
    but->tab_stop = 1;
    self->login_window->default_button = but;

    /* Cancel button */
    but = xrdp_bitmap_create(globals->ls_btn_cancel_width,
                             globals->ls_btn_cancel_height, self->screen->bpp,
                             WND_TYPE_BUTTON, self);
    list_add_item(self->login_window->child_list, (long)but);
    but->parent = self->login_window;
    but->owner = self->login_window;
    but->left = globals->ls_btn_cancel_x_pos;
    but->top = globals->ls_btn_cancel_y_pos;
    but->id = 2;
    set_string(&but->caption1, "Cancel");
    but->tab_stop = 1;
    self->login_window->esc_button = but;

    /* labels and edits.
    * parameter: 1 = decode domain field index information from client.
    * We only perform this the first time for each connection.
    */
    combo->item_index = xrdp_wm_parse_domain_information(
                            self->session->client_info->domain,
                            combo->data_list->count, 1,
                            resultIP /* just a dummy place holder, we ignore */ );
    xrdp_wm_show_edits(self, combo);

    return 0;
}

/**
 * Map a bitmap transform string to a value
 *
 * @param param Param we're trying to read
 * @param str   String we're trying to map
 *
 * @return enum xrdp_bitmap_load_transform value
 *
 * A warning is logged if the string is not recognised
 *****************************************************************************/
static enum xrdp_bitmap_load_transform
bitmap_transform_str_to_val(const char *param, const char *str)
{
    enum xrdp_bitmap_load_transform rv;
    if (g_strcmp(str, "none") == 0)
    {
        rv = XBLT_NONE;
    }
    else if (g_strcmp(str, "scale") == 0)
    {
        rv = XBLT_SCALE;
    }
    else if (g_strcmp(str, "zoom") == 0)
    {
        rv = XBLT_ZOOM;
    }
    else
    {
        LOG(LOG_LEVEL_WARNING, "Param '%s' has unrecognised value '%s'"
            " - assuming 'none'", param, str);
        rv = XBLT_NONE;
    }

    return rv;
}

/**
 * Load configuration from xrdp.ini file
 *
 * @param config XRDP configuration to initialise
 * @param xrdp_ini Path to xrdp.ini
 * @param bpp bits-per-pixel for this connection
 *
 * @return 0 on success, -1 on failure
 *****************************************************************************/
int
load_xrdp_config(struct xrdp_config *config, const char *xrdp_ini, int bpp)
{
    struct xrdp_cfg_globals  *globals;

    struct list *names;
    struct list *values;

    char *n;
    char *v;
    int   fd;
    int   i;

    if (!config)
    {
        return -1;
    }

    globals = &config->cfg_globals;

    /* set default values in case we can't get them from xrdp.ini file */
    globals->ini_version = 1;
    globals->ls_top_window_bg_color = HCOLOR(bpp, xrdp_wm_htoi("009cb5"));
    globals->ls_bg_color = HCOLOR(bpp, xrdp_wm_htoi("dedede"));
    globals->ls_width = 350;
    globals->ls_height = 350;
    globals->ls_background_transform = XBLT_NONE;
    globals->ls_logo_transform = XBLT_NONE;
    globals->ls_logo_x_pos = 63;
    globals->ls_logo_y_pos = 50;
    globals->ls_label_x_pos = 30;
    globals->ls_label_width = 65;
    globals->ls_input_x_pos = 110;
    globals->ls_input_width = 210;
    globals->ls_input_y_pos = 150;
    globals->ls_btn_ok_x_pos = 150;
    globals->ls_btn_ok_y_pos = 300;
    globals->ls_btn_ok_width = 85;
    globals->ls_btn_ok_height = 30;
    globals->ls_btn_cancel_x_pos = 245;
    globals->ls_btn_cancel_y_pos = 300;
    globals->ls_btn_cancel_width = 85;
    globals->ls_btn_cancel_height = 30;

    /* open xrdp.ini file */
    if ((fd = g_file_open(xrdp_ini)) < 0)
    {
        LOG(LOG_LEVEL_ERROR, "load_config: Could not read "
            "xrdp.ini file %s", xrdp_ini);
        return -1;

    }

    names = list_create();
    values = list_create();
    names->auto_free = 1;
    values->auto_free = 1;

    if (file_read_section(fd, "globals", names, values) != 0)
    {
        list_delete(names);
        list_delete(values);
        g_file_close(fd);
        LOG(LOG_LEVEL_ERROR, "load_config: Could not read globals "
            "section from xrdp.ini file %s", xrdp_ini);
        return -1;
    }

    for (i = 0; i < names->count; i++)
    {
        n = (char *) list_get_item(names, i);
        v = (char *) list_get_item(values, i);

        /*
         * parse globals section
         */

        if (g_strncmp(n, "ini_version", 64) == 0)
        {
            globals->ini_version = g_atoi(v);
        }

        else if (g_strncmp(n, "bitmap_cache", 64) == 0)
        {
            globals->use_bitmap_cache = g_text2bool(v);
        }

        else if (g_strncmp(n, "bitmap_compression", 64) == 0)
        {
            globals->use_bitmap_compression = g_text2bool(v);
        }

        else if (g_strncmp(n, "port", 64) == 0)
        {
            globals->port = g_atoi(v);
        }

        else if (g_strncmp(n, "crypt_level", 64) == 0)
        {
            if (g_strcmp(v, "low") == 0)
            {
                globals->crypt_level = 1;
            }
            else if (g_strcmp(v, "medium") == 0)
            {
                globals->crypt_level = 2;
            }
            else
            {
                globals->crypt_level = 3;
            }
        }

        else if (g_strncmp(n, "allow_channels", 64) == 0)
        {
            globals->allow_channels = g_text2bool(v);
        }

        else if (g_strncmp(n, "max_bpp", 64) == 0)
        {
            globals->max_bpp = g_atoi(v);
        }

        else if (g_strncmp(n, "fork", 64) == 0)
        {
            globals->fork = g_text2bool(v);
        }

        else if (g_strncmp(n, "tcp_nodelay", 64) == 0)
        {
            globals->tcp_nodelay = g_text2bool(v);
        }

        else if (g_strncmp(n, "tcp_keepalive", 64) == 0)
        {
            globals->tcp_keepalive = g_text2bool(v);
        }

        else if (g_strncmp(n, "tcp_send_buffer_bytes", 64) == 0)
        {
            globals->tcp_send_buffer_bytes = g_atoi(v);
        }

        else if (g_strncmp(n, "tcp_recv_buffer_bytes", 64) == 0)
        {
            globals->tcp_recv_buffer_bytes = g_atoi(v);
        }

        /* colors */

        else if (g_strncmp(n, "grey", 64) == 0)
        {
            globals->grey = xrdp_wm_htoi(v);
        }

        else if (g_strncmp(n, "black", 64) == 0)
        {
            globals->black = xrdp_wm_htoi(v);
        }

        else if (g_strncmp(n, "dark_grey", 64) == 0)
        {
            globals->dark_grey = xrdp_wm_htoi(v);
        }

        else if (g_strncmp(n, "blue", 64) == 0)
        {
            globals->blue = xrdp_wm_htoi(v);
        }

        else if (g_strncmp(n, "dark_blue", 64) == 0)
        {
            globals->dark_blue = xrdp_wm_htoi(v);
        }

        else if (g_strncmp(n, "white", 64) == 0)
        {
            globals->white = xrdp_wm_htoi(v);
        }

        else if (g_strncmp(n, "red", 64) == 0)
        {
            globals->red = xrdp_wm_htoi(v);
        }

        else if (g_strncmp(n, "green", 64) == 0)
        {
            globals->green = xrdp_wm_htoi(v);
        }

        else if (g_strncmp(n, "background", 64) == 0)
        {
            globals->background = xrdp_wm_htoi(v);
        }

        /* misc stuff */

        else if (g_strncmp(n, "autorun", 255) == 0)
        {
            g_strncpy(globals->autorun, v, 255);
        }

        else if (g_strncmp(n, "hidelogwindow", 64) == 0)
        {
            globals->hidelogwindow = g_text2bool(v);
        }

        else if (g_strncmp(n, "require_credentials", 64) == 0)
        {
            globals->require_credentials = g_text2bool(v);
        }

        else if (g_strncmp(n, "bulk_compression", 64) == 0)
        {
            globals->bulk_compression = g_text2bool(v);
        }

        else if (g_strncmp(n, "new_cursors", 64) == 0)
        {
            globals->new_cursors = g_text2bool(v);
        }

        else if (g_strncmp(n, "nego_sec_layer", 64) == 0)
        {
            globals->nego_sec_layer = g_atoi(v);
        }

        else if (g_strncmp(n, "allow_multimon", 64) == 0)
        {
            globals->allow_multimon = g_text2bool(v);
        }

        else if (g_strncmp(n, "enable_token_login", 64) == 0)
        {
            LOG(LOG_LEVEL_DEBUG, "Token login detection enabled x");
            globals->enable_token_login = g_text2bool(v);
        }

        /* login screen values */
        else if (g_strncmp(n, "ls_top_window_bg_color", 64) == 0)
        {
            globals->ls_top_window_bg_color = HCOLOR(bpp, xrdp_wm_htoi(v));
        }

        else if (g_strncmp(n, "ls_width", 64) == 0)
        {
            globals->ls_width = g_atoi(v);
        }

        else if (g_strncmp(n, "ls_height", 64) == 0)
        {
            globals->ls_height = g_atoi(v);
        }

        else if (g_strncmp(n, "ls_bg_color", 64) == 0)
        {
            globals->ls_bg_color = HCOLOR(bpp, xrdp_wm_htoi(v));
        }

        else if (g_strncmp(n, "ls_title", 255) == 0)
        {
            g_strncpy(globals->ls_title, v, 255);
            globals->ls_title[255] = 0;
        }

        else if (g_strncmp(n, "ls_background_image", 255) == 0)
        {
            g_strncpy(globals->ls_background_image, v, 255);
            globals->ls_background_image[255] = 0;
        }

        else if (g_strncmp(n, "ls_background_transform", 255) == 0)
        {
            globals->ls_background_transform =
                bitmap_transform_str_to_val(n, v);
        }

        else if (g_strncmp(n, "ls_logo_filename", 255) == 0)
        {
            g_strncpy(globals->ls_logo_filename, v, 255);
            globals->ls_logo_filename[255] = 0;
        }

        else if (g_strncmp(n, "ls_logo_transform", 255) == 0)
        {
            globals->ls_logo_transform = bitmap_transform_str_to_val(n, v);
        }

        else if (g_strncmp(n, "ls_logo_width", 64) == 0)
        {
            globals->ls_logo_width = g_atoi(v);
        }

        else if (g_strncmp(n, "ls_logo_height", 64) == 0)
        {
            globals->ls_logo_height = g_atoi(v);
        }

        else if (g_strncmp(n, "ls_logo_x_pos", 64) == 0)
        {
            globals->ls_logo_x_pos = g_atoi(v);
        }

        else if (g_strncmp(n, "ls_logo_y_pos", 64) == 0)
        {
            globals->ls_logo_y_pos = g_atoi(v);
        }

        else if (g_strncmp(n, "ls_label_x_pos", 64) == 0)
        {
            globals->ls_label_x_pos = g_atoi(v);
        }

        else if (g_strncmp(n, "ls_label_width", 64) == 0)
        {
            globals->ls_label_width = g_atoi(v);
        }

        else if (g_strncmp(n, "ls_input_x_pos", 64) == 0)
        {
            globals->ls_input_x_pos = g_atoi(v);
        }

        else if (g_strncmp(n, "ls_input_width", 64) == 0)
        {
            globals->ls_input_width = g_atoi(v);
        }

        else if (g_strncmp(n, "ls_input_y_pos", 64) == 0)
        {
            globals->ls_input_y_pos = g_atoi(v);
        }

        else if (g_strncmp(n, "ls_btn_ok_x_pos", 64) == 0)
        {
            globals->ls_btn_ok_x_pos = g_atoi(v);
        }

        else if (g_strncmp(n, "ls_btn_ok_y_pos", 64) == 0)
        {
            globals->ls_btn_ok_y_pos = g_atoi(v);
        }

        else if (g_strncmp(n, "ls_btn_ok_width", 64) == 0)
        {
            globals->ls_btn_ok_width = g_atoi(v);
        }

        else if (g_strncmp(n, "ls_btn_ok_height", 64) == 0)
        {
            globals->ls_btn_ok_height = g_atoi(v);
        }

        else if (g_strncmp(n, "ls_btn_cancel_x_pos", 64) == 0)
        {
            globals->ls_btn_cancel_x_pos = g_atoi(v);
        }

        else if (g_strncmp(n, "ls_btn_cancel_y_pos", 64) == 0)
        {
            globals->ls_btn_cancel_y_pos = g_atoi(v);
        }

        else if (g_strncmp(n, "ls_btn_cancel_width", 64) == 0)
        {
            globals->ls_btn_cancel_width = g_atoi(v);
        }

        else if (g_strncmp(n, "ls_btn_cancel_height", 64) == 0)
        {
            globals->ls_btn_cancel_height = g_atoi(v);
        }
    }

    LOG(LOG_LEVEL_DEBUG, "ini_version:             %d", globals->ini_version);
    LOG(LOG_LEVEL_DEBUG, "use_bitmap_cache:        %d", globals->use_bitmap_cache);
    LOG(LOG_LEVEL_DEBUG, "use_bitmap_compression:  %d", globals->use_bitmap_compression);
    LOG(LOG_LEVEL_DEBUG, "port:                    %d", globals->port);
    LOG(LOG_LEVEL_DEBUG, "crypt_level:             %d", globals->crypt_level);
    LOG(LOG_LEVEL_DEBUG, "allow_channels:          %d", globals->allow_channels);
    LOG(LOG_LEVEL_DEBUG, "max_bpp:                 %d", globals->max_bpp);
    LOG(LOG_LEVEL_DEBUG, "fork:                    %d", globals->fork);
    LOG(LOG_LEVEL_DEBUG, "tcp_nodelay:             %d", globals->tcp_nodelay);
    LOG(LOG_LEVEL_DEBUG, "tcp_keepalive:           %d", globals->tcp_keepalive);
    LOG(LOG_LEVEL_DEBUG, "tcp_send_buffer_bytes:   %d", globals->tcp_send_buffer_bytes);
    LOG(LOG_LEVEL_DEBUG, "tcp_recv_buffer_bytes:   %d", globals->tcp_recv_buffer_bytes);
    LOG(LOG_LEVEL_DEBUG, "new_cursors:             %d", globals->new_cursors);
    LOG(LOG_LEVEL_DEBUG, "allow_multimon:          %d", globals->allow_multimon);

    LOG(LOG_LEVEL_DEBUG, "grey:                    %d", globals->grey);
    LOG(LOG_LEVEL_DEBUG, "black:                   %d", globals->black);
    LOG(LOG_LEVEL_DEBUG, "dark_grey:               %d", globals->dark_grey);
    LOG(LOG_LEVEL_DEBUG, "blue:                    %d", globals->blue);
    LOG(LOG_LEVEL_DEBUG, "dark_blue:               %d", globals->dark_blue);
    LOG(LOG_LEVEL_DEBUG, "white:                   %d", globals->white);
    LOG(LOG_LEVEL_DEBUG, "red:                     %d", globals->red);
    LOG(LOG_LEVEL_DEBUG, "green:                   %d", globals->green);
    LOG(LOG_LEVEL_DEBUG, "background:              %d", globals->background);

    LOG(LOG_LEVEL_DEBUG, "autorun:                 %s", globals->autorun);
    LOG(LOG_LEVEL_DEBUG, "hidelogwindow:           %d", globals->hidelogwindow);
    LOG(LOG_LEVEL_DEBUG, "require_credentials:     %d", globals->require_credentials);
    LOG(LOG_LEVEL_DEBUG, "bulk_compression:        %d", globals->bulk_compression);
    LOG(LOG_LEVEL_DEBUG, "new_cursors:             %d", globals->new_cursors);
    LOG(LOG_LEVEL_DEBUG, "nego_sec_layer:          %d", globals->nego_sec_layer);
    LOG(LOG_LEVEL_DEBUG, "allow_multimon:          %d", globals->allow_multimon);
    LOG(LOG_LEVEL_DEBUG, "enable_token_login:      %d", globals->enable_token_login);

    LOG(LOG_LEVEL_DEBUG, "ls_top_window_bg_color:  %x", globals->ls_top_window_bg_color);
    LOG(LOG_LEVEL_DEBUG, "ls_width:                %d", globals->ls_width);
    LOG(LOG_LEVEL_DEBUG, "ls_height:               %d", globals->ls_height);
    LOG(LOG_LEVEL_DEBUG, "ls_bg_color:             %x", globals->ls_bg_color);
    LOG(LOG_LEVEL_DEBUG, "ls_title:                %s", globals->ls_title);
    LOG(LOG_LEVEL_DEBUG, "ls_logo_filename:        %s", globals->ls_logo_filename);
    LOG(LOG_LEVEL_DEBUG, "ls_logo_x_pos:           %d", globals->ls_logo_x_pos);
    LOG(LOG_LEVEL_DEBUG, "ls_logo_y_pos:           %d", globals->ls_logo_y_pos);
    LOG(LOG_LEVEL_DEBUG, "ls_label_x_pos:          %d", globals->ls_label_x_pos);
    LOG(LOG_LEVEL_DEBUG, "ls_label_width:          %d", globals->ls_label_width);
    LOG(LOG_LEVEL_DEBUG, "ls_input_x_pos:          %d", globals->ls_input_x_pos);
    LOG(LOG_LEVEL_DEBUG, "ls_input_width:          %d", globals->ls_input_width);
    LOG(LOG_LEVEL_DEBUG, "ls_input_y_pos:          %d", globals->ls_input_y_pos);
    LOG(LOG_LEVEL_DEBUG, "ls_btn_ok_x_pos:         %d", globals->ls_btn_ok_x_pos);
    LOG(LOG_LEVEL_DEBUG, "ls_btn_ok_y_pos:         %d", globals->ls_btn_ok_y_pos);
    LOG(LOG_LEVEL_DEBUG, "ls_btn_ok_width:         %d", globals->ls_btn_ok_width);
    LOG(LOG_LEVEL_DEBUG, "ls_btn_ok_height:        %d", globals->ls_btn_ok_height);
    LOG(LOG_LEVEL_DEBUG, "ls_btn_cancel_x_pos:     %d", globals->ls_btn_cancel_x_pos);
    LOG(LOG_LEVEL_DEBUG, "ls_btn_cancel_y_pos:     %d", globals->ls_btn_cancel_y_pos);
    LOG(LOG_LEVEL_DEBUG, "ls_btn_cancel_width:     %d", globals->ls_btn_cancel_width);
    LOG(LOG_LEVEL_DEBUG, "ls_btn_cancel_height:    %d", globals->ls_btn_cancel_height);

    list_delete(names);
    list_delete(values);
    g_file_close(fd);
    return 0;
}
