diff --git a/CMakeLists.txt b/CMakeLists.txt index fc8d1b8..822879f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -29,6 +29,7 @@ add_executable(${PROJECT_NAME} WIN32 viewlog.c as.c pkcs11.c + config_parser.c res/openvpn-gui-res.rc) find_package(OpenSSL REQUIRED) diff --git a/Makefile.am b/Makefile.am index d34e7fe..a9d5af3 100644 --- a/Makefile.am +++ b/Makefile.am @@ -96,6 +96,7 @@ openvpn_gui_SOURCES = \ echo.c echo.h \ as.c as.h \ pkcs11.c pkcs11.h \ + config_parser.c config_parser.h \ openvpn-gui-res.h openvpn_gui_LDFLAGS = -mwindows diff --git a/config_parser.c b/config_parser.c new file mode 100644 index 0000000..c2e20d3 --- /dev/null +++ b/config_parser.c @@ -0,0 +1,180 @@ +/* + * This file is a part of OpenVPN-GUI -- A Windows GUI for OpenVPN. + * + * Copyright (C) 2016 Selva Nair + * + * 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 + */ + +#include +#include +#include +#include +#include +#include "main.h" +#include "config_parser.h" + +static int +legal_escape(wchar_t c) +{ + wchar_t *escapes = L"\"\' \\"; /* space, ", ' or backslash */ + return (wcschr(escapes, c) != NULL); +} + +static int +copy_token(wchar_t **dest, wchar_t **src, wchar_t* delim) +{ + wchar_t *p = *src; + wchar_t *s = *dest; + + /* copy src to dest until delim character with escaped chars converted */ + for ( ; *p != L'\0' && wcschr(delim, *p) == NULL; p++, s++) + { + if (*p == L'\\' && legal_escape(*(p+1))) + { + *s = *(++p); + } + else if (*p == L'\\') + { + return -1; /* parse error -- illegal backslash in input */ + } + else + *s = *p; + } + /* at this point p is one of the delimiters or null */ + *s = L'\0'; + *src = p; + *dest = s; + return 0; +} + +static int +tokenize(config_entry_t *ce) +{ + wchar_t *p, *s; + p = ce->line; + s = ce->sline; + unsigned int i = 0; + int status; + + for ( ; *p != L'\0'; p++, s++) + { + if (*p == L' ' || *p == L'\t') continue; + + if (_countof(ce->tokens) <= i) + { + return -1; + } + ce->tokens[i++] = s; + + if (*p == L'\'' ) + { + int len = wcscspn(++p, L"\'"); + wcsncpy(s, p, len); + s += len; + p += len; + } + else if (*p == L'\"') + { + p++; + status = copy_token(&s, &p, L"\""); + } + else + status = copy_token(&s, &p, L" \t"); + if (status != 0) return status; + + if (*p == L'\0') break; + } + ce->ntokens = i; + return 0; +} + +config_entry_t * +config_readline(FILE *fd, int first) +{ + int len; + char tmp[MAX_LINE_LENGTH]; + int offset = 0; + + if (fgets(tmp, _countof(tmp)-1, fd) == NULL) + { + return NULL; + } + /* remove UTF-8 BOM */ + if (first && strncmp(tmp, "\xEF\xBB\xBF", 3) == 0) + { + offset = 3; + } + + config_entry_t *ce = calloc(sizeof(*ce), 1); + if (!ce) + { + MsgToEventLog(EVENTLOG_ERROR_TYPE, L"Out of memory in tokenize"); + return NULL; + } + + mbstowcs(ce->line, &tmp[offset], _countof(ce->line)-1); + + len = wcscspn(ce->line, L"\n\r"); + ce->line[len] = L'\0'; + + if (tokenize(ce) != 0) + { + free(ce); + return NULL; + } + + return ce; +} + +config_entry_t * +config_parse(wchar_t *fname) +{ + FILE *fd = NULL; + config_entry_t *head, *tail; + + if (fname) + { + fd = _wfopen(fname, L"r"); + } + if (!fd) + { + MsgToEventLog(EVENTLOG_ERROR_TYPE, L"Error opening <%ls> in config_parse", fname); + return NULL; + } + head = tail = config_readline(fd, 1); + + while (tail) + { + tail->next = config_readline(fd, 0); + tail = tail->next; + } + fclose(fd); + return head; +} + +void +config_list_free(config_entry_t *head) +{ + config_entry_t *next; + while (head) + { + next = head->next; + free(head); + head = next; + } + return; +} diff --git a/config_parser.h b/config_parser.h new file mode 100644 index 0000000..4848aed --- /dev/null +++ b/config_parser.h @@ -0,0 +1,54 @@ +/* + * This file is a part of OpenVPN-GUI -- A Windows GUI for OpenVPN. + * + * Copyright (C) 2016 Selva Nair + * + * 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 + */ + +#ifndef CONFIG_PARSER_H +#define CONFIG_PARSER_H + +#define MAX_LINE_LENGTH 256 + +typedef struct config_entry config_entry_t; + +struct config_entry { + wchar_t line[MAX_LINE_LENGTH]; + wchar_t sline[MAX_LINE_LENGTH]; + wchar_t *tokens[16]; + int ntokens; + config_entry_t *next; +}; + +/** + * Parse an ovpn file into a list of tokenized + * structs. + * @param fname : filename of the config to parse + * @returns the pointer to the head of a list of + * config_entry_t structs. + * The called must free it after use by calling + * config_list_free() + */ +config_entry_t *config_parse(wchar_t *fname); + +/** + * Free a list of config_entry_t structs + * @param head : list head returned by config_parse() + */ +void config_list_free(config_entry_t *head); + +#endif