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 $@ $<
#------------------------------------------------------------------------------
# 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 \
--- /dev/null
+/**
+ @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
--- /dev/null
+/**
+ @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 */
+++ /dev/null
-/* 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;
-}
--- /dev/null
+/*
+ @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);
+ }
+}
+