diff options
author | Anton Luka Šijanec <anton@sijanec.eu> | 2022-04-30 02:10:29 +0200 |
---|---|---|
committer | Anton Luka Šijanec <anton@sijanec.eu> | 2022-04-30 02:10:29 +0200 |
commit | 743086cf4e69c77e139c23defaf63bfa403c1678 (patch) | |
tree | 752cb5c0427ea9ea306ee200f0b1148a8fa33ab2 /ircxmpp.c | |
parent | oh, looh, security issues already (diff) | |
download | ircxmpp-743086cf4e69c77e139c23defaf63bfa403c1678.tar ircxmpp-743086cf4e69c77e139c23defaf63bfa403c1678.tar.gz ircxmpp-743086cf4e69c77e139c23defaf63bfa403c1678.tar.bz2 ircxmpp-743086cf4e69c77e139c23defaf63bfa403c1678.tar.lz ircxmpp-743086cf4e69c77e139c23defaf63bfa403c1678.tar.xz ircxmpp-743086cf4e69c77e139c23defaf63bfa403c1678.tar.zst ircxmpp-743086cf4e69c77e139c23defaf63bfa403c1678.zip |
Diffstat (limited to '')
-rw-r--r-- | ircxmpp.c (renamed from main.c) | 415 | ||||
-rw-r--r-- | ircxmpp.conf | 10 |
2 files changed, 261 insertions, 164 deletions
@@ -1,14 +1,26 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> -#include <strophe.h> #include <ctype.h> #include <time.h> #include <sys/select.h> #include <errno.h> #include <signal.h> +#include <stdarg.h> +#include <search.h> #include "ircxmpp.h" -void free_bridge (struct bridge ** bridge, const char * razlog) { +#ifdef IX_LIB +#define BUG(...) bug(__FILE__, __func__, __LINE__, __VA_ARGS__) +__attribute__((noreturn)) static void bug (const char * file, const char * function, int line, const char * format, ...) { + va_list ap; + va_start(ap, format); + fprintf(stderr, "BUG DETECTED in %s()@%s:%d REPORT TO anton@šijanec.eu and attach core.\n" + "additional description: ", file, function, line); + vfprintf(stderr, format, ap); + abort(); + va_end(ap); /* this is never called */ +} +static void free_bridge (struct bridge ** bridge, const char * razlog) { if (!bridge || !*bridge) return; fprintf(stderr, "freeing bridge with reason: %s\n", razlog); @@ -18,7 +30,7 @@ void free_bridge (struct bridge ** bridge, const char * razlog) { irc_destroy_session((*bridge)->irc); } if ((*bridge)->conn) - xmpp_conn_release((*bridge)->conn); /* graceful disconnect, what is that? */ + xmpp_conn_release((*bridge)->conn); // graceful disconnect, what is that? free((*bridge)->identifier); for (size_t i = 0; i < (*bridge)->messages_length; i++) free((*bridge)->messages[i]); @@ -28,26 +40,32 @@ void free_bridge (struct bridge ** bridge, const char * razlog) { struct bridge * tofree = *bridge; if ((*bridge)->prev) (*bridge)->prev->next = (*bridge)->next; - else + else { + if ((*bridge)->next) + (*bridge)->next->prev = (*bridge)->prev; *bridge = (*bridge)->next; + } free(tofree); } -void free_bridges (struct bridge ** bridges) { - while (*bridges) // enkrat bo *bridges NULL, ker ne bo nobenega več notri +static void free_bridges (struct bridge ** bridges) { + while (*bridges) { // enkrat bo *bridges NULL, ker ne bo nobenega več notri + if ((*bridges)->prev) + BUG("(*bridges)->prev is set"); free_bridge(bridges, "vsi mostovi se podirajo, ker se ircxmpp izklaplja"); + } } -struct bridge ** find_bridge (struct bridge ** bridges, const char * identifier, enum side side) { +static struct bridge ** find_bridge (struct bridge ** bridges, const char * id, enum side side) { s: if (!bridges || !*bridges) return NULL; - if ((*bridges)->side == side && !strcmp((*bridges)->identifier, identifier)) + if ((*bridges)->side == side && !strcmp((*bridges)->identifier, id)) return bridges; bridges = &((*bridges)->next); goto s; } -void jid2ircnick (char * jid) { /* edits a jid into an irc-compatible nick. libera trims nicks. */ +static void jid2ircnick (char * jid) { /* edits a jid into an irc nick. libera trims nicks. */ char * c = strchr(jid, '/'); if (c) { c++; @@ -60,7 +78,7 @@ void jid2ircnick (char * jid) { /* edits a jid into an irc-compatible nick. libe if (!(isalnum(*jid) || memchr(VALID_NICK, *jid, strlen(VALID_NICK)))) *jid = '_'; } -void jid2ircuser (char * jid) { +static void jid2ircuser (char * jid) { char * c = strchr(jid, '/'); if (c) { c++; @@ -74,12 +92,12 @@ void jid2ircuser (char * jid) { if (!(isalnum(*jid) || memchr(VALID_USER, *jid, strlen(VALID_USER)))) *jid = 'x'; } -void bridge_forward (const char * from, const char * msg, struct ircxmpp * ircxmpp, enum side side) { - struct bridge ** bridge_resp = find_bridge(&ircxmpp->bridges, from, !side); - if (strstr(from, "ircxmpp_") || (ircxmpp->irchost && strstr(from, ircxmpp->irchost))) +static void bridge_forward (const char * f, const char * m, struct ircxmpp * ircxmpp, enum side s) { + struct bridge ** bridge_resp = find_bridge(&ircxmpp->bridges, f, !s); + if (strstr(f, "ircxmpp_") || (ircxmpp->irchost && strstr(f, ircxmpp->irchost))) return; - fprintf(stderr, "sending text from %s to %s: %s\n", from, side == IRC ? "IRC" : "XMPP", - msg ? msg : "[join only]"); + fprintf(stderr, "sending text from %s to %s: %s\n", f, s == IRC ? "IRC" : "XMPP", + m ? m : "[join only]"); struct bridge * bridge; if (!bridge_resp) { bridge = calloc(1, sizeof(struct bridge)); @@ -87,10 +105,10 @@ void bridge_forward (const char * from, const char * msg, struct ircxmpp * ircxm ircxmpp->bridges->prev = bridge; bridge->next = ircxmpp->bridges; ircxmpp->bridges = bridge; - bridge->identifier = strdup(from); + bridge->identifier = strdup(f); bridge->ircxmpp = ircxmpp; - bridge->side = !side; - if (side == IRC) + bridge->side = !s; + if (s == IRC) init_irc(bridge); else { bridge->conn = xmpp_conn_new(bridge->ircxmpp->ctx); @@ -100,22 +118,30 @@ void bridge_forward (const char * from, const char * msg, struct ircxmpp * ircxm } } else bridge = *bridge_resp; - if (side == IRC) { + if (s == IRC) { irc_cmd_join(bridge->irc, ircxmpp->channel, ircxmpp->channel_password); irc_run_once(bridge); - if (msg) - irc_cmd_msg(bridge->irc, ircxmpp->channel, msg); + if (m) { + char b[1024]; + strncpy(b, m, 1023); + b[1023] = '\0'; + char * c = b; + while (*++c != '\0') + if (!(isprint(*c) || * (unsigned char *) c > 0x7F /* UTF-8 */)) + *c = ' '; /* to prevent newline message smuggling */ + irc_cmd_msg(bridge->irc, ircxmpp->channel, m); + } irc_run_once(bridge); - } else if (msg) { + } else if (m) { bridge->messages = realloc(bridge->messages, sizeof(*bridge->messages) * (bridge->messages_length+1)); - bridge->messages[bridge->messages_length++] = strdup(msg); + bridge->messages[bridge->messages_length++] = strdup(m); } -} /* msg can be NULL, in that case we only join. */ -int message_handler (xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void * const userdata) { - if (!conn) /* just to get rid of -Wunused-parameter */ +} /* m can be NULL, in that case we only join. */ +static int message_handler (xmpp_conn_t * const c, xmpp_stanza_t * const stanza, void * const ud) { + if (!c) /* just to get rid of -Wunused-parameter */ return 1; - struct ircxmpp * ircxmpp = (struct ircxmpp *) userdata; + struct ircxmpp * ircxmpp = (struct ircxmpp *) ud; xmpp_stanza_t * body; const char * type; char * intext; @@ -135,9 +161,9 @@ int message_handler (xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, voi xmpp_free(ircxmpp->ctx, intext); return 1; } -int presence_handler (xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, void * const ud) { +static int presence_handler (xmpp_conn_t * const c, xmpp_stanza_t * const stanza, void * const ud) { struct ircxmpp * ircxmpp = (struct ircxmpp *) ud; - if (!conn || !xmpp_stanza_get_from(stanza) || !strchr(xmpp_stanza_get_from(stanza), '/')) + if (!c || !xmpp_stanza_get_from(stanza) || !strchr(xmpp_stanza_get_from(stanza), '/')) return 1; if (xmpp_stanza_get_type(stanza) && !strcmp("unavailable", xmpp_stanza_get_type(stanza))) { fprintf(stderr, "sogovornik %s je zapustil MUC\n", xmpp_stanza_get_from(stanza)); @@ -151,8 +177,8 @@ int presence_handler (xmpp_conn_t * const conn, xmpp_stanza_t * const stanza, vo return 1; } -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) { +static 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) { struct ircxmpp * ircxmpp = (struct ircxmpp *) userdata; if (stream_error) /* just for -Wunused-parameter */ fprintf(stderr, "stream_error in conn_handler, error = %d\n", error); @@ -181,8 +207,8 @@ void conn_handler (xmpp_conn_t * const conn, const xmpp_conn_event_t status, con // xmpp_stop(ircxmpp->ctx); } } -void conn_handler_bridge (xmpp_conn_t * const conn, const xmpp_conn_event_t status, const int error, - xmpp_stream_error_t * const stream_error, void * const userdata) { +static void conn_handler_bridge (xmpp_conn_t * const conn, const xmpp_conn_event_t status, + const int error, xmpp_stream_error_t * const stream_error, void * const userdata) { struct bridge * bridge = (struct bridge *) userdata; if (stream_error) /* just for -Wunused-parameter */ fprintf(stderr, "stream_error in conn_handler, error = %d\n", error); @@ -202,27 +228,6 @@ void conn_handler_bridge (xmpp_conn_t * const conn, const xmpp_conn_event_t stat xmpp_stanza_release(x); xmpp_send(bridge->conn, pres); xmpp_stanza_release(pres); - /* xmpp_stanza_t * pres; - fprintf(stderr, "DEBUG: connected.\n"); - xmpp_handler_add(conn, message_handler, NULL, "message", NULL, bridge); - xmpp_handler_add(conn, presence_handler, NULL, "presence", NULL, bridge); - // Send initial <presence/> so that we appear online to contacts - // pres = xmpp_presence_new(ctx); // somehow send can only be called once? - // xmpp_send(conn, pres); - // xmpp_stanza_release(pres); - pres = xmpp_presence_new(bridge->ircxmpp->ctx); // joining a MUC - char b[512]; - bridge->identifier(); - snprintf(b, 512, "%s/ircxmpp_%x", ircxmpp->muc, rand()); - xmpp_stanza_set_to(pres, b); - xmpp_stanza_t * x = xmpp_stanza_new(ircxmpp->ctx); - xmpp_stanza_set_name(x, "x"); - xmpp_stanza_set_ns(x, "http://jabber.org/protocol/muc"); - xmpp_stanza_add_child(pres, x); - xmpp_stanza_release(x); - xmpp_send(conn, pres); - xmpp_stanza_release(pres); - */ } else { fprintf(stderr, "DEBUG: disconnected\n"); // xmpp_stop(ircxmpp->ctx); @@ -231,7 +236,8 @@ void conn_handler_bridge (xmpp_conn_t * const conn, const xmpp_conn_event_t stat } /* IRC */ -void dump_event (irc_session_t * s, const char * e, const char * o, const char ** p, unsigned c) { +static void dump_event ( + irc_session_t * s, const char * e, const char * o, const char ** p, unsigned c) { if (!s) /* just for -Wunused-parameter */ return; fprintf(stderr, "Event %s, origin %s, params %d [", e, o ? o : "NULL", c); @@ -242,40 +248,42 @@ void dump_event (irc_session_t * s, const char * e, const char * o, const char * } fprintf(stderr, "]\n"); } -void event_connect (irc_session_t * s, const char * e, const char * o, const char ** p, unsigned c) { +static void event_connect ( + irc_session_t * s, const char * e, const char * o, const char ** p, unsigned c) { dump_event(s, e, o, p, c); struct bridge * bridge = (struct bridge *) irc_get_ctx(s); irc_cmd_join(s, bridge->ircxmpp->channel, bridge->ircxmpp->channel_password); } -void event_connect_control (irc_session_t * s, const char * e, const char * o, const char ** p, - unsigned c) { +static void event_connect_control ( + irc_session_t * s, const char * e, const char * o, const char ** p, unsigned c) { dump_event(s, e, o, p, c); struct ircxmpp * ircxmpp = (struct ircxmpp *) irc_get_ctx(s); irc_cmd_join(s, ircxmpp->channel, ircxmpp->channel_password); } -void event_privmsg (irc_session_t * s, const char * e, const char * o, const char ** p, unsigned c) { +static void event_privmsg ( + irc_session_t * s, const char * e, const char * o, const char ** p, unsigned c) { dump_event(s, e, o, p, c); /* SAME FOR _control. do NOT use irc_get_ctx here!!! */ char nickbuf[512]; irc_target_get_nick(o, nickbuf, sizeof(nickbuf)); irc_cmd_msg(s, nickbuf, "ircxmpp (še) ne podpira zasebnih sporočil xmpp uporabnikom. ircxmpp se razvija na http://git.sijanec.eu/sijanec/ircxmpp/."); } -void event_partquit_control (irc_session_t * s, const char * e, const char * o, const char ** p, - unsigned c) { +static void event_partquit_control ( + irc_session_t * s, const char * e, const char * o, const char ** p, unsigned c) { dump_event(s, e, o, p, c); struct ircxmpp * ircxmpp = (struct ircxmpp *) irc_get_ctx(s); struct bridge ** bridge = find_bridge(&ircxmpp->bridges, o /* indeed n!u@h */, IRC); free_bridge(bridge, "part or quit from irc"); } /* part and quit events */ -void event_channel_control (irc_session_t * s, const char * e, const char * o, const char ** p, - unsigned c) { +static void event_channel_control ( + irc_session_t * s, const char * e, const char * o, const char ** p, unsigned c) { dump_event(s, e, o, p, c); /* o je avtor, p[0] je kanal p[1] je besedilo */ if (c != 2) /* no message text */ return; struct ircxmpp * ircxmpp = (struct ircxmpp *) irc_get_ctx(s); bridge_forward(o /* indeed n!u@h */, p[1], ircxmpp, XMPP); } -void event_join_control (irc_session_t * s, const char * e, const char * o, const char ** p, - unsigned c) { +static void event_join_control ( + irc_session_t * s, const char * e, const char * o, const char ** p, unsigned c) { dump_event(s, e, o, p, c); struct ircxmpp * ircxmpp = (struct ircxmpp *) irc_get_ctx(s); char buf[512]; @@ -298,8 +306,8 @@ void event_join_control (irc_session_t * s, const char * e, const char * o, cons } bridge_forward(o /* indeed n!u@h */, NULL, ircxmpp, XMPP); } -void event_nick_control (irc_session_t * s, const char * e, const char * o, const char ** p, - unsigned c) { +static void event_nick_control ( + irc_session_t * s, const char * e, const char * o, const char ** p, unsigned c) { dump_event(s, e, o, p, c); /* o je originalen nick, p[0] je nov nick */ if (!c) return; @@ -313,15 +321,16 @@ void event_nick_control (irc_session_t * s, const char * e, const char * o, cons : "neznan uporabnik@neznan strežnik"); bridge_forward(buf, NULL, ircxmpp, XMPP); /* and now connect */ } -void event_topic_control (irc_session_t * s, const char * e, const char * o, const char ** p, - unsigned c) { +static void event_topic_control ( + irc_session_t * s, const char * e, const char * o, const char ** p, unsigned c) { dump_event(s, e, o, p, c); /* o je avtor, p[0] je kanal, p[1] je nova tema/zadeva */ struct ircxmpp * ircxmpp = (struct ircxmpp *) irc_get_ctx(s); char buf[1024]; snprintf(buf, 1024, "/me je nastavil IRC temo na: %s", p[1]); bridge_forward(o, buf, ircxmpp, XMPP); } /* TODO */ -void event_numeric (irc_session_t * s, unsigned int e, const char * o, const char ** p, unsigned c) { +static void event_numeric ( + irc_session_t * s, unsigned int e, const char * o, const char ** p, unsigned c) { char b[512]; struct bridge * bridge = (struct bridge *) irc_get_ctx(s); sprintf(b, "%d", e); @@ -342,8 +351,8 @@ void event_numeric (irc_session_t * s, unsigned int e, const char * o, const cha break; } } -void event_numeric_control (irc_session_t * s, unsigned int e, const char * o, const char ** p, - unsigned c) { +static void event_numeric_control ( + irc_session_t * s, unsigned int e, const char * o, const char ** p, unsigned c) { char b[512]; struct ircxmpp * ircxmpp = (struct ircxmpp *) irc_get_ctx(s); sprintf(b, "%d", e); @@ -360,7 +369,7 @@ void event_numeric_control (irc_session_t * s, unsigned int e, const char * o, c break; } } -int irc_run_once (struct bridge * bridge) { /* returns nonzero if connection failed (dropped brid) */ +static int irc_run_once (struct bridge * bridge) { /* returns nonzero if connection failed */ if (!irc_is_connected(bridge->irc)) { char b[512], u[512]; strncpy(b, bridge->identifier, sizeof(b)-1); @@ -400,7 +409,9 @@ int irc_run_once (struct bridge * bridge) { /* returns nonzero if connection fai } return 0; } -int irc_run_once_control (struct ircxmpp * ircxmpp) { /* returns nonzero if failed (dropped conne) */ +static int irc_run_once_control (struct ircxmpp * ircxmpp) { /* returns nonzero if failed */ + if (!ircxmpp->irc) + return 1; if (!irc_is_connected(ircxmpp->irc)) { char b[512]; sprintf(b, "ircxmpp_%X", rand()); @@ -408,11 +419,12 @@ int irc_run_once_control (struct ircxmpp * ircxmpp) { /* returns nonzero if fail free(ircxmpp->ircnick); ircxmpp->ircnick = strdup(b); if (irc_connect(ircxmpp->irc, ircxmpp->hostname, ircxmpp->port, - NULL, b, "ircxmpp", "http://git.sijanec.eu/sijanec/ircxmpp")) { + NULL, b, "ircxmpp", "http git.sijanec.eu/sijanec/ircxmpp")) { fprintf(stderr, "could not connect: %s\n", irc_strerror(irc_errno(ircxmpp->irc))); - return 1; + return 2; } + return 0; } struct timeval tv; fd_set in_set, out_set; @@ -425,13 +437,13 @@ int irc_run_once_control (struct ircxmpp * ircxmpp) { /* returns nonzero if fail if (select(maxfd+1, &in_set, &out_set, NULL, &tv) < 0) { if (errno == EINTR) return 0; - return 1; + return 3; } if (irc_process_select_descriptors(ircxmpp->irc, &in_set, &out_set)) - return 1; + return 4; return 0; } -void init_irc (struct bridge * bridge) { +static void init_irc (struct bridge * bridge) { irc_callbacks_t callbacks; irc_session_t * s; memset(&callbacks, 0, sizeof(callbacks)); @@ -465,7 +477,7 @@ void init_irc (struct bridge * bridge) { irc_run_once(bridge); return; } -void init_irc_control (struct ircxmpp * ircxmpp) { +static void init_irc_control (struct ircxmpp * ircxmpp) { irc_callbacks_t callbacks; irc_session_t * s; memset(&callbacks, 0, sizeof(callbacks)); @@ -499,92 +511,172 @@ void init_irc_control (struct ircxmpp * ircxmpp) { irc_run_once_control(ircxmpp); return; } -/* /IRC */ /* irc_is_connected(irc_session_t * session): 1 ali 0 */ /* TODO: if nick is used */ +/* /IRC */ /* irc_is_connected(irc_session_t * session): 1 ali 0 */ +struct ircxmpp * ircxmpp_init (void) { + xmpp_initialize(); + return calloc(1, sizeof(struct ircxmpp)); +} +void ircxmpp_set_jid (struct ircxmpp * ircxmpp, const char * jid) { + free(ircxmpp->jid); + ircxmpp->jid = strdup(jid); +} +void ircxmpp_set_password (struct ircxmpp * ircxmpp, const char * password) { + free(ircxmpp->password); + ircxmpp->password = strdup(password); +} +void ircxmpp_set_hostname (struct ircxmpp * ircxmpp, const char * hostname) { + free(ircxmpp->hostname); + ircxmpp->hostname = strdup(hostname); +} +void ircxmpp_set_port (struct ircxmpp * ircxmpp, unsigned short int port) { + ircxmpp->port = port; +} +void ircxmpp_set_channel (struct ircxmpp * ircxmpp, const char * channel) { + free(ircxmpp->channel); + ircxmpp->channel = strdup(channel); +} +void ircxmpp_set_muc (struct ircxmpp * ircxmpp, const char * muc) { + free(ircxmpp->muc); + ircxmpp->muc = strdup(muc); +} +void ircxmpp_set_channel_password (struct ircxmpp * ircxmpp, const char * channel_password) { + free(ircxmpp->channel_password); + ircxmpp->channel_password = strdup(channel_password); +} +void ircxmpp_run_once (struct ircxmpp * ircxmpp) { + if (!ircxmpp->ctx || !ircxmpp->conn || (!xmpp_conn_is_connected(ircxmpp->conn) + && !xmpp_conn_is_connecting(ircxmpp->conn))) { + fprintf(stderr, "XMPP control is DISCONNECTED! CONNECTING!\n"); + if (ircxmpp->conn) + xmpp_conn_release(ircxmpp->conn); + if (ircxmpp->ctx) + xmpp_ctx_free(ircxmpp->ctx); + ircxmpp->ctx = xmpp_ctx_new(NULL, xmpp_get_default_logger(XMPP_LEVEL_DEBUG)); + ircxmpp->conn = xmpp_conn_new(ircxmpp->ctx); + xmpp_conn_set_jid(ircxmpp->conn, ircxmpp->jid); + xmpp_conn_set_pass(ircxmpp->conn, ircxmpp->password); + xmpp_connect_client(ircxmpp->conn, NULL, 0, conn_handler, ircxmpp); + } + xmpp_run_once(ircxmpp->ctx, 1); /* no need to run for bridges, as they share the ctx */ + int ret = 0; + if ((ret = irc_run_once_control(ircxmpp))) { + fprintf(stderr, "IRC control is DISCONNECTED with code %d. CONNECTING!\n", ret); + if (ircxmpp->irc) + irc_destroy_session(ircxmpp->irc); + init_irc_control(ircxmpp); + } + struct bridge ** bridge = &ircxmpp->bridges; + while (bridge && *bridge) { + struct bridge ** next = NULL; + if ((*bridge)->next) + next = &((*bridge)->next); + if ((*bridge)->irc && irc_run_once(*bridge)) { + free_bridge(bridge, "irc connection dropped"); + goto cont; + } + if ((*bridge)->conn && !xmpp_conn_is_connected((*bridge)->conn) + && !xmpp_conn_is_connecting(((*bridge)->conn))) { + if ((*bridge)->side == IRC && (*bridge)->messages_length) { + fprintf(stderr, "RECONNECTING BRIDGE BECAUSE IT DIED and has msgs!"); + xmpp_connect_client((*bridge)->conn, NULL, 0, + conn_handler_bridge, *bridge); + } else + free_bridge(bridge, "xmpp connection dropped"); + goto cont; + } + while ((*bridge)->conn && xmpp_conn_is_connected((*bridge)->conn) + && (*bridge)->messages_length + && xmpp_conn_get_bound_jid((*bridge)->conn)) { + char id[64]; + fprintf(stderr, "sending xmpp msg from %s\n", (*bridge)->identifier); + sprintf(id, "ircxmpp-%x", rand()); + xmpp_stanza_t * stan = xmpp_message_new(ircxmpp->ctx, + "groupchat", ircxmpp->muc, id); + xmpp_message_set_body(stan, + (*bridge)->messages[(*bridge)->messages_length-1]); + xmpp_stanza_set_from(stan, xmpp_conn_get_bound_jid((*bridge)->conn)); + xmpp_send((*bridge)->conn, stan); + xmpp_stanza_release(stan); + free((*bridge)->messages[--(*bridge)->messages_length]); + } +cont: + bridge = next; + } +} +void ircxmpp_free (struct ircxmpp * ircxmpp) { + free_bridges(&ircxmpp->bridges); + xmpp_conn_release(ircxmpp->conn); + xmpp_ctx_free(ircxmpp->ctx); + xmpp_shutdown(); + irc_cmd_quit(ircxmpp->irc, "vsesplošni izklop programa"); + irc_run_once_control(ircxmpp); // verjetno je to potrebno, da pošlje quit + irc_destroy_session(ircxmpp->irc); + free(ircxmpp->ircnick); + free(ircxmpp->irchost); + free(ircxmpp->jid); + free(ircxmpp->password); + free(ircxmpp->hostname); + free(ircxmpp->channel); + free(ircxmpp->muc); + free(ircxmpp->channel_password); + free(ircxmpp); +} +#else int shouldexit = 0; void signalhandler (int s) { shouldexit += s+1; /* only for -Wunused-parameter */ } +const char * str2str (const char * str) { + return str; +} int main (void) { srand(time(NULL)); - struct ircxmpp ircxmpp; - memset(&ircxmpp, '\0', sizeof(ircxmpp)); - xmpp_log_t *log; - if (!getenv("IX_JID") || !getenv("IX_PASS") || !getenv("IX_HOST") || !getenv("IX_PORT") - || !getenv("IX_CHANNEL") || !getenv("IX_MUC")) { - fprintf(stderr, "Usage: IX_JID=jid@xmpp.server IX_PASS=pass IX_HOST=irc.server " - "IX_PORT=6666 IX_CHANNEL=#channel IX_MUC=muc@xmpp.srv ircxmpp\n"); - return 1; +#define USAGE " *** ircxmpp is developed at http://git.sijanec.eu/sijanec/ircxmpp ***\n" \ + "Usage: IX_JID=jid@xmpp.server IX_PASS=pass IX_HOST=irc.server IX_PORT=6666 " \ + "IX_CHANNEL=#channel IX_MUC=muc@xmpp.srv ircxmpp\n" \ + "multiple links can be specified by appending a consecutive number, starting with " \ + "2, to every environment variable. first link is IX_*, second is IX_*2, and so on.\n" + size_t handles_length = 0; + ircxmpp ** handles = NULL; + while (1) { // note that if input config is invalid we leak memory before exiting + char b[64]; // i don't free any allocated shit and just return, probably it's ok + if (handles_length++) + sprintf(b, "IX_JID%d", handles_length); + else + strcpy(b, "IX_JID"); + if (!getenv(b)) { + if (handles_length == 1) { + fprintf(stderr, USAGE "FAIL: at least one link is required!\n"); + return 1; + } + handles_length--; + break; + } + handles = realloc(handles, sizeof(ircxmpp *)*handles_length); + handles[handles_length-1] = ircxmpp_init(); +#define PREPARE_HANDLE(value, config, function, required) \ + if (handles_length == 1) \ + sprintf(b, "IX_" config); \ + else \ + sprintf(b, "IX_" config "%d", handles_length); \ + if (getenv(b)) \ + ircxmpp_set_##value(handles[handles_length-1], function(getenv(b))); \ + else if (required) \ + fprintf(stderr, USAGE "FAIL: environment variable %s was expected to be set.\n", b) + PREPARE_HANDLE(jid, "JID", str2str, 1); + PREPARE_HANDLE(password, "PASS", str2str, 1); + PREPARE_HANDLE(hostname, "HOST", str2str, 1); + PREPARE_HANDLE(port, "PORT", atoi, 1); + PREPARE_HANDLE(channel, "CHANNEL", str2str, 1); + PREPARE_HANDLE(muc, "MUC", str2str, 1); + PREPARE_HANDLE(channel_password, "CHPASS", str2str, 0); } - ircxmpp.jid = getenv("IX_JID"); - ircxmpp.password = getenv("IX_PASS"); - ircxmpp.hostname = getenv("IX_HOST"); - ircxmpp.port = atoi(getenv("IX_PORT")); - ircxmpp.channel = getenv("IX_CHANNEL"); - ircxmpp.muc = getenv("IX_MUC"); - ircxmpp.channel_password = getenv("IX_CHPASS"); - xmpp_initialize(); - log = xmpp_get_default_logger(XMPP_LEVEL_DEBUG); - init_irc_control(&ircxmpp); -c: - ircxmpp.ctx = xmpp_ctx_new(NULL, log); - ircxmpp.conn = xmpp_conn_new(ircxmpp.ctx); - xmpp_conn_set_jid(ircxmpp.conn, ircxmpp.jid); - xmpp_conn_set_pass(ircxmpp.conn, ircxmpp.password); - xmpp_connect_client(ircxmpp.conn, NULL, 0, conn_handler, &ircxmpp); signal(SIGTERM, signalhandler); signal(SIGINT, signalhandler); // signal(SIGPIPE, SIG_IGN); while (!shouldexit) { /* TEH EVENT LOOP */ - xmpp_conn_is_disconnected(ircxmpp.conn); - xmpp_run_once(ircxmpp.ctx, 1); - struct bridge ** bridge = &ircxmpp.bridges; - while (*bridge) { - struct bridge ** next = &((*bridge)->next); - if ((*bridge)->irc && irc_run_once(*bridge)) { - free_bridge(bridge, "irc connection dropped"); - goto cont; - } - if ((*bridge)->conn) - xmpp_run_once(xmpp_conn_get_context((*bridge)->conn), 1); - if ((*bridge)->conn && !xmpp_conn_is_connected((*bridge)->conn) - && !xmpp_conn_is_connecting(((*bridge)->conn))) { - if ((*bridge)->side == IRC) { - fprintf(stderr, "RECONNECTING BRIDGE BECAUSE IT DIED!"); - xmpp_connect_client((*bridge)->conn, NULL, 0, - conn_handler_bridge, *bridge); - } else - free_bridge(bridge, "xmpp connection dropped"); - goto cont; - } - while ((*bridge)->conn && xmpp_conn_is_connected((*bridge)->conn) - && (*bridge)->messages_length - && xmpp_conn_get_bound_jid((*bridge)->conn)) { - char id[64]; - fprintf(stderr, "sending xmpp msg from %s\n", (*bridge)->identifier); - sprintf(id, "ircxmpp-%x", rand()); - xmpp_stanza_t * stan = xmpp_message_new((*bridge)->ircxmpp->ctx, - "groupchat", (*bridge)->ircxmpp->muc, id); - xmpp_message_set_body(stan, - (*bridge)->messages[(*bridge)->messages_length-1]); - xmpp_stanza_set_from(stan, xmpp_conn_get_bound_jid((*bridge)->conn)); - xmpp_send((*bridge)->conn, stan); - xmpp_stanza_release(stan); - free((*bridge)->messages[--(*bridge)->messages_length]); - } -cont: - bridge = next; - } - if (!xmpp_conn_is_connected(ircxmpp.conn) && !xmpp_conn_is_connecting(ircxmpp.conn)){ - fprintf(stderr, "XMPP control DISCONNECTED! RECONNECTING!\n"); - xmpp_conn_release(ircxmpp.conn); - xmpp_ctx_free(ircxmpp.ctx); - goto c; - } - if (irc_run_once_control(&ircxmpp)) { - fprintf(stderr, "IRC control DISCONNECTED! RECONNECTING!\n"); - irc_destroy_session(ircxmpp.irc); - init_irc_control(&ircxmpp); - } + for (size_t i = 0; i < handles_length; i++) + ircxmpp_run_once(handles[i]); struct timespec ts = { .tv_sec = 0, .tv_nsec = getenv("IX_LOOPDELAY") ? atoi(getenv("IX_LOOPDELAY"))/1000 : 1e8 @@ -592,14 +684,9 @@ cont: nanosleep(&ts, NULL); } fprintf(stderr, "signal received, cleaning up!\n"); - free_bridges(&ircxmpp.bridges); - xmpp_conn_release(ircxmpp.conn); - xmpp_ctx_free(ircxmpp.ctx); - xmpp_shutdown(); - irc_cmd_quit(ircxmpp.irc, "vsesplošni izklop programa"); - irc_run_once_control(&ircxmpp); // verjetno je to potrebno, da pošlje quit - irc_destroy_session(ircxmpp.irc); - free(ircxmpp.ircnick); - free(ircxmpp.irchost); + for (size_t i = 0; i < handles_length; i++) + ircxmpp_free(handles[i]); + free(handles); return 0; } +#endif diff --git a/ircxmpp.conf b/ircxmpp.conf index fd635c7..ae11eef 100644 --- a/ircxmpp.conf +++ b/ircxmpp.conf @@ -19,3 +19,13 @@ # IX_CHPASS=somepassword ## delay after each event loop cycle in microseconds, defaults to 100ms. # IX_LOOPDELAY=100000 +####################### ANOTHER LINK SETUP ######################## +## As many links as you'd like can be setup. Append a number, starting with 2, to every setting. +# IX_JID2=v@lu.e +# IX_PASS2=value +# IX_HOST2=valu.e +# IX_PORT2=6666 +# IX_CHANNEL2=#value +# IX_MUC2=v@l.u.e +# IX_CHPASS2=value +# IX_LOOPDELAY2 does not exist, as the event loop is shared between all links. |