]> git.mdlowis.com Git - archive/acc.git/commitdiff
Added ini config file parsing for connection info
authorMike Lowis <mike.lowis@gentex.com>
Thu, 3 Mar 2016 18:03:19 +0000 (13:03 -0500)
committerMike Lowis <mike.lowis@gentex.com>
Thu, 3 Mar 2016 18:03:19 +0000 (13:03 -0500)
Makefile
include/autil.h [new file with mode: 0644]
include/ini.h [new file with mode: 0644]
source/basic.c [deleted file]
source/main.c [new file with mode: 0644]

index 54f7b5cf7d71f69924dbdf50e9e177b7ca556b9c..ce98623d7a7f7375de14acfbdf0e73b40d2e7c10 100644 (file)
--- 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 (file)
index 0000000..d5689ce
--- /dev/null
@@ -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 <stddef.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <limits.h>
+#include <assert.h>
+
+/* Useful Standard Functions */
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+
+/* 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 (file)
index 0000000..cd5e1df
--- /dev/null
@@ -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 <stdio.h>
+#include <stdbool.h>
+#include <string.h>
+#include <ctype.h>
+
+#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 (file)
index 652d860..0000000
+++ /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 <stdio.h>
-#include <string.h>
-
-#include <strophe.h>
-
-
-/* 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] <jid> <pass> [<host>]\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 (file)
index 0000000..3b3b853
--- /dev/null
@@ -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);
+    }
+}
+