diff --git a/makefile b/makefile index 66f239c..3f37bc6 100644 --- a/makefile +++ b/makefile @@ -1,17 +1,18 @@ -SRC = src/main.c +SRC = src/bot/handlers.c src/bot/context.c src/main.c LOCPATH = ./bin INSTALLPATH = /usr/local -CFLAGS = -O2 -LFLAGS = -lstrophe +CFLAGS = `pkg-config --cflags glib-2.0` +LFLAGS = -lstrophe `pkg-config --libs glib-2.0` DEBFLAGS = -g -Wall +OFLAGS = -O2 NAME = xmpp-bot all: mkdir -p $(LOCPATH) - gcc $(SRC) -o $(LOCPATH)/$(NAME) $(CFLAGS) $(LFLAGS) $(LFLAGS_STATUS) + gcc $(SRC) -o $(LOCPATH)/$(NAME) $(CFLAGS) $(OFLAGS) $(LFLAGS) $(LFLAGS_STATUS) nostatus: mkdir -p $(LOCPATH) - gcc $(SRC) -o $(LOCPATH)/$(NAME) -O2 $(LFLAGS) + gcc $(SRC) -o $(LOCPATH)/$(NAME) $(OFLAGS) $(LFLAGS) debug: mkdir -p $(LOCPATH)/debug gcc $(SRC) -o $(LOCPATH)/debug/$(NAME)_debug $(CFLAGS) $(LFLAGS) $(DEBFLAGS) $(LFLAGS_STATUS) diff --git a/src/bot/context.c b/src/bot/context.c new file mode 100644 index 0000000..dfc6946 --- /dev/null +++ b/src/bot/context.c @@ -0,0 +1,62 @@ +/* bot/context.c + */ + +#include "context.h" + +int bot_xmpp_startup(struct BotUserInfo user, struct BotXmppCtx *bctx) +{ + xmpp_log_t* newLog; + xmpp_ctx_t* newCtx; + xmpp_conn_t* newCon; + int connectStatus; + + /* init library */ + xmpp_initialize(); + + /* create a context */ + newLog = xmpp_get_default_logger(XMPP_LEVEL_DEBUG); /* pass NULL instead to silence output */ + newCtx = xmpp_ctx_new(NULL, newLog); + + if (!(newLog && newCtx)) { + fprintf(stderr, "Unable to startup. Context creation failed."); + return 1; + } + + /* create a connection */ + newCon = xmpp_conn_new(newCtx); + + if (!newCon) { + fprintf(stderr, "Unable to startup. Connection failed."); + return 1; + } + + /* setup authentication information */ + xmpp_conn_set_jid(newCon, user.jid->str); + xmpp_conn_set_pass(newCon, user.pass->str); + + /* initiate connection */ + connectStatus = xmpp_connect_client(newCon, NULL, 0, conn_handler, newCtx); + if(connectStatus != 0) { + /* connecting went wrong */ + return connectStatus; + } + + (*bctx).log = newLog; + (*bctx).ctx = newCtx; + (*bctx).conn = newCon; + return 0; +} + +void bot_xmpp_shutdown(struct BotXmppCtx *bctx) +{ + /* enter the event loop - + our connect handler will trigger an exit */ + xmpp_run((*bctx).ctx); + + /* release our connection and context */ + xmpp_conn_release((*bctx).conn); + xmpp_ctx_free((*bctx).ctx); + + /* final shutdown of the library */ + xmpp_shutdown(); +} diff --git a/src/bot/context.h b/src/bot/context.h new file mode 100644 index 0000000..a312574 --- /dev/null +++ b/src/bot/context.h @@ -0,0 +1,31 @@ +/* bot/context.h + * --- + * Implement XMPP connection startup, shutdown, context and user data + * structures. + * --- + */ + +#pragma once + +#include +#include +#include +#include +#include "handlers.h" + +struct BotUserInfo { + GString* jid; + GString* pass; +}; + +struct BotXmppCtx { + xmpp_log_t* log; + xmpp_ctx_t* ctx; + xmpp_conn_t* conn; +}; + +/* perform xmpp startup, store in bctx if completed successfully */ +int bot_xmpp_startup(struct BotUserInfo user, struct BotXmppCtx *bctx); + +/* teardown and free ctx and connection in bctx */ +void bot_xmpp_shutdown(struct BotXmppCtx *bctx); diff --git a/src/bot/handlers.c b/src/bot/handlers.c new file mode 100644 index 0000000..db6948b --- /dev/null +++ b/src/bot/handlers.c @@ -0,0 +1,108 @@ +/* bot/handlers.c + */ +#include "handlers.h" + +int version_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void * const userdata) +{ + xmpp_stanza_t *reply, *query, *name, *version, *text; + const char *ns; + xmpp_ctx_t *ctx = (xmpp_ctx_t*)userdata; + printf("Received version request from %s\n", xmpp_stanza_get_attribute(stanza, "from")); + + reply = xmpp_stanza_new(ctx); + xmpp_stanza_set_name(reply, "iq"); + xmpp_stanza_set_type(reply, "result"); + xmpp_stanza_set_id(reply, xmpp_stanza_get_id(stanza)); + xmpp_stanza_set_attribute(reply, "to", xmpp_stanza_get_attribute(stanza, "from")); + + query = xmpp_stanza_new(ctx); + xmpp_stanza_set_name(query, "query"); + ns = xmpp_stanza_get_ns(xmpp_stanza_get_children(stanza)); + if (ns) { + xmpp_stanza_set_ns(query, ns); + } + + name = xmpp_stanza_new(ctx); + xmpp_stanza_set_name(name, "name"); + xmpp_stanza_add_child(query, name); + + text = xmpp_stanza_new(ctx); + xmpp_stanza_set_text(text, "libstrophe example bot"); + xmpp_stanza_add_child(name, text); + + version = xmpp_stanza_new(ctx); + xmpp_stanza_set_name(version, "version"); + xmpp_stanza_add_child(query, version); + + text = xmpp_stanza_new(ctx); + xmpp_stanza_set_text(text, "1.0"); + xmpp_stanza_add_child(version, text); + + xmpp_stanza_add_child(reply, query); + + xmpp_send(conn, reply); + xmpp_stanza_release(reply); + return 1; +} + + +int message_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void * const userdata) +{ + xmpp_stanza_t *reply, *body, *text; + char *intext, *replytext; + xmpp_ctx_t *ctx = (xmpp_ctx_t*)userdata; + + if(!xmpp_stanza_get_child_by_name(stanza, "body")) return 1; + if(!strcmp(xmpp_stanza_get_attribute(stanza, "type"), "error")) return 1; + + intext = xmpp_stanza_get_text(xmpp_stanza_get_child_by_name(stanza, "body")); + + printf("Incoming message from %s: %s\n", xmpp_stanza_get_attribute(stanza, "from"), intext); + + reply = xmpp_stanza_new(ctx); + xmpp_stanza_set_name(reply, "message"); + xmpp_stanza_set_type(reply, xmpp_stanza_get_type(stanza)?xmpp_stanza_get_type(stanza):"chat"); + xmpp_stanza_set_attribute(reply, "to", xmpp_stanza_get_attribute(stanza, "from")); + + body = xmpp_stanza_new(ctx); + xmpp_stanza_set_name(body, "body"); + + replytext = malloc(strlen(" to you too!") + strlen(intext) + 1); + strcpy(replytext, intext); + strcat(replytext, " to you too!"); + + text = xmpp_stanza_new(ctx); + xmpp_stanza_set_text(text, replytext); + xmpp_stanza_add_child(body, text); + xmpp_stanza_add_child(reply, body); + + xmpp_send(conn, reply); + xmpp_stanza_release(reply); + free(replytext); + return 1; +} + +/* 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; + + if (status == XMPP_CONN_CONNECT) { + xmpp_stanza_t* pres; + fprintf(stderr, "DEBUG: connected\n"); + xmpp_handler_add(conn,version_handler, "jabber:iq:version", "iq", NULL, ctx); + xmpp_handler_add(conn,message_handler, NULL, "message", NULL, ctx); + + /* Send initial so that we appear online to contacts */ + pres = xmpp_stanza_new(ctx); + xmpp_stanza_set_name(pres, "presence"); + xmpp_send(conn, pres); + xmpp_stanza_release(pres); + } + else { + fprintf(stderr, "DEBUG: disconnected\n"); + xmpp_stop(ctx); + } +} diff --git a/src/bot/handlers.h b/src/bot/handlers.h new file mode 100644 index 0000000..1ebbdcb --- /dev/null +++ b/src/bot/handlers.h @@ -0,0 +1,13 @@ +#include +#include +#include +#include + +/* respond to version queries */ +int version_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void * const userdata); + +/* react and respond to incoming messages */ +int message_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void * const userdata); + +/* 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); diff --git a/src/main.c b/src/main.c index 73403d4..6650ad5 100644 --- a/src/main.c +++ b/src/main.c @@ -1,179 +1,47 @@ -/* bot.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 software is distributed under license and may not be copied, -** modified or distributed except as expressly authorized under the -** terms of the license contained in the file LICENSE.txt in this -** distribution. -*/ - -/* simple bot example -** -** This example was provided by Matthew Wild . -** -** This bot responds to basic messages and iq version requests. +/* main.c */ #include #include -#include - -#include - - -int version_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void * const userdata) -{ - xmpp_stanza_t *reply, *query, *name, *version, *text; - const char *ns; - xmpp_ctx_t *ctx = (xmpp_ctx_t*)userdata; - printf("Received version request from %s\n", xmpp_stanza_get_attribute(stanza, "from")); - - reply = xmpp_stanza_new(ctx); - xmpp_stanza_set_name(reply, "iq"); - xmpp_stanza_set_type(reply, "result"); - xmpp_stanza_set_id(reply, xmpp_stanza_get_id(stanza)); - xmpp_stanza_set_attribute(reply, "to", xmpp_stanza_get_attribute(stanza, "from")); - - query = xmpp_stanza_new(ctx); - xmpp_stanza_set_name(query, "query"); - ns = xmpp_stanza_get_ns(xmpp_stanza_get_children(stanza)); - if (ns) { - xmpp_stanza_set_ns(query, ns); - } - - name = xmpp_stanza_new(ctx); - xmpp_stanza_set_name(name, "name"); - xmpp_stanza_add_child(query, name); - - text = xmpp_stanza_new(ctx); - xmpp_stanza_set_text(text, "libstrophe example bot"); - xmpp_stanza_add_child(name, text); - - version = xmpp_stanza_new(ctx); - xmpp_stanza_set_name(version, "version"); - xmpp_stanza_add_child(query, version); - - text = xmpp_stanza_new(ctx); - xmpp_stanza_set_text(text, "1.0"); - xmpp_stanza_add_child(version, text); - - xmpp_stanza_add_child(reply, query); - - xmpp_send(conn, reply); - xmpp_stanza_release(reply); - return 1; -} - - -int message_handler(xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void * const userdata) -{ - xmpp_stanza_t *reply, *body, *text; - char *intext, *replytext; - xmpp_ctx_t *ctx = (xmpp_ctx_t*)userdata; - - if(!xmpp_stanza_get_child_by_name(stanza, "body")) return 1; - if(!strcmp(xmpp_stanza_get_attribute(stanza, "type"), "error")) return 1; - - intext = xmpp_stanza_get_text(xmpp_stanza_get_child_by_name(stanza, "body")); - - printf("Incoming message from %s: %s\n", xmpp_stanza_get_attribute(stanza, "from"), intext); - - reply = xmpp_stanza_new(ctx); - xmpp_stanza_set_name(reply, "message"); - xmpp_stanza_set_type(reply, xmpp_stanza_get_type(stanza)?xmpp_stanza_get_type(stanza):"chat"); - xmpp_stanza_set_attribute(reply, "to", xmpp_stanza_get_attribute(stanza, "from")); - - body = xmpp_stanza_new(ctx); - xmpp_stanza_set_name(body, "body"); - - replytext = malloc(strlen(" to you too!") + strlen(intext) + 1); - strcpy(replytext, intext); - strcat(replytext, " to you too!"); - - text = xmpp_stanza_new(ctx); - xmpp_stanza_set_text(text, replytext); - xmpp_stanza_add_child(body, text); - xmpp_stanza_add_child(reply, body); - - xmpp_send(conn, reply); - xmpp_stanza_release(reply); - free(replytext); - return 1; -} - -/* 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; - - if (status == XMPP_CONN_CONNECT) { - xmpp_stanza_t* pres; - fprintf(stderr, "DEBUG: connected\n"); - xmpp_handler_add(conn,version_handler, "jabber:iq:version", "iq", NULL, ctx); - xmpp_handler_add(conn,message_handler, NULL, "message", NULL, ctx); - - /* Send initial so that we appear online to contacts */ - pres = xmpp_stanza_new(ctx); - xmpp_stanza_set_name(pres, "presence"); - xmpp_send(conn, pres); - xmpp_stanza_release(pres); - } - else { - fprintf(stderr, "DEBUG: disconnected\n"); - xmpp_stop(ctx); - } -} +#include "bot/context.h" int main(int argc, char **argv) { xmpp_ctx_t *ctx; xmpp_conn_t *conn; xmpp_log_t *log; - char *jid, *pass; + struct BotUserInfo user; + struct BotXmppCtx bctx; + int startup_code; /* take a jid and password on the command line */ if (argc != 3) { fprintf(stderr, "Usage: bot \n\n"); return 1; } + + if (!g_utf8_validate(argv[1], -1, NULL) || !g_utf8_validate(argv[2], -1, NULL)) { + fprintf(stderr, " and should be valid UTF-8 strings.\n\n"); + return 1; + } - jid = argv[1]; - pass = argv[2]; + user.jid = g_string_new(argv[1]); + user.pass = g_string_new(argv[2]); - /* 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); - - /* setup authentication information */ - xmpp_conn_set_jid(conn, jid); - xmpp_conn_set_pass(conn, pass); - - /* initiate connection */ - xmpp_connect_client(conn, NULL, 0, conn_handler, ctx); + /* perform the startup and connect routine */ + startup_code = bot_xmpp_startup(user, &bctx); + if(startup_code != 0) { + /* something went wrong at startup */ + return startup_code; + } /* enter the event loop - our connect handler will trigger an exit */ - xmpp_run(ctx); + xmpp_run(bctx.ctx); /* release our connection and context */ - xmpp_conn_release(conn); - xmpp_ctx_free(ctx); - /* final shutdown of the library */ - xmpp_shutdown(); + bot_xmpp_shutdown(&bctx); return 0; }