/*
 *  OpenVPN-GUI -- A Windows GUI for OpenVPN.
 *
 *  Copyright (C) 2004 Mathias Sundman <mathias@nilings.se>
 *                2010 Heiko Hund <heikoh@users.sf.net>
 *                2016 Selva Nair <selva.nair@gmail.com>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program (see the file COPYING included with this
 *  distribution); if not, write to the Free Software Foundation, Inc.,
 *  59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <windows.h>

#include "main.h"
#include "openvpn-gui-res.h"
#include "options.h"
#include "localization.h"
#include "save_pass.h"
#include "misc.h"
#include "passphrase.h"

typedef enum
{
    match_false,
    match_file,
    match_dir
} match_t;

extern options_t o;

static match_t
match(const WIN32_FIND_DATA *find, const TCHAR *ext)
{
    size_t ext_len = _tcslen(ext);
    int i;

    if (find->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
        return match_dir;

    if (ext_len == 0)
        return match_file;

    i = _tcslen(find->cFileName) - ext_len - 1;

    if (i > 0 && find->cFileName[i] == '.'
    && _tcsicmp(find->cFileName + i + 1, ext) == 0)
        return match_file;

    return match_false;
}

static bool
CheckReadAccess (const TCHAR *dir, const TCHAR *file)
{
    TCHAR path[MAX_PATH];

    _sntprintf_0 (path, _T("%s\\%s"), dir, file);

    return CheckFileAccess (path, GENERIC_READ);
}

static int
ConfigAlreadyExists(TCHAR *newconfig)
{
    int i;
    for (i = 0; i < o.num_configs; ++i)
    {
        if (_tcsicmp(o.conn[i].config_file, newconfig) == 0)
            return true;
    }
    return false;
}


static void
AddConfigFileToList(int config, const TCHAR *filename, const TCHAR *config_dir)
{
    connection_t *c = &o.conn[config];
    int i;

    _tcsncpy(c->config_file, filename, _countof(c->config_file) - 1);
    _tcsncpy(c->config_dir, config_dir, _countof(c->config_dir) - 1);
    _tcsncpy(c->config_name, c->config_file, _countof(c->config_name) - 1);
    c->config_name[_tcslen(c->config_name) - _tcslen(o.ext_string) - 1] = _T('\0');
    _sntprintf_0(c->log_path, _T("%s\\%s.log"), o.log_dir, c->config_name);

    c->manage.sk = INVALID_SOCKET;
    c->manage.skaddr.sin_family = AF_INET;
    c->manage.skaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
    c->manage.skaddr.sin_port = htons(25340 + config);

#ifndef DISABLE_CHANGE_PASSWORD
    if (CheckKeyFileWriteAccess (c))
        c->flags |= ALLOW_CHANGE_PASSPHRASE;
#endif

    /* Check if connection should be autostarted */
    for (i = 0; i < MAX_CONFIGS && o.auto_connect[i]; ++i)
    {
        if (_tcsicmp(c->config_file, o.auto_connect[i]) == 0)
        {
            c->auto_connect = true;
            break;
        }
    }
    /* check whether passwords are saved */
    if (IsAuthPassSaved(c->config_name))
        c->flags |= FLAG_SAVE_AUTH_PASS;
    if (IsKeyPassSaved(c->config_name))
        c->flags |= FLAG_SAVE_KEY_PASS;
}


static void
BuildFileList0(const TCHAR *config_dir, bool warn_duplicates)
{
    WIN32_FIND_DATA find_obj;
    HANDLE find_handle;
    TCHAR find_string[MAX_PATH];
    TCHAR subdir_table[MAX_CONFIG_SUBDIRS][MAX_PATH];
    int subdirs = 0;
    int i;

    _sntprintf_0(find_string, _T("%s\\*"), config_dir);
    find_handle = FindFirstFile(find_string, &find_obj);
    if (find_handle == INVALID_HANDLE_VALUE)
        return;

    /* Loop over each config file in main config dir */
    do
    {
        if (o.num_configs >= MAX_CONFIGS)
        {
            ShowLocalizedMsg(IDS_ERR_MANY_CONFIGS, MAX_CONFIGS);
            break;
        }

        match_t match_type = match(&find_obj, o.ext_string);
        if (match_type == match_file)
        {
            if (ConfigAlreadyExists(find_obj.cFileName))
            {
                if (warn_duplicates)
                    ShowLocalizedMsg(IDS_ERR_CONFIG_EXIST, find_obj.cFileName);
                continue;
            }

            if (CheckReadAccess (config_dir, find_obj.cFileName))
                AddConfigFileToList(o.num_configs++, find_obj.cFileName, config_dir);
        }
        else if (match_type == match_dir)
        {
            if (_tcsncmp(find_obj.cFileName, _T("."), _tcslen(find_obj.cFileName)) != 0
            &&  _tcsncmp(find_obj.cFileName, _T(".."), _tcslen(find_obj.cFileName)) != 0
            &&  subdirs < MAX_CONFIG_SUBDIRS)
            {
                /* Add dir to dir_table */
                _sntprintf_0(subdir_table[subdirs], _T("%s\\%s"), config_dir, find_obj.cFileName);
                subdirs++;
            }
        }
    } while (FindNextFile(find_handle, &find_obj));

    FindClose(find_handle);

    /* Loop over each config file in every subdir */
    for (i = 0; i < subdirs; ++i)
    {
        _sntprintf_0(find_string, _T("%s\\*"), subdir_table[i]);

        find_handle = FindFirstFile (find_string, &find_obj);
        if (find_handle == INVALID_HANDLE_VALUE)
            continue;

        do
        {
            if (o.num_configs >= MAX_CONFIGS)
            {
                ShowLocalizedMsg(IDS_ERR_MANY_CONFIGS, MAX_CONFIGS);
                FindClose(find_handle);
                return;
            }

            /* does file have the correct type and extension? */
            if (match(&find_obj, o.ext_string) != match_file)
                continue;

            if (ConfigAlreadyExists(find_obj.cFileName))
            {
                if (warn_duplicates)
                    ShowLocalizedMsg(IDS_ERR_CONFIG_EXIST, find_obj.cFileName);
                continue;
            }

            if (CheckReadAccess (subdir_table[i], find_obj.cFileName))
                AddConfigFileToList(o.num_configs++, find_obj.cFileName, subdir_table[i]);
        } while (FindNextFile(find_handle, &find_obj));

        FindClose(find_handle);
    }
}

void
BuildFileList()
{
    static bool issue_warnings = true;

    /*
     * If no connections are active reset num_configs and rescan
     * to make a new list. Else we keep all current configs and
     * rescan to add any new one's found
     */
    if (CountConnState(disconnected) == o.num_configs)
        o.num_configs = 0;

    BuildFileList0 (o.config_dir, issue_warnings);

    if (_tcscmp (o.global_config_dir, o.config_dir))
        BuildFileList0 (o.global_config_dir, issue_warnings);

    if (o.num_configs == 0 && issue_warnings)
        ShowLocalizedMsg(IDS_NFO_NO_CONFIGS);

    issue_warnings = false;
}