From: Mike Lowis Date: Thu, 3 Mar 2016 18:03:19 +0000 (-0500) Subject: Added ini config file parsing for connection info X-Git-Url: https://git.mdlowis.com/?a=commitdiff_plain;h=9fa60fbc334273aa1cebab004dd83c056718de5b;p=archive%2Facc.git Added ini config file parsing for connection info --- diff --git a/Makefile b/Makefile index 54f7b5c..ce98623 100644 --- a/Makefile +++ b/Makefile @@ -13,8 +13,7 @@ LIBS = -lexpat -lssl -lresolv CPPFLAGS = -DVERSION="$(VERSION)" CFLAGS = -std=gnu11 ${INCS} ${CPPFLAGS} LDFLAGS = ${LIBS} -INCS = -Isource/ \ - -I/usr/include/ +INCS = -Iinclude/ -Isource/ -I/usr/include/ # commands COMPILE = ${CC} ${CFLAGS} -c -o $@ $< @@ -26,8 +25,8 @@ CLEAN = @rm -f #------------------------------------------------------------------------------ # library macros BIN = acc -SRCS = source/auth.c \ - source/basic.c \ +SRCS = source/main.c \ + source/auth.c \ source/conn.c \ source/ctx.c \ source/event.c \ diff --git a/include/autil.h b/include/autil.h new file mode 100644 index 0000000..d5689ce --- /dev/null +++ b/include/autil.h @@ -0,0 +1,285 @@ +/** + @brief Collection of useful C types and functions. + @author Michael D. Lowis + @license BSD 2-clause License +*/ +#ifndef UTIL_H +#define UTIL_H + +/* Standard Macros and Types */ +#include +#include +#include +#include +#include +#include + +/* Useful Standard Functions */ +#include +#include +#include +#include +#include + +/* Type Definitions + *****************************************************************************/ +typedef unsigned short ushort; +typedef unsigned char uchar; +typedef unsigned long ulong; +typedef unsigned int uint; +typedef signed char schar; +typedef long long vlong; +typedef unsigned long long uvlong; + +/* Generic Death Function + *****************************************************************************/ +static void die(const char* msgfmt, ...) +{ + va_list args; + va_start(args, msgfmt); + #ifdef CLEANUP_HOOK + CLEANUP_HOOK(); + #endif + fprintf(stderr, "Error: "); + vfprintf(stderr, msgfmt, args); + fprintf(stderr, "\n"); + va_end(args); + exit(EXIT_FAILURE); +} + +/* Signal Handling + *****************************************************************************/ +static void esignal(int sig, void (*func)(int)) +{ + errno = 0; + func = signal(sig, func); + if (func == SIG_ERR || errno > 0) + die("failed to register signal handler for signal %d", sig); +} + +static int eraise(int sig) +{ + int ret; + if ((ret = raise(sig)) != 0) + die("failed to raise signal %d", sig); + return ret; +} + +/* Dynamic Allocation + *****************************************************************************/ +static void* ecalloc(size_t num, size_t size) +{ + void* ret; + if (NULL == (ret = calloc(num,size))) + die("out of memory"); + return ret; +} + +static void* emalloc(size_t size) +{ + void* ret; + if (NULL == (ret = malloc(size))) + die("out of memory"); + return ret; +} + +static void* erealloc(void* ptr, size_t size) +{ + void* ret; + if (NULL == (ret = realloc(ptr,size))) + die("out of memory"); + return ret; +} + +/* File Handling + *****************************************************************************/ +static FILE* efopen(const char* filename, const char* mode) +{ + FILE* file; + errno = 0; + if (NULL == (file = fopen(filename, mode)) || errno != 0) + die("failed to open file: %s", filename); + return file; +} + +static char* efreadline(FILE* input) +{ + size_t size = 8; + size_t index = 0; + char* str = (char*)emalloc(size); + memset(str, 0, 8); + if (feof(input)) { + free(str); + return NULL; + } + while (true) { + char ch = fgetc(input); + if (ch == EOF) break; + str[index++] = ch; + str[index] = '\0'; + if (index+1 >= size) { + size = size << 1; + str = erealloc(str, size); + } + if (ch == '\n') break; + } + return str; +} + +/* String Handling + *****************************************************************************/ +static char* estrdup(const char *s) +{ + char* ns = (char*)emalloc(strlen(s) + 1); + strcpy(ns,s); + return ns; +} + +/* Option Parsing + * + * This following macros implement a simple POSIX-style option parsing strategy. + * They are heavily influenced and inspired by the arg.h file from suckless.org + * (http://git.suckless.org/libsl/tree/arg.h). That file is in turn inspired by + * the corresponding macros defined in plan9. + * + * The interface assumes that the main function will have the following + * prototype: + * + * int main(int argc, char** argv); + * + * An example usage of the interface would look something like the follwoing: + * + * char* ARGV0; + * int main(int argc, char** argv) { + * OPTBEGIN { + * case 'a': printf("Simple option\n"); break; + * case 'b': printf("Option with arg: %s\n", OPTARG()); break; + * default: printf("Unknown option!\n"); + * } OPTEND; + * return 0; + * } + */ + +/* This variable contains the value of argv[0] so that it can be referenced + * again once the option parsing is done. This variable must be defined by the + * program. + * + * NOTE: Ensure that you define this variable with external linkage (i.e. not + * static) */ +extern char* ARGV0; + +/* This is a helper function used by the following macros to parse the next + * option from the command line. */ +static inline char* _getopt_(int* p_argc, char*** p_argv) { + if (!(*p_argv)[0][1] && !(*p_argv)[1]) { + return (char*)0; + } else if ((*p_argv)[0][1]) { + return &(*p_argv)[0][1]; + } else { + *p_argv = *p_argv + 1; + *p_argc = *p_argc - 1; + return (*p_argv)[0]; + } +} + +/* This macro is almost identical to the ARGBEGIN macro from suckless.org. If + * it ain't broke, don't fix it. */ +#define OPTBEGIN \ + for ( \ + ARGV0 = *argv, argc--, argv++; \ + argv[0] && argv[0][1] && argv[0][0] == '-'; \ + argc--, argv++ \ + ) { \ + int brk_; char argc_ , **argv_, *optarg_; \ + if (argv[0][1] == '-' && !argv[0][2]) { \ + argv++, argc--; break; \ + } \ + for (brk_=0, argv[0]++, argv_=argv; argv[0][0] && !brk_; argv[0]++) { \ + if (argv_ != argv) break; \ + argc_ = argv[0][0]; \ + switch (argc_) + +/* Terminate the option parsing. */ +#define OPTEND }} + +/* Get the current option character */ +#define OPTC() (argc_) + +/* Get an argument from the command line and return it as a string. If no + * argument is available, this macro returns NULL */ +#define OPTARG() \ + (optarg_ = _getopt_(&argc,&argv), brk_ = (optarg_!=0), optarg_) + +/* Get an argument from the command line and return it as a string. If no + * argument is available, this macro executes the provided code. If that code + * returns, then abort is called. */ +#define EOPTARG(code) \ + (optarg_ = _getopt_(&argc,&argv), \ + (!optarg_ ? ((code), abort(), (char*)0) : (brk_ = 1, optarg_))) + +/* Helper macro to recognize number options */ +#define OPTNUM \ + case '0': \ + case '1': \ + case '2': \ + case '3': \ + case '4': \ + case '5': \ + case '6': \ + case '7': \ + case '8': \ + case '9' + +/* Helper macro to recognize "long" options ala GNU style. */ +#define OPTLONG \ + case '-' + +/* Error Handling + *****************************************************************************/ +#ifdef NDEBUG + #define debug(msg, ...) \ + ((void)0) +#else + #define debug(msg, ...) \ + fprintf(stderr, "DEBUG %s:%d: " msg "\n", __FILE__, __LINE__, ##__VA_ARGS__) +#endif + +#define errnostr() \ + (errno == 0 ? "None" : strerror(errno)) + +#define print_error(msg, ...) \ + fprintf(stderr, "[ERROR] (%s:%d: errno: %s) " msg "\n", __FILE__, __LINE__, errnostr(), ##__VA_ARGS__) + +#define check(expr, msg, ...) \ + if(!(expr)) { print_error(msg, ##__VA_ARGS__); errno=0; goto error; } + +#define sentinel(msg, ...) \ + { print_error(msg, ##__VA_ARGS__); errno=0; goto error; } + +/* Miscellaneous + *****************************************************************************/ +#ifndef nelem + #define nelem(x) \ + (sizeof(x)/sizeof((x)[0])) +#endif + +#ifndef container_of + #define container_of(obj, type, member) \ + (type*)((uintptr_t)obj - offsetof(type, member)) +#endif + +#define concat(a,b) \ + a##b + +#define ident(a) \ + concat(id, a) + +#define unique_id \ + ident(__LINE__) + +#ifndef static_assert + #define static_assert(expr) \ + typedef char unique_id[( expr )?1:-1] +#endif + +#endif diff --git a/include/ini.h b/include/ini.h new file mode 100644 index 0000000..cd5e1df --- /dev/null +++ b/include/ini.h @@ -0,0 +1,104 @@ +/** + @ibrief A simple and minimal INI-file parser. + @author Michael D. Lowis + @license BSD 2-clause License +*/ +#ifndef INI_H +#define INI_H + +#include +#include +#include +#include + +#ifndef INI_SECT_MAX +#define INI_SECT_MAX (256u) +#endif + +#ifndef INI_LINE_MAX +#define INI_LINE_MAX (256u) +#endif + +#ifndef INI_COMMENT_CHAR +#define INI_COMMENT_CHAR ';' +#endif + +typedef struct { + FILE* file; + char section[INI_SECT_MAX]; + char line[INI_LINE_MAX]; +} inifile_t; + +typedef struct { + const char* section; + const char* name; + const char* value; +} inientry_t; + +static char* ininows(char* str) +{ + /* Skip leading whitespace */ + while (*str && isspace(*str)) + str++; + /* Trim trailing whitespace */ + char* end = str + strlen(str); + while (end > str && isspace(*(--end))) + *end = '\0'; + return str; +} + +static bool iniparse(inifile_t* inifile, inientry_t* entry) +{ + /* If no file was opened then stop parsing here. */ + if (inifile->file == NULL) return false; + /* First things first, clear the entry */ + memset(entry, 0, sizeof(inientry_t)); + /* Otherwise, there must be something we can do */ + while (!feof(inifile->file)) { + inifile->line[0] = '\0'; // Reset the line + fgets(inifile->line, INI_LINE_MAX, inifile->file); + char* start = ininows(inifile->line); + if (*start == '[') { + char* section = inifile->section; + start++; + while (*start && *start != ']' && + ((section - inifile->section) <= INI_SECT_MAX)) { + *(section++) = *(start++); + *(section) = '\0'; + } + } else if (*start != '\0' && *start != INI_COMMENT_CHAR) { + /* Figure out the maximum end of the name field */ + char* end = (inifile->line + (INI_LINE_MAX-2)); + /* Store off the section and name since we have those now */ + entry->section = inifile->section; + entry->name = start; + /* Mark the end of the name field with a null char */ + while (*start && start < end && !isspace(*start)) + start++; + *(start++) = '\0'; + /* Skip everything up to and including the = */ + while (*start && start < end && *start != '=') + start++; + /* Skip whitespace after the = */ + entry->value = ininows(start+1); + /* Mark the end of the value field */ + while (*start && *start != INI_COMMENT_CHAR) + start++; + *start = '\0'; + /* Strip whitespace on both sides and return the entry */ + entry->value = ininows((char*)(entry->value)); + return true; + } + } + /* If we got here, the file is done being parsed so close it and wrap up */ + fclose(inifile->file); + inifile->file = NULL; + return false; +} + +static bool inimatch(const char* expval, inientry_t* entry) +{ + return (0 == strcmp(expval, entry->name)); +} + +#endif /* INI_H */ diff --git a/source/basic.c b/source/basic.c deleted file mode 100644 index 652d860..0000000 --- a/source/basic.c +++ /dev/null @@ -1,112 +0,0 @@ -/* basic.c -** libstrophe XMPP client library -- basic usage example -** -** Copyright (C) 2005-2009 Collecta, Inc. -** -** This software is provided AS-IS with no warranty, either express -** or implied. -** -** This program is dual licensed under the MIT and GPLv3 licenses. -*/ - -#include -#include - -#include - - -/* define a handler for connection events */ -void conn_handler(xmpp_conn_t * const conn, const xmpp_conn_event_t status, - const int error, xmpp_stream_error_t * const stream_error, - void * const userdata) -{ - xmpp_ctx_t *ctx = (xmpp_ctx_t *)userdata; - int secured; - - if (status == XMPP_CONN_CONNECT) { - fprintf(stderr, "DEBUG: connected\n"); - secured = xmpp_conn_is_secured(conn); - fprintf(stderr, "DEBUG: connection is %s.\n", - secured ? "secured" : "NOT secured"); - xmpp_disconnect(conn); - } - else { - fprintf(stderr, "DEBUG: disconnected\n"); - xmpp_stop(ctx); - } -} - -int main(int argc, char **argv) -{ - xmpp_ctx_t *ctx; - xmpp_conn_t *conn; - xmpp_log_t *log; - char *jid, *pass, *host = NULL; - long flags = 0; - int i; - - /* take a jid and password on the command line */ - for (i = 1; i < argc; ++i) { - if (strcmp(argv[i], "--disable-tls") == 0) - flags |= XMPP_CONN_FLAG_DISABLE_TLS; - else if (strcmp(argv[i], "--mandatory-tls") == 0) - flags |= XMPP_CONN_FLAG_MANDATORY_TLS; - else if (strcmp(argv[i], "--legacy-ssl") == 0) - flags |= XMPP_CONN_FLAG_LEGACY_SSL; - else - break; - } - if ((argc - i) < 2 || (argc - i) > 3) { - fprintf(stderr, "Usage: basic [options] []\n\n" - "Options:\n" - " --disable-tls Disable TLS.\n" - " --mandatory-tls Deny plaintext connection.\n" - " --legacy-ssl Use old style SSL.\n\n" - "Note: --disable-tls conflicts with --mandatory-tls or " - "--legacy-ssl\n"); - return 1; - } - - jid = argv[i]; - pass = argv[i + 1]; - if (i + 2 < argc) - host = argv[i + 2]; - - /* - * Note, this example doesn't handle errors. Applications should check - * return values of non-void functions. - */ - - /* init library */ - xmpp_initialize(); - - /* create a context */ - log = xmpp_get_default_logger(XMPP_LEVEL_DEBUG); /* pass NULL instead to silence output */ - ctx = xmpp_ctx_new(NULL, log); - - /* create a connection */ - conn = xmpp_conn_new(ctx); - - /* configure connection properties (optional) */ - xmpp_conn_set_flags(conn, flags); - - /* setup authentication information */ - xmpp_conn_set_jid(conn, jid); - xmpp_conn_set_pass(conn, pass); - - /* initiate connection */ - xmpp_connect_client(conn, host, 0, conn_handler, ctx); - - /* enter the event loop - - our connect handler will trigger an exit */ - xmpp_run(ctx); - - /* release our connection and context */ - xmpp_conn_release(conn); - xmpp_ctx_free(ctx); - - /* final shutdown of the library */ - xmpp_shutdown(); - - return 0; -} diff --git a/source/main.c b/source/main.c new file mode 100644 index 0000000..3b3b853 --- /dev/null +++ b/source/main.c @@ -0,0 +1,102 @@ +/* + @brief Main file for ACC + @author Michael D. Lowis + @license BSD 2-Clause License +*/ +#include "autil.h" +#include "ini.h" +#include "strophe.h" + +char* ARGV0 = NULL; +char* User = NULL; +char* Pass = NULL; +char* Resource = NULL; +char* Alias = NULL; +char* Server = NULL; +char* FileProxy = NULL; +int Port = 5222; +long Flags = 0; + +static void usage(void); +static void load_config(void); +void conn_handler(xmpp_conn_t * const conn, const xmpp_conn_event_t status, + const int error, xmpp_stream_error_t * const stream_error, + void * const userdata); + +int main(int argc, char** argv) +{ + /* Parse command-line options */ + OPTBEGIN { + default: + usage(); + } OPTEND; + /* Parse config file if it exists */ + load_config(); + /* Start the connection */ + xmpp_initialize(); + xmpp_log_t* log = xmpp_get_default_logger(XMPP_LEVEL_DEBUG); + xmpp_ctx_t* ctx = xmpp_ctx_new(NULL, log); + xmpp_conn_t* conn = xmpp_conn_new(ctx); + /* setup authentication information and connect */ + xmpp_conn_set_flags(conn, Flags); + xmpp_conn_set_jid(conn, User); + xmpp_conn_set_pass(conn, Pass); + xmpp_connect_client(conn, Server, 0, conn_handler, ctx); + /* enter the event loop our connect handler will trigger an exit */ + xmpp_run(ctx); + /* gracefully shut everything down */ + xmpp_conn_release(conn); + xmpp_ctx_free(ctx); + xmpp_shutdown(); + return 0; +} + +static void usage(void) +{ + fprintf(stderr, "Usage: %s [options] PROFILE\n", ARGV0); + exit(1); +} + +static void load_config(void) +{ + inientry_t entry = { 0 }; + inifile_t cfgfile = { .file = fopen("./config.ini","r") }; + while (iniparse(&cfgfile, &entry)) { + //printf("[%s] '%s' = '%s'\n", entry.section, entry.name, entry.value); + if (inimatch("user",&entry)) + User = estrdup(entry.value); + else if (inimatch("pass",&entry)) + Pass = estrdup(entry.value); + else if (inimatch("resource",&entry)) + Resource = estrdup(entry.value); + else if (inimatch("alias",&entry)) + Alias = estrdup(entry.value); + else if (inimatch("port",&entry)) + Port = strtoul(entry.value,NULL,0); + else if (inimatch("server",&entry)) + Server = estrdup(entry.value); + else if (inimatch("fileproxy",&entry)) + FileProxy = estrdup(entry.value); + } +} + +void conn_handler(xmpp_conn_t * const conn, const xmpp_conn_event_t status, + const int error, xmpp_stream_error_t * const stream_error, + void * const userdata) +{ + xmpp_ctx_t *ctx = (xmpp_ctx_t *)userdata; + int secured; + + if (status == XMPP_CONN_CONNECT) { + fprintf(stderr, "DEBUG: connected\n"); + secured = xmpp_conn_is_secured(conn); + fprintf(stderr, "DEBUG: connection is %s.\n", + secured ? "secured" : "NOT secured"); + xmpp_disconnect(conn); + } + else { + fprintf(stderr, "DEBUG: disconnected\n"); + xmpp_stop(ctx); + } +} +