/*
 * config.c © by Martin Steppler
 *
 * $Id: config.c 1.2 1994/04/16 15:58:23 steppler Exp $
 *
 * $Log: config.c $
 * Revision 1.2  1994/04/16  15:58:23  steppler
 * LoadConfig(): better env variable handling
 *
 * Revision 1.1.1.1  1994/04/10  17:24:05  steppler
 * use MUIFFR/Config as environment variable for
 * configuration file
 * extended configuration, new entries:
 * Bit per Sec, Charges per Unit, Time per Unit, Bytes position
 *
 * Revision 1.1  1994/04/10  17:23:10  steppler
 * Initial revision
 *
 *
 */

#include "muiffr.h"
#include "muiffr_locale.h"

#define CONFIGNAME "PROGDIR:muiffr.config"
#define CONFBANNER "$VER: muiffr.config 1.2 (16.04.94) $\nDo not edit by hand!\n"
#define OLDCONFBANNER "MUI Fido File Request Configuration"

static int GetFileSize(UBYTE *filename, int *file_len, int quiet);
static int GetLine(UBYTE *readbuf, int *read_pos, int filesize, UBYTE *dest, int dest_size);

int ConfAliasReady(void)
{
    int return_ok = FALSE;
    struct NodeList *nl = app->app_NodeList, node_list;
    UBYTE *buf;

    get(app->app_st_Conf_Alias, MUIA_String_Contents, &buf);

    if (!*buf)
        DeleteNodeListEntry();
    else
    {
        node_list.nl_Next = NULL;
        node_list.nl_Alias = buf;
        get(app->app_st_Conf_Node, MUIA_String_Contents, &buf);
        node_list.nl_Node = buf;
        get(app->app_st_Conf_List, MUIA_String_Contents, &buf);
        node_list.nl_List = buf;
        get(app->app_st_Conf_Font, MUIA_String_Contents, &buf);
        node_list.nl_Font = buf;
        get(app->app_st_Conf_Phone, MUIA_String_Contents, &buf);
        node_list.nl_Phone = buf;
        get(app->app_st_Conf_Password, MUIA_String_Contents, &buf);
        node_list.nl_Password = buf;
        get(app->app_st_Conf_Speed, MUIA_String_Contents, &buf);
        node_list.nl_Speed = buf;
        get(app->app_st_Conf_Time, MUIA_String_Contents, &buf);
        node_list.nl_Time = buf;
        get(app->app_st_Conf_Charges, MUIA_String_Contents, &buf);
        node_list.nl_Charges = buf;

        if (!nl)
        {
            if (!AddNodeListEntry(&node_list, DONT_BE_SILENT, NODELIST_APPEND))
                goto abort;
        }
        else
        {
            if (!AddNodeListEntry(&node_list, DONT_BE_SILENT, NODELIST_REPLACE))
                goto abort;
        }
    }

    return_ok = TRUE;
abort:
    return (return_ok);
}

int AddNodeListEntryInteractively(void)
{
    int return_ok = FALSE;
    struct NodeList node_list;
    UBYTE *buf;

    node_list.nl_Next = NULL;
    get(app->app_st_Conf_Alias, MUIA_String_Contents, &buf);
    node_list.nl_Alias = buf;
    get(app->app_st_Conf_Node, MUIA_String_Contents, &buf);
    node_list.nl_Node = buf;
    get(app->app_st_Conf_List, MUIA_String_Contents, &buf);
    node_list.nl_List = buf;
    get(app->app_st_Conf_Font, MUIA_String_Contents, &buf);
    node_list.nl_Font = buf;
    get(app->app_st_Conf_Phone, MUIA_String_Contents, &buf);
    node_list.nl_Phone = buf;
    get(app->app_st_Conf_Password, MUIA_String_Contents, &buf);
    node_list.nl_Password = buf;
    get(app->app_st_Conf_Speed, MUIA_String_Contents, &buf);
    node_list.nl_Speed = buf;
    get(app->app_st_Conf_Time, MUIA_String_Contents, &buf);
    node_list.nl_Time = buf;
    get(app->app_st_Conf_Charges, MUIA_String_Contents, &buf);
    node_list.nl_Charges = buf;

    if (!AddNodeListEntry(&node_list, DONT_BE_SILENT, NODELIST_APPEND))
        goto abort;

    return_ok = TRUE;
abort:
    return (return_ok);
}

int AddNodeListEntry(struct NodeList *node_list, int quiet, int mode)
{
    int return_ok = FALSE;
    struct NodeList *nl = NULL;
    struct NodeList *nl_walk;

    if (!(nl = (struct NodeList *)AllocVec(sizeof(struct NodeList), MEMF_CLEAR)))
        goto abort;

    if (!(nl->nl_Alias = (UBYTE *)AllocVec(strlen(node_list->nl_Alias) + 1, MEMF_CLEAR)))
        goto abort;
    strcpy(nl->nl_Alias, node_list->nl_Alias);

    if (!(nl->nl_Node = (UBYTE *)AllocVec(strlen(node_list->nl_Node) + 1, MEMF_CLEAR)))
        goto abort;
    strcpy(nl->nl_Node, node_list->nl_Node);

    if (!(nl->nl_List = (UBYTE *)AllocVec(strlen(node_list->nl_List) + 1, MEMF_CLEAR)))
        goto abort;
    strcpy(nl->nl_List, node_list->nl_List);

    if (!(nl->nl_Font = (UBYTE *)AllocVec(strlen(node_list->nl_Font) + 1, MEMF_CLEAR)))
        goto abort;
    strcpy(nl->nl_Font, node_list->nl_Font);

    if (!(nl->nl_Phone = (UBYTE *)AllocVec(strlen(node_list->nl_Phone) + 1, MEMF_CLEAR)))
        goto abort;
    strcpy(nl->nl_Phone, node_list->nl_Phone);

    if (!(nl->nl_Password = (UBYTE *)AllocVec(strlen(node_list->nl_Password) + 1, MEMF_CLEAR)))
        goto abort;
    strcpy(nl->nl_Password, node_list->nl_Password);

    if (!(nl->nl_Speed = (UBYTE *)AllocVec(strlen(node_list->nl_Speed) + 1, MEMF_CLEAR)))
        goto abort;
    strcpy(nl->nl_Speed, node_list->nl_Speed);

    if (!(nl->nl_Time = (UBYTE *)AllocVec(strlen(node_list->nl_Time) + 1, MEMF_CLEAR)))
        goto abort;
    strcpy(nl->nl_Time, node_list->nl_Time);

    if (!(nl->nl_Charges = (UBYTE *)AllocVec(strlen(node_list->nl_Charges) + 1, MEMF_CLEAR)))
        goto abort;
    strcpy(nl->nl_Charges, node_list->nl_Charges);

    if (mode == NODELIST_APPEND)
    {
        // append entry
        if (nl_walk = app->app_NodeList)
        {
            while (nl_walk->nl_Next)
                nl_walk = nl_walk->nl_Next;
            nl_walk->nl_Next = nl;
        }
        else
            app->app_NodeList = nl;

        // now add entry
        DoMethod(app->app_lv_Conf_NodeList, MUIM_List_Insert,
                 &nl->nl_Alias, 1, MUIV_List_Insert_Bottom);
        set(app->app_lv_Conf_NodeList, MUIA_List_Active, MUIV_List_Active_Bottom);
    }
    else if (mode == NODELIST_REPLACE)
    {
        LONG pos = -1, count = 0, pos_bak;


        nl_walk = app->app_NodeList;
        if (nl_walk)
        {
            for (; nl_walk; nl_walk = nl_walk->nl_Next)
                count++;

            nl_walk = app->app_NodeList;

            // cursor position
            get(app->app_lv_Conf_NodeList, MUIA_List_Active, &pos);
            if (pos >= 0)
            {
                struct NodeList nl_copy;

                pos_bak = pos;
                // jump to selected entry
                while (pos > 0)
                {
                    nl_walk = nl_walk->nl_Next;
                    pos--;
                }
                CopyMem(nl_walk, &nl_copy, sizeof(struct NodeList));

                // remove entry
                DoMethod(app->app_lv_Conf_NodeList, MUIM_List_Remove, pos_bak);
                nl_walk->nl_Alias = nl->nl_Alias;
                nl_walk->nl_Node = nl->nl_Node;
                nl_walk->nl_List = nl->nl_List;
                nl_walk->nl_Font = nl->nl_Font;
                nl_walk->nl_Phone = nl->nl_Phone;
                nl_walk->nl_Password = nl->nl_Password;
                nl_walk->nl_Speed = nl->nl_Speed;
                nl_walk->nl_Time = nl->nl_Time;
                nl_walk->nl_Charges = nl->nl_Charges;

                // readd entry
                DoMethod(app->app_lv_Conf_NodeList, MUIM_List_Insert,
                         &nl_walk->nl_Alias, 1,
                         (pos_bak + 1 == count) ? MUIV_List_Insert_Bottom : pos_bak);
                set(app->app_lv_Conf_NodeList, MUIA_List_Active, pos_bak);

                FreeVec(nl_copy.nl_Alias);
                FreeVec(nl_copy.nl_Node);
                FreeVec(nl_copy.nl_List);
                FreeVec(nl_copy.nl_Font);
                FreeVec(nl_copy.nl_Phone);
                FreeVec(nl_copy.nl_Password);
                FreeVec(nl_copy.nl_Speed);
                FreeVec(nl_copy.nl_Time);
                FreeVec(nl_copy.nl_Charges);
            }
            else
            {
                quiet = BE_SILENT;
                goto abort;
            }
        }
        else
        {
            quiet = BE_SILENT;
            goto abort;
        }
    }

    return_ok = TRUE;
abort:
    if (!return_ok)
    {
        if (nl)
        {
            if (nl->nl_Alias)
                FreeVec(nl->nl_Alias);
            if (nl->nl_Node)
                FreeVec(nl->nl_Node);
            if (nl->nl_List)
                FreeVec(nl->nl_List);
            if (nl->nl_Font)
                FreeVec(nl->nl_Font);
            if (nl->nl_Phone)
                FreeVec(nl->nl_Phone);
            if (nl->nl_Password)
                FreeVec(nl->nl_Password);
            if (nl->nl_Speed)
                FreeVec(nl->nl_Speed);
            if (nl->nl_Time)
                FreeVec(nl->nl_Time);
            if (nl->nl_Charges)
                FreeVec(nl->nl_Charges);
            FreeVec(nl);
        }
        if (!quiet)
            DispError(MSG_ERROR_OUT_OF_MEMORY, NULL);
    }
    else if (mode == NODELIST_REPLACE)
    {
        if (nl)
            FreeVec(nl);
    }
    return (return_ok);
}

void DeleteNodeListEntry(void)
{
    // non empty list
    if (app->app_NodeList)
    {
        LONG pos = -1;
        struct NodeList **nl = &app->app_NodeList, *nl_delete;

        get(app->app_lv_Conf_NodeList, MUIA_List_Active, &pos);
        if (pos >= 0)
        {
            DoMethod(app->app_lv_Conf_NodeList, MUIM_List_Remove, pos);

            // jump to selected entry
            while (pos > 0)
            {
                nl = &(*nl)->nl_Next;
                pos--;
            }

            // delete from chain and free mem
            if (*nl)
            {
                nl_delete = *nl;
                *nl = (*nl)->nl_Next;

                // update gadgets
                NodeListDoubleClick();

                if (nl_delete->nl_Alias)
                    FreeVec(nl_delete->nl_Alias);
                if (nl_delete->nl_Node)
                    FreeVec(nl_delete->nl_Node);
                if (nl_delete->nl_List)
                    FreeVec(nl_delete->nl_List);
                if (nl_delete->nl_Font)
                    FreeVec(nl_delete->nl_Font);
                if (nl_delete->nl_Phone)
                    FreeVec(nl_delete->nl_Phone);
                if (nl_delete->nl_Password)
                    FreeVec(nl_delete->nl_Password);
                if (nl_delete->nl_Speed)
                    FreeVec(nl_delete->nl_Speed);
                if (nl_delete->nl_Time)
                    FreeVec(nl_delete->nl_Time);
                if (nl_delete->nl_Charges)
                    FreeVec(nl_delete->nl_Charges);
                FreeVec(nl_delete);
            }
        }
    }
}
void ClearNodeList(void)
{
    if (app->app_NodeList)
    {
        struct NodeList *nl = app->app_NodeList, *nl_next;

        do
        {
            if (nl->nl_Alias)
                FreeVec(nl->nl_Alias);
            if (nl->nl_Node)
                FreeVec(nl->nl_Node);
            if (nl->nl_List)
                FreeVec(nl->nl_List);
            if (nl->nl_Font)
                FreeVec(nl->nl_Font);
            if (nl->nl_Phone)
                FreeVec(nl->nl_Phone);
            if (nl->nl_Password)
                FreeVec(nl->nl_Password);
            if (nl->nl_Speed)
                FreeVec(nl->nl_Speed);
            if (nl->nl_Time)
                FreeVec(nl->nl_Time);
            if (nl->nl_Charges)
                FreeVec(nl->nl_Charges);
            nl_next = nl->nl_Next;
            FreeVec(nl);
            nl = nl_next;
        }
        while (nl);
        app->app_NodeList = NULL;
    }
}
void NodeListDoubleClick(void)
{
    LONG pos = -1;
    struct NodeList *nl = app->app_NodeList;

    if (nl)
    {
        get(app->app_lv_Conf_NodeList, MUIA_List_Active, &pos);
        if (pos >= 0)
        {
            // jump to selected entry
            while (pos > 0)
            {
                nl = nl->nl_Next;
                pos--;
            }

            // set string gadgets accordingly
            set(app->app_st_Conf_Alias, MUIA_String_Contents, nl->nl_Alias);
            set(app->app_st_Conf_Node, MUIA_String_Contents, nl->nl_Node);
            set(app->app_st_Conf_List, MUIA_String_Contents, nl->nl_List);
            set(app->app_st_Conf_Font, MUIA_String_Contents, nl->nl_Font);
            set(app->app_st_Conf_Phone, MUIA_String_Contents, nl->nl_Phone);
            set(app->app_st_Conf_Password, MUIA_String_Contents, nl->nl_Password);
            set(app->app_st_Conf_Speed, MUIA_String_Contents, nl->nl_Speed);
            set(app->app_st_Conf_Time, MUIA_String_Contents, nl->nl_Time);
            set(app->app_st_Conf_Charges, MUIA_String_Contents, nl->nl_Charges);
        }
    }
    else
    {
        // set string gadgets accordingly
        set(app->app_st_Conf_Alias, MUIA_String_Contents, app->app_Conf_AliasBuf);
        set(app->app_st_Conf_Node, MUIA_String_Contents, app->app_Conf_NodeBuf);
        set(app->app_st_Conf_List, MUIA_String_Contents, app->app_Conf_ListBuf);
        set(app->app_st_Conf_Font, MUIA_String_Contents, app->app_Conf_FontBuf);
        set(app->app_st_Conf_Phone, MUIA_String_Contents, app->app_Conf_PhoneBuf);
        set(app->app_st_Conf_Password, MUIA_String_Contents, app->app_Conf_PasswordBuf);
        set(app->app_st_Conf_Speed, MUIA_String_Contents, app->app_Conf_SpeedBuf);
        set(app->app_st_Conf_Time, MUIA_String_Contents, app->app_Conf_TimeBuf);
        set(app->app_st_Conf_Charges, MUIA_String_Contents, app->app_Conf_ChargesBuf);
    }
}

int UpdateNodelist(int buf_num)
{
    int return_ok = FALSE;

    LONG pos = -1;
    struct NodeList *nl = app->app_NodeList;
    UBYTE *buf, **buf_ptr, *new_buf;
    APTR strgad;

    if (nl)
    {
        get(app->app_lv_Conf_NodeList, MUIA_List_Active, &pos);
        if (pos >= 0)
        {
            // jump to selected entry
            while (pos > 0)
            {
                nl = nl->nl_Next;
                pos--;
            }

            if (buf_num == NODE_BUF_NUM)
            {
                get(strgad = app->app_st_Conf_Node, MUIA_String_Contents, &buf);
                buf_ptr = &nl->nl_Node;
            }
            else if (buf_num == LIST_BUF_NUM)
            {
                get(strgad = app->app_st_Conf_List, MUIA_String_Contents, &buf);
                buf_ptr = &nl->nl_List;
            }
            else if (buf_num == FONT_BUF_NUM)
            {
                get(strgad = app->app_st_Conf_Font, MUIA_String_Contents, &buf);
                buf_ptr = &nl->nl_Font;
            }
            else if (buf_num == PHONE_BUF_NUM)
            {
                get(strgad = app->app_st_Conf_Phone, MUIA_String_Contents, &buf);
                buf_ptr = &nl->nl_Phone;
            }
            else if (buf_num == PASSWORD_BUF_NUM)
            {
                get(strgad = app->app_st_Conf_Password, MUIA_String_Contents, &buf);
                buf_ptr = &nl->nl_Password;
            }
            else if (buf_num == SPEED_BUF_NUM)
            {
                get(strgad = app->app_st_Conf_Speed, MUIA_String_Contents, &buf);
                buf_ptr = &nl->nl_Speed;
            }
            else if (buf_num == TIME_BUF_NUM)
            {
                get(strgad = app->app_st_Conf_Time, MUIA_String_Contents, &buf);
                buf_ptr = &nl->nl_Time;
            }
            else if (buf_num == CHARGES_BUF_NUM)
            {
                get(strgad = app->app_st_Conf_Charges, MUIA_String_Contents, &buf);
                buf_ptr = &nl->nl_Charges;
            }

            if (!(new_buf = (UBYTE *)AllocVec(strlen(buf) + 1, MEMF_CLEAR)))
                goto abort;
            strcpy(new_buf, buf);
            buf = *buf_ptr;
            *buf_ptr = new_buf;

            // set string gadgets accordingly
            set(strgad, MUIA_String_Contents, new_buf);
            FreeVec(buf);
        }
    }

    return_ok = TRUE;
abort:
    if (!return_ok)
        DispError(MSG_ERROR_OUT_OF_MEMORY, NULL);
    return (return_ok);
}

void SaveConfig(void)
{
    int return_ok = FALSE;
    UBYTE *buf, *confname;
    FILE *stream = NULL;
    int error = TRUE;

    // getenv MUIFFR/Config
    confname = getenv("MUIFFR/Config");
    // no env variable
    if (!confname)
        confname = CONFIGNAME;
    if (!(stream = fopen(confname, "w")))
    {
        error = FALSE;
        DispError(MSG_ERROR_CANT_OPEN, confname);
        goto abort;
    }

    if (fprintf(stream, "%s", CONFBANNER) < 0)
        goto abort;

    get(app->app_st_Main_Aliases, MUIA_String_Contents, &buf);
    if (fprintf(stream, "%s\n", buf) < 0)
        goto abort;

    get(app->app_st_Main_Password, MUIA_String_Contents, &buf);
    if (fprintf(stream, "%s\n", buf) < 0)
        goto abort;

    get(app->app_st_Main_Find, MUIA_String_Contents, &buf);
    if (fprintf(stream, "%s\n", buf) < 0)
        goto abort;

    get(app->app_st_Conf_Outbound, MUIA_String_Contents, &buf);
    if (fprintf(stream, "%s\n", buf) < 0)
        goto abort;

    if (app->app_Flags & APP_CYA_CONF_TERM_CRLF)
        fprintf(stream, "CRLF\n");
    else
        fprintf(stream, "LF\n");
    if (ferror(stream))
        goto abort;

    {
        LONG pos = 1;

        get(app->app_sl_Conf_FileNamePos, MUIA_Slider_Level, &pos);
        if (fprintf(stream, "%d\n", pos) < 0)
            goto abort;

        get(app->app_sl_Conf_BytesPos, MUIA_Slider_Level, &pos);
        if (fprintf(stream, "%d\n", pos) < 0)
            goto abort;
    }

    get(app->app_st_Conf_ViaNumber, MUIA_String_Contents, &buf);
    if (fprintf(stream, "%s\n", buf) < 0)
        goto abort;

    get(app->app_st_Conf_ViaNode, MUIA_String_Contents, &buf);
    if (fprintf(stream, "%s\n", buf) < 0)
        goto abort;

    get(app->app_st_Conf_NodeList, MUIA_String_Contents, &buf);
    if (fprintf(stream, "%s\n", buf) < 0)
        goto abort;

    {
        struct NodeList *nl = app->app_NodeList;

        for (; nl; nl = nl->nl_Next)
        {
            if (fprintf(stream, "%s\n", nl->nl_Alias) < 0)
                goto abort;

            if (fprintf(stream, "%s\n", nl->nl_Node) < 0)
                goto abort;

            if (fprintf(stream, "%s\n", nl->nl_Phone) < 0)
                goto abort;

            if (fprintf(stream, "%s\n", nl->nl_Password) < 0)
                goto abort;

            if (fprintf(stream, "%s\n", nl->nl_List) < 0)
                goto abort;

            if (fprintf(stream, "%s\n", nl->nl_Font) < 0)
                goto abort;

            if (fprintf(stream, "%s\n", nl->nl_Speed) < 0)
                goto abort;

            if (fprintf(stream, "%s\n", nl->nl_Time) < 0)
                goto abort;

            if (fprintf(stream, "%s\n", nl->nl_Charges) < 0)
                goto abort;
        }
    }

    return_ok = TRUE;
abort:
    if (stream)
        fclose(stream);
    if (!return_ok && error)
        DispError(MSG_ERROR_WHILE_WRITING, confname);
    if (return_ok)
        DispError(MSG_INFO_CONFIG_SAVED, NULL);
}

int LoadConfig(int quiet)
{
    int return_ok = FALSE;
    UBYTE buffer[BUF_SIZE], *confname, *readbuf = NULL;
    FILE *stream = NULL;
    int error = TRUE, old_config = FALSE;
    int filenamepos = 1;
    int bytespos = 2;
    int filesize, read_pos = 0;

    // getenv MUIFFR/Config
    confname = getenv("MUIFFR/Config");
    if (confname)
        stream = fopen(confname, "r");
    if (!stream)
    {
        // file specified by MUIFFR/Config does not exist or
        // no env variable
        confname = CONFIGNAME;

        if (!(stream = fopen(confname, "r")))
        {
            error = FALSE;
            if (!quiet)
                DispError(MSG_ERROR_CANT_OPEN, confname);
            goto abort;
        }
    }

    if (!GetFileSize(confname, &filesize, quiet))
    {
        error = FALSE;
        goto abort;
    }

    if (!(readbuf = (UBYTE *)AllocVec(filesize, MEMF_CLEAR)))
    {
        error = FALSE;
        if (!quiet)
            DispError(MSG_ERROR_OUT_OF_MEMORY, confname);
        goto abort;
    }

    if (fread(readbuf, 1, filesize, stream) != filesize)
    {
        error = FALSE;
        if (!quiet)
            DispError(MSG_ERROR_WHILE_READING, confname);
        goto abort;
    }

    // skip banner
    if (!GetLine(readbuf, &read_pos, filesize, buffer, BUF_SIZE))
        goto abort;
    // old config (version 1.1 or worse)
    if (!strncmp(buffer, OLDCONFBANNER, sizeof(OLDCONFBANNER)))
        old_config = TRUE;
    if (!GetLine(readbuf, &read_pos, filesize, buffer, BUF_SIZE))
        goto abort;

    // alias
    if (!GetLine(readbuf, &read_pos, filesize, buffer, ALIASSIZE))
        goto abort;
    strncpy(app->app_Main_AliasesBuf, buffer, ALIASSIZE - 1);

    // password
    if (!GetLine(readbuf, &read_pos, filesize, buffer, STANDARDSIZE))
        goto abort;
    strncpy(app->app_Main_PasswordBuf, buffer, STANDARDSIZE - 1);

    // find
    if (!GetLine(readbuf, &read_pos, filesize, buffer, STANDARDSIZE))
        goto abort;
    strncpy(app->app_Main_FindBuf, buffer, STANDARDSIZE - 1);

    // outbound
    if (!GetLine(readbuf, &read_pos, filesize, buffer, PATHSIZE))
        goto abort;
    strncpy(app->app_Conf_OutboundBuf, buffer, PATHSIZE - 1);

    // termination
    if (!GetLine(readbuf, &read_pos, filesize, buffer, STANDARDSIZE))
        goto abort;
    if (!strcmp(buffer, "CRLF"))
        app->app_Flags |= APP_CYA_CONF_TERM_CRLF;
    else
        app->app_Flags &= ~APP_CYA_CONF_TERM_CRLF;

    // filenamepos
    if (!GetLine(readbuf, &read_pos, filesize, buffer, STANDARDSIZE))
        goto abort;
    filenamepos = atoi(buffer);
    if (filenamepos < 1 || filenamepos > MAX_FILENAMEPOS)
        filenamepos = 1;

    if (!old_config)
    {
        // bytespos
        if (!GetLine(readbuf, &read_pos, filesize, buffer, STANDARDSIZE))
            goto abort;
        bytespos = atoi(buffer);
        if (bytespos < 1 || bytespos > MAX_BYTESPOS)
            bytespos = 1;
    }

    // via number
    if (!GetLine(readbuf, &read_pos, filesize, buffer, PATHSIZE))
        goto abort;
    strncpy(app->app_Conf_ViaNumberBuf, buffer, PATHSIZE - 1);

    // via node
    if (!GetLine(readbuf, &read_pos, filesize, buffer, PATHSIZE))
        goto abort;
    strncpy(app->app_Conf_ViaNodeBuf, buffer, PATHSIZE - 1);

    // nodelist
    if (!GetLine(readbuf, &read_pos, filesize, buffer, PATHSIZE))
        goto abort;
    strncpy(app->app_Conf_NodeListBuf, buffer, PATHSIZE - 1);

    // clear gadgets
    set(app->app_st_Conf_Alias, MUIA_String_Contents, app->app_Conf_AliasBuf);
    set(app->app_st_Conf_Node, MUIA_String_Contents, app->app_Conf_NodeBuf);
    set(app->app_st_Conf_List, MUIA_String_Contents, app->app_Conf_ListBuf);
    set(app->app_st_Conf_Font, MUIA_String_Contents, app->app_Conf_FontBuf);
    set(app->app_st_Conf_Phone, MUIA_String_Contents, app->app_Conf_PhoneBuf);
    set(app->app_st_Conf_Password, MUIA_String_Contents, app->app_Conf_PasswordBuf);
    set(app->app_st_Conf_Speed, MUIA_String_Contents, app->app_Conf_SpeedBuf);
    set(app->app_st_Conf_Time, MUIA_String_Contents, app->app_Conf_TimeBuf);
    set(app->app_st_Conf_Charges, MUIA_String_Contents, app->app_Conf_ChargesBuf);
    DoMethod(app->app_lv_Conf_NodeList, MUIM_List_Clear, TAG_IGNORE);
    ClearNodeList();

    // set gadgets
    set(app->app_st_Main_Aliases, MUIA_String_Contents, app->app_Main_AliasesBuf);
    set(app->app_st_Main_Password, MUIA_String_Contents, app->app_Main_PasswordBuf);
    set(app->app_st_Main_Find, MUIA_String_Contents, app->app_Main_FindBuf);
    set(app->app_st_Conf_Outbound, MUIA_String_Contents, app->app_Conf_OutboundBuf);
    set(app->app_sl_Conf_FileNamePos, MUIA_Slider_Level, filenamepos);
    set(app->app_sl_Conf_BytesPos, MUIA_Slider_Level, bytespos);
    set(app->app_cy_Conf_Termination, MUIA_Cycle_Active, (app->app_Flags & APP_CYA_CONF_TERM_CRLF) ? CYA_CONF_TERM_CRLF : CYA_CONF_TERM_LF);
    set(app->app_st_Conf_ViaNumber, MUIA_String_Contents, app->app_Conf_ViaNumberBuf);
    set(app->app_st_Conf_ViaNode, MUIA_String_Contents, app->app_Conf_ViaNodeBuf);
    set(app->app_st_Conf_NodeList, MUIA_String_Contents, app->app_Conf_NodeListBuf);

    // get nodes
    {
        struct NodeList node_list;
        int end_of_file = FALSE;
        UBYTE alias_buf[ALIASSIZE], node_buf[NODESIZE],
              list_buf[PATHSIZE], font_buf[PATHSIZE],
              phone_buf[PATHSIZE], password_buf[PATHSIZE],
              speed_buf[SMALLSIZE], time_buf[SMALLSIZE],
              charges_buf[SMALLSIZE];

        node_list.nl_Next = NULL;
        node_list.nl_Alias = alias_buf;
        node_list.nl_Node = node_buf;
        node_list.nl_List = list_buf;
        node_list.nl_Font = font_buf;
        node_list.nl_Phone = phone_buf;
        node_list.nl_Password = password_buf;
        node_list.nl_Speed = speed_buf;
        node_list.nl_Time = time_buf;
        node_list.nl_Charges = charges_buf;

        while (!end_of_file)
        {
            // alias
            if (!GetLine(readbuf, &read_pos, filesize, buffer, ALIASSIZE))
            {
                end_of_file = TRUE;
                continue;
            }
            strncpy(alias_buf, buffer, ALIASSIZE - 1);

            // node
            if (!GetLine(readbuf, &read_pos, filesize, buffer, NODESIZE))
                goto abort;
            strncpy(node_buf, buffer, NODESIZE - 1);

            // phone
            if (!GetLine(readbuf, &read_pos, filesize, buffer, PATHSIZE))
                goto abort;
            strncpy(phone_buf, buffer, PATHSIZE - 1);

            // password
            if (!GetLine(readbuf, &read_pos, filesize, buffer, PATHSIZE))
                goto abort;
            strncpy(password_buf, buffer, PATHSIZE - 1);

            // list
            if (!GetLine(readbuf, &read_pos, filesize, buffer, PATHSIZE))
                goto abort;
            strncpy(list_buf, buffer, PATHSIZE - 1);

            // font
            if (!GetLine(readbuf, &read_pos, filesize, buffer, PATHSIZE))
                goto abort;
            strncpy(font_buf, buffer, PATHSIZE - 1);

            if (!old_config)
            {
                // speed
                if (!GetLine(readbuf, &read_pos, filesize, buffer, SMALLSIZE))
                    goto abort;
                strncpy(speed_buf, buffer, SMALLSIZE - 1);

                // time
                if (!GetLine(readbuf, &read_pos, filesize, buffer, SMALLSIZE))
                    goto abort;
                strncpy(time_buf, buffer, SMALLSIZE - 1);

                // charges
                if (!GetLine(readbuf, &read_pos, filesize, buffer, SMALLSIZE))
                    goto abort;
                strncpy(charges_buf, buffer, SMALLSIZE - 1);
            }
            else
            {
                *speed_buf = EOS;
                *time_buf = EOS;
                *charges_buf = EOS;
            }

            if (!AddNodeListEntry(&node_list, quiet, NODELIST_APPEND))
            {
                error = FALSE;
                goto abort;
            }
        }
    }

    // select node in nodelist and load filelist
    {
        LONG pos = 0, count = -1;
        struct NodeList *nl = app->app_NodeList;

        if (nl)
        {
            get(app->app_lv_Conf_NodeList, MUIA_List_Entries, &count);

            for (; pos < count; pos++, nl = nl->nl_Next)
                if (!strcmp(app->app_Main_AliasesBuf, nl->nl_Alias))
                    break;

            if (pos < count)
            {
                // refresh configuration window
                set(app->app_lv_Conf_NodeList, MUIA_List_Active, pos);
                NodeListDoubleClick();

                if (!ReadList(quiet))
                {
                    error = FALSE;
                    goto abort;
                }
            }
            else
            {
                if (!quiet)
                    DispError(MSG_ERROR_UNKNOWN_ALIAS, app->app_Main_AliasesBuf);
                error = FALSE;
                goto abort;
            }
        }
    }

    return_ok = TRUE;
abort:
    if (stream)
        fclose(stream);
    if (readbuf)
        FreeVec(readbuf);
    if (!return_ok && !quiet && error)
        DispError(MSG_ERROR_CORRUPT_CONFIG, confname);
    return (return_ok);
}

static int GetFileSize(UBYTE *filename, int *file_len, int quiet)
{
    int return_ok = FALSE;
    BPTR lock;
    __aligned struct FileInfoBlock fib;

    if (!(lock = Lock(filename, ACCESS_READ)))
    {
        if (!quiet)
            DispError(MSG_ERROR_CANT_OPEN, filename);
        goto abort;
    }

    if (!Examine(lock, &fib))
        goto abort;

    *file_len = fib.fib_Size;
    return_ok = TRUE;
abort:
    if (lock)
        UnLock(lock);
    return (return_ok);
}

static int GetLine(UBYTE *readbuf, int *read_pos, int filesize, UBYTE *dest, int dest_size)
{
    int return_ok = FALSE;
    int dest_pos = 0;

    if (*read_pos >= filesize)
        goto abort;

    for (; *read_pos < filesize &&
           readbuf[*read_pos] != '\n' &&
           dest_pos < dest_size - 1; (*read_pos)++)
        dest[dest_pos++] = readbuf[*read_pos];

    dest[dest_pos] = EOS;
    (*read_pos)++;

    return_ok = TRUE;
abort:
    return (return_ok);
}

void Download(void)
{
    UBYTE *nodelist, *mailer, *buf;
    UBYTE buffer[BUF_SIZE];
    int buf_pos = 0, flag = FALSE;

    // request list empty?
    if (!app->app_RequestList)
    {
        DispError(MSG_ERROR_NOTHING_SELECTED, NULL);
        return;
    }

    // save request list
    if (!SaveReqFile())
        return;

    // did the user supply a telephone number for the current node?
    get(app->app_st_Conf_Phone, MUIA_String_Contents, &buf);

    // yes --> call node via phone number
    if (*buf)
        get(app->app_st_Conf_ViaNumber, MUIA_String_Contents, &mailer);
    // no --> call node via node number
    else
        get(app->app_st_Conf_ViaNode, MUIA_String_Contents, &mailer);
    get(app->app_st_Conf_NodeList, MUIA_String_Contents, &nodelist);

    if (!*mailer)
        return;

    while (*mailer)
    {
        if (*mailer == '%')
        {
            // %t --> insert telephone number
            // %n --> insert node number
            // %l --> insert node list
            // %p --> insert password

            flag = TRUE;
            switch (mailer[1])
            {
            case 't':
                get(app->app_st_Conf_Phone, MUIA_String_Contents, &buf);
                break;

            case 'n':
                get(app->app_st_Conf_Node, MUIA_String_Contents, &buf);
                break;

            case 'l':
                get(app->app_st_Conf_NodeList, MUIA_String_Contents, &buf);
                break;

            case 'p':
                get(app->app_st_Conf_Password, MUIA_String_Contents, &buf);
                break;

            default:
                flag = FALSE;
                break;
            }
            if (flag)
            {
                buffer[buf_pos] = EOS;
                strcat(buffer, buf);
                buf_pos = strlen(buffer);
                mailer += 2;
            }
        }
        if (!flag)
            buffer[buf_pos++] = *mailer++;
        flag = FALSE;
    }
    buffer[buf_pos] = EOS;

    // invoke mailer asynchronously
    {
        BPTR nil_in, nil_out;

        // input handle
        if (!(nil_in = Open("nil:", MODE_OLDFILE)))
            return;

        // output handle
        if (!(nil_out = Open("nil:", MODE_OLDFILE)))
        {
            Close(nil_in);
            return;
        }

        if (SystemTags(buffer,
                       SYS_Input, nil_in,
                       SYS_Output, nil_out,
                       SYS_UserShell, TRUE,
                       SYS_Asynch, TRUE,
                       NP_StackSize, 50000,
                       TAG_DONE) == -1)
        {
            DispError(MSG_ERROR_INVOKING_MAILER, buffer);
            Close(nil_in);
            Close(nil_out);
        }
    }
}
