diff options
author | Anton Luka Šijanec <anton@sijanec.eu> | 2022-04-30 15:35:33 +0200 |
---|---|---|
committer | Anton Luka Šijanec <anton@sijanec.eu> | 2022-04-30 15:35:33 +0200 |
commit | 61125a3302eeb712700efc0208024b066668ed9b (patch) | |
tree | 2d30e7eb7d791c7677b2073b3d6147fa5ac98a87 | |
parent | not working. just a checkpoint before I rewrite to binary tree (diff) | |
download | ircxmpp-61125a3302eeb712700efc0208024b066668ed9b.tar ircxmpp-61125a3302eeb712700efc0208024b066668ed9b.tar.gz ircxmpp-61125a3302eeb712700efc0208024b066668ed9b.tar.bz2 ircxmpp-61125a3302eeb712700efc0208024b066668ed9b.tar.lz ircxmpp-61125a3302eeb712700efc0208024b066668ed9b.tar.xz ircxmpp-61125a3302eeb712700efc0208024b066668ed9b.tar.zst ircxmpp-61125a3302eeb712700efc0208024b066668ed9b.zip |
-rw-r--r-- | README | 3 | ||||
-rw-r--r-- | ircxmpp.c | 416 | ||||
-rw-r--r-- | ircxmpp.h | 37 |
3 files changed, 288 insertions, 168 deletions
@@ -82,12 +82,13 @@ Compilation and manual install from source: - make install # not required, but otherwise LD_LIBRARY_PATH=. ./ircxmpp for libircxmpp.so Using as a library: - - you may only use functions between #else and #endif in ircxmpp.h, handler struct is opaque + - you may only use functions after #endif in ircxmpp.h, handler struct is opaque - after make install, ircxmpp.h and libircxmpp.so are placed in /usr/{include,lib} - #include <ircxmpp.h> and link with -lircxmpp - functions return nothing (except ircxmpp_init) and log to standard error - do not call ircxmpp_set_*() functions after first call to ircxmpp_run_once() - actual main function of the ircxmpp program is in ircxmpp.c between #else and #endif + - in the 0.0.x stage, no binary compatiblity is guaranteed. nothing is guaranteed. Gentoo/openrc?: - http://github.com/OpenRC/openrc/pull/517 needs to be merged before for increased security @@ -1,3 +1,4 @@ +#define _GNU_SOURCE #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -11,58 +12,88 @@ #include "ircxmpp.h" #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, ...) { +__attribute__((noreturn)) __attribute__((unused)) +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" + fprintf(stderr, "BUG in ircxmpp %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); - if ((*bridge)->irc) { - irc_cmd_quit((*bridge)->irc, razlog); - irc_run_once(*bridge); // verjetno je to potrebno, da pošlje quit - irc_destroy_session((*bridge)->irc); - } - if ((*bridge)->conn) - 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]); - free((*bridge)->messages); - if ((*bridge)->next) - (*bridge)->next->prev = (*bridge)->prev; - struct bridge * tofree = *bridge; - if ((*bridge)->prev) - (*bridge)->prev->next = (*bridge)->next; +static int bridge_compare (const void * ap, const void * bp) { + const struct bridge * a = (const struct bridge *) ap; + const struct bridge * b = (const struct bridge *) bp; + int i = strcmp(a->identifier, b->identifier); + if (i) + return i; + if (a->side == b->side) + return 0; else { - if ((*bridge)->next) - (*bridge)->next->prev = (*bridge)->prev; - *bridge = (*bridge)->next; + if (a->side > b->side) + return -1; + else + return 1; } - free(tofree); } -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"); +#define LOG(ircxmpp, level, ...) logwrite(ircxmpp, level, __FILE__, __LINE__, __func__, __VA_ARGS__); +static void logwrite (struct ircxmpp * ircxmpp, enum ircxmpp_loglevel level, const char * file, + int line, const char * function, const char * format, ...) { + va_list ap_len, ap_print; + va_start(ap_len, format); + va_copy(ap_print, ap_len); +#define LOCATION_FORMAT "%s()@%s:%d " +#define LOCATION_ARGS function, file, line + int location_len = snprintf(NULL, 0, LOCATION_FORMAT, LOCATION_ARGS); + int len = vsnprintf(NULL, 0, format, ap_len) + 1 + location_len; + if (len > 65535) + return; + char buf[len]; + char * c = buf; + c += sprintf(c, LOCATION_FORMAT, LOCATION_ARGS); + vsprintf(c, format, ap_print); + ircxmpp->log_handler(ircxmpp->log_userdata, level, "ircxmpp", buf); + va_end(ap_len); + va_end(ap_print); +} +static void free_bridge (struct bridge * bridge, const char * razlog) { + if (!bridge) + return; + LOG(bridge->ircxmpp, IRCXMPP_DEBUG, "freeing bridge with reason: %s", razlog); + if (bridge->irc) { + irc_cmd_quit(bridge->irc, razlog); + irc_run_once(bridge); // verjetno je to potrebno, da pošlje quit + irc_destroy_session(bridge->irc); } + if (bridge->conn) + xmpp_conn_release(bridge->conn); // graceful disconnect, what is that? + tdelete(bridge, bridge->ircxmpp->bridges, bridge_compare); + free(bridge->identifier); + for (size_t i = 0; i < bridge->messages_length; i++) + free(bridge->messages[i]); + free(bridge->messages); + free(bridge); +} + +static char * free_reason = "global readdson not set since startup. this is a bug. report it."; +static void free_bridge_global_reason (void * bridge) { + free_bridge((struct bridge *) bridge, free_reason); +} +static void free_bridges (void ** bridges) { + free_reason = "vsi mostovi se podirajo, ker se ircxmpp izklaplja"; + tdestroy(*bridges, free_bridge_global_reason); } -static struct bridge ** find_bridge (struct bridge ** bridges, const char * id, enum side side) { -s: - if (!bridges || !*bridges) +static struct bridge * find_bridge (void ** bridges, const char * id, enum side side) { + struct bridge bridge = { + .side = side, + .identifier = (char *) id + }; + struct bridge ** found = tfind(&bridge, bridges, bridge_compare); + if (!found) return NULL; - if ((*bridges)->side == side && !strcmp((*bridges)->identifier, id)) - return bridges; - bridges = &((*bridges)->next); - goto s; + return *found; } static void jid2ircnick (char * jid) { /* edits a jid into an irc nick. libera trims nicks. */ @@ -93,21 +124,17 @@ static void jid2ircuser (char * jid) { *jid = 'x'; } 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); + struct bridge * bridge = 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", f, s == IRC ? "IRC" : "XMPP", + LOG(ircxmpp, IRCXMPP_DEBUG, "sending text from %s to %s: %s", f, s == IRC ? "IRC" : "XMPP", m ? m : "[join only]"); - struct bridge * bridge; - if (!bridge_resp) { + if (!bridge) { bridge = calloc(1, sizeof(struct bridge)); - if (ircxmpp->bridges) - ircxmpp->bridges->prev = bridge; - bridge->next = ircxmpp->bridges; - ircxmpp->bridges = bridge; bridge->identifier = strdup(f); bridge->ircxmpp = ircxmpp; bridge->side = !s; + tsearch(bridge, &ircxmpp->bridges, bridge_compare); if (s == IRC) init_irc(bridge); else { @@ -116,8 +143,7 @@ static void bridge_forward (const char * f, const char * m, struct ircxmpp * irc xmpp_conn_set_pass(bridge->conn, bridge->ircxmpp->password); xmpp_connect_client(bridge->conn, NULL, 0, conn_handler_bridge, bridge); } - } else - bridge = *bridge_resp; + } if (s == IRC) { irc_cmd_join(bridge->irc, ircxmpp->channel, ircxmpp->channel_password); irc_run_once(bridge); @@ -138,13 +164,12 @@ static void bridge_forward (const char * f, const char * m, struct ircxmpp * irc bridge->messages[bridge->messages_length++] = strdup(m); } } /* 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; +static int message_handler (xmpp_conn_t * const c __attribute__((unused)), + xmpp_stanza_t * const stanza, void * const ud) { struct ircxmpp * ircxmpp = (struct ircxmpp *) ud; xmpp_stanza_t * body; const char * type; - char * intext; + char * tx; body = xmpp_stanza_get_child_by_name(stanza, "body"); if (body == NULL) return 1; @@ -155,10 +180,10 @@ static int message_handler (xmpp_conn_t * const c, xmpp_stanza_t * const stanza, return 1; // this is our message if (xmpp_stanza_get_child_by_name_and_ns(stanza, "delay", "urn:xmpp:delay")) return 1; // MUC MAM history - intext = xmpp_stanza_get_text(body); - printf("Incoming message from %s: %s\n", xmpp_stanza_get_from(stanza), intext); - bridge_forward(xmpp_stanza_get_from(stanza), intext, ircxmpp, IRC); - xmpp_free(ircxmpp->ctx, intext); + tx = xmpp_stanza_get_text(body); + LOG(ircxmpp, IRCXMPP_INFO, "Incoming message from %s: %s", xmpp_stanza_get_from(stanza), tx); + bridge_forward(xmpp_stanza_get_from(stanza), tx, ircxmpp, IRC); + xmpp_free(ircxmpp->ctx, tx); return 1; } static int presence_handler (xmpp_conn_t * const c, xmpp_stanza_t * const stanza, void * const ud) { @@ -166,25 +191,25 @@ static int presence_handler (xmpp_conn_t * const c, xmpp_stanza_t * const 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)); + LOG(ircxmpp, IRCXMPP_INFO, "user %s left MUC", xmpp_stanza_get_from(stanza)); free_bridge(find_bridge(&ircxmpp->bridges, xmpp_stanza_get_from(stanza), XMPP), "ircxmpp: sogovornik je zapustil XMPP MUC sobo"); } if (!xmpp_stanza_get_type(stanza)) { - fprintf(stderr, "sogovornik %s se je pridružil MUC\n", xmpp_stanza_get_from(stanza)); + LOG(ircxmpp, IRCXMPP_INFO, "user %s connected to MUC", xmpp_stanza_get_from(stanza)); bridge_forward(xmpp_stanza_get_from(stanza), NULL, ircxmpp, IRC); } return 1; } 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) { + const int error __attribute__((unused)), + xmpp_stream_error_t * const stream_error __attribute__((unused)), + 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); if (status == XMPP_CONN_CONNECT) { xmpp_stanza_t * pres; - fprintf(stderr, "DEBUG: connected.\n"); + LOG(ircxmpp, IRCXMPP_INFO, "control connected to XMPP."); xmpp_handler_add(conn, message_handler, NULL, "message", NULL, ircxmpp); xmpp_handler_add(conn, presence_handler, NULL, "presence", NULL, ircxmpp); /* Send initial <presence/> so that we appear online to contacts */ @@ -202,23 +227,19 @@ static void conn_handler (xmpp_conn_t * const conn, const xmpp_conn_event_t stat xmpp_stanza_release(x); xmpp_send(conn, pres); xmpp_stanza_release(pres); - } else { - fprintf(stderr, "DEBUG: disconnected\n"); - // xmpp_stop(ircxmpp->ctx); - } + } else + LOG(ircxmpp, IRCXMPP_WARN, "control disconnected from XMPP."); } -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) { +static void conn_handler_bridge (xmpp_conn_t * const conn __attribute__((unused)), + const xmpp_conn_event_t status, const int error __attribute__((unused)), + xmpp_stream_error_t * const stream_error __attribute__((unused)), + 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); - if (!conn) /* just for -Wunused-parameter */ - return; if (status == XMPP_CONN_CONNECT) { char stanzato[512]; snprintf(stanzato, 512, "%s/%s ircxmpp_%x", bridge->ircxmpp->muc, bridge->identifier, rand()); - fprintf(stderr, "joining muc %s (to)\n", stanzato); + LOG(bridge->ircxmpp, IRCXMPP_INFO, "bridge joining muc %s (to)", stanzato); xmpp_stanza_t * pres = xmpp_presence_new(bridge->ircxmpp->ctx); // joining a MUC xmpp_stanza_set_to(pres, stanzato); xmpp_stanza_t * x = xmpp_stanza_new(bridge->ircxmpp->ctx); @@ -228,55 +249,88 @@ static void conn_handler_bridge (xmpp_conn_t * const conn, const xmpp_conn_event xmpp_stanza_release(x); xmpp_send(bridge->conn, pres); xmpp_stanza_release(pres); - } else { - fprintf(stderr, "DEBUG: disconnected\n"); - // xmpp_stop(ircxmpp->ctx); - } + } else + LOG(bridge->ircxmpp, IRCXMPP_WARN, "control disconnected from XMPP."); } /* IRC */ +static void dump_event_control ( + irc_session_t * s, const char * e, const char * o, const char ** p, unsigned c) { + struct ircxmpp * ircxmpp = (struct ircxmpp *) irc_get_ctx(s); + int requiredbuf = 256; + for (unsigned int i = 0; i < c; i++) { + requiredbuf += strlen(p[c])+256; + } + if (requiredbuf > 65535) + return; + char buf[requiredbuf]; + char * cp = buf; + cp += sprintf(cp, "%s, origin %s, params %d [", e, o ? o : "NULL", c); + for (unsigned int i = 0; i < c; i++) { + if (i) + cp += sprintf(cp, "|"); + cp += sprintf(cp, "%s", p[i]); + } + cp += sprintf(cp, "]"); + ircxmpp->log_handler(ircxmpp->log_userdata, IRCXMPP_DEBUG, "irc_event_control", buf); +} 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 */ + struct bridge * bridge = (struct bridge *) irc_get_ctx(s); + struct ircxmpp * ircxmpp = bridge->ircxmpp; + int requiredbuf = 256; + for (unsigned int i = 0; i < c; i++) { + requiredbuf += strlen(p[c])+256; + } + if (requiredbuf > 65535) return; - fprintf(stderr, "Event %s, origin %s, params %d [", e, o ? o : "NULL", c); + char buf[requiredbuf]; + char * cp = buf; + cp += sprintf(cp, "%s, origin %s, params %d [", e, o ? o : "NULL", c); for (unsigned int i = 0; i < c; i++) { if (i) - fprintf(stderr, "|"); - fprintf(stderr, "%s", p[i]); + cp += sprintf(cp, "|"); + cp += sprintf(cp, "%s", p[i]); } - fprintf(stderr, "]\n"); + cp += sprintf(cp, "]"); + ircxmpp->log_handler(ircxmpp->log_userdata, IRCXMPP_DEBUG, "irc_event", buf); } 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); + dump_event(s, e, o, p, c); irc_cmd_join(s, bridge->ircxmpp->channel, bridge->ircxmpp->channel_password); } 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); + dump_event_control(s, e, o, p, c); irc_cmd_join(s, ircxmpp->channel, ircxmpp->channel_password); } 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!!! */ + dump_event(s, e, o, p, c); + 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/."); +} +static void event_privmsg_control ( + irc_session_t * s, const char * e, const char * o, const char ** p, unsigned c) { + dump_event_control(s, e, o, p, c); 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/."); } 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); + dump_event_control(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"); + free_bridge(find_bridge(&ircxmpp->bridges, o, IRC), "part or quit from irc"); } /* part and quit events */ 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 */ + dump_event_control(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); @@ -284,7 +338,7 @@ static void event_channel_control ( } 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); + dump_event_control(s, e, o, p, c); struct ircxmpp * ircxmpp = (struct ircxmpp *) irc_get_ctx(s); char buf[512]; strncpy(buf, o, 512-1); @@ -301,21 +355,18 @@ static void event_join_control ( if (cp) { free(ircxmpp->irchost); ircxmpp->irchost = strdup(++cp); - fprintf(stderr, "FOUND OUR HOSTNAME: %s\n", ircxmpp->irchost); + LOG(ircxmpp, IRCXMPP_DEBUG, "FOUND OUR HOSTNAME: %s", ircxmpp->irchost); } } bridge_forward(o /* indeed n!u@h */, NULL, ircxmpp, XMPP); } 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 */ + dump_event_control(s, e, o, p, c); /* o je originalen nick, p[0] je nov nick */ if (!c) return; struct ircxmpp * ircxmpp = (struct ircxmpp *) irc_get_ctx(s); - struct bridge ** bridge = find_bridge(&ircxmpp->bridges, o /* indeed n!u@h */, IRC); - if (!bridge || !*bridge) - return; - free_bridge(bridge, "nick change from irc"); + free_bridge(find_bridge(&ircxmpp->bridges, o, IRC), "nick change from irc"); char buf[512]; snprintf(buf, 512, "%s%s", p[0], strchr(o, '!') ? strchr(o, '!') : "neznan uporabnik@neznan strežnik"); @@ -323,7 +374,7 @@ static void event_nick_control ( } 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 */ + dump_event_control(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]); @@ -356,7 +407,7 @@ static void event_numeric_control ( char b[512]; struct ircxmpp * ircxmpp = (struct ircxmpp *) irc_get_ctx(s); sprintf(b, "%d", e); - dump_event(s, b, o, p, c); + dump_event_control(s, b, o, p, c); switch (e) { case ERR_NONICKNAMEGIVEN: case ERR_ERRONEUSNICKNAME: @@ -381,10 +432,10 @@ static int irc_run_once (struct bridge * bridge) { /* returns nonzero if connect sprintf(b, "xmpp_%x", rand()); if (!strlen(u)) strcpy(u, "ircxmpp"); - fprintf(stderr, "CONNECTING bridge %s!%s@host\n", b, u); + LOG(bridge->ircxmpp, IRCXMPP_INFO, "CONNECTING bridge %s!%s@host", b, u); if (irc_connect(bridge->irc, bridge->ircxmpp->hostname, bridge->ircxmpp->port, NULL, b, u, bridge->identifier)) { - fprintf(stderr, "could not connect: %s\n", + LOG(bridge->ircxmpp, IRCXMPP_ERROR, "bridge could not connect: %s", irc_strerror(irc_errno(bridge->irc))); return 1; } @@ -400,11 +451,11 @@ static int irc_run_once (struct bridge * bridge) { /* returns nonzero if connect if (select(maxfd+1, &in_set, &out_set, NULL, &tv) < 0) { if (errno == EINTR) return 0; - fprintf(stderr, "SELECT failed in bridge!\n"); + LOG(bridge->ircxmpp, IRCXMPP_WARN, "SELECT failed in bridge!"); return 1; } if (irc_process_select_descriptors(bridge->irc, &in_set, &out_set)) { - fprintf(stderr, "PROCESS_SELECT failed in bridge!\n"); + LOG(bridge->ircxmpp, IRCXMPP_WARN, "PROCESS_SELECT failed in bridge!"); return 1; } return 0; @@ -415,12 +466,12 @@ static int irc_run_once_control (struct ircxmpp * ircxmpp) { /* returns nonzero if (!irc_is_connected(ircxmpp->irc)) { char b[512]; sprintf(b, "ircxmpp_%X", rand()); - fprintf(stderr, "CONNECTING control %s!ircxmpp@host\n", b); + LOG(ircxmpp, IRCXMPP_INFO, "CONNECTING control %s!ircxmpp@host", b); 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")) { - fprintf(stderr, "could not connect: %s\n", + LOG(ircxmpp, IRCXMPP_ERROR, "control could not connect: %s", irc_strerror(irc_errno(ircxmpp->irc))); return 2; } @@ -469,7 +520,7 @@ static void init_irc (struct bridge * bridge) { // callbacks.event_dcc_chat_req = dump_event; // callbacks.event_dcc_send_req = dump_event; if (!(s = irc_create_session(&callbacks))) { - fprintf(stderr, "could not create IRC session!\n"); + LOG(bridge->ircxmpp, IRCXMPP_ERROR, "could not create IRC bridge session!"); return; } bridge->irc = s; @@ -486,24 +537,24 @@ static void init_irc_control (struct ircxmpp * ircxmpp) { callbacks.event_quit = event_partquit_control; callbacks.event_join = event_join_control; callbacks.event_part = event_partquit_control; - callbacks.event_mode = dump_event; - callbacks.event_umode = dump_event; + callbacks.event_mode = dump_event_control; + callbacks.event_umode = dump_event_control; callbacks.event_topic = event_topic_control; - callbacks.event_kick = dump_event; + callbacks.event_kick = dump_event_control; callbacks.event_channel = event_channel_control; - callbacks.event_privmsg = event_privmsg; - callbacks.event_notice = dump_event; - callbacks.event_channel_notice = dump_event; - callbacks.event_invite = dump_event; - callbacks.event_ctcp_req = dump_event; - callbacks.event_ctcp_rep = dump_event; - callbacks.event_ctcp_action = dump_event; - callbacks.event_unknown = dump_event; + callbacks.event_privmsg = event_privmsg_control; + callbacks.event_notice = dump_event_control; + callbacks.event_channel_notice = dump_event_control; + callbacks.event_invite = dump_event_control; + callbacks.event_ctcp_req = dump_event_control; + callbacks.event_ctcp_rep = dump_event_control; + callbacks.event_ctcp_action = dump_event_control; + callbacks.event_unknown = dump_event_control; callbacks.event_numeric = event_numeric_control; // callbacks.event_dcc_chat_req = dump_event; // callbacks.event_dcc_send_req = dump_event; if (!(s = irc_create_session(&callbacks))) { - fprintf(stderr, "could not create IRC control session!\n"); + LOG(ircxmpp, IRCXMPP_ERROR, "could not create IRC control session!"); return; } ircxmpp->irc = s; @@ -511,10 +562,61 @@ static void init_irc_control (struct ircxmpp * ircxmpp) { irc_run_once_control(ircxmpp); return; } +static void ircxmpp_default_logger (void * const u __attribute__((unused)), + const enum ircxmpp_loglevel l, const char * const a, const char * const m) { + char * t = "unspec"; + switch (l) { + case IRCXMPP_DEBUG: + t = "DEBUG"; + break; + case IRCXMPP_INFO: + t = "INFO"; + break; + case IRCXMPP_WARN: + t = "WARN"; + break; + case IRCXMPP_ERROR: + t = "ERROR"; + break; + } + fprintf(stderr, "[ircxmpp %s] %s: %s\n", t, a, m); +} /* /IRC */ /* irc_is_connected(irc_session_t * session): 1 ali 0 */ +static void send_xmpp_logs_to_me (void * const u, const xmpp_log_level_t l, const char * const a, + const char * const m) { + enum ircxmpp_loglevel loglevel = IRCXMPP_ERROR; + switch (l) { + case XMPP_LEVEL_DEBUG: + loglevel = IRCXMPP_DEBUG; + break; + case XMPP_LEVEL_INFO: + loglevel = IRCXMPP_INFO; + break; + case XMPP_LEVEL_WARN: + loglevel = IRCXMPP_WARN; + break; + case XMPP_LEVEL_ERROR: + loglevel = IRCXMPP_ERROR; + break; + } + struct ircxmpp * ircxmpp = (struct ircxmpp *) u; + ircxmpp->log_handler(ircxmpp->log_userdata, loglevel, a, m); +} struct ircxmpp * ircxmpp_init (void) { xmpp_initialize(); - return calloc(1, sizeof(struct ircxmpp)); + struct ircxmpp * ircxmpp = calloc(1, sizeof(struct ircxmpp)); + ircxmpp->log_handler = ircxmpp_default_logger; + ircxmpp->xmpp_logger.handler = send_xmpp_logs_to_me; + ircxmpp->xmpp_logger.userdata = ircxmpp; + return ircxmpp; +} +void ircxmpp_set_log_handler (struct ircxmpp * ircxmpp, ircxmpp_logger log_handler) { + if (!log_handler) + ircxmpp->log_handler = ircxmpp_default_logger; + ircxmpp->log_handler = log_handler; +} +void ircxmpp_set_log_userdata (struct ircxmpp * ircxmpp, void * log_userdata) { + ircxmpp->log_userdata = log_userdata; } void ircxmpp_set_jid (struct ircxmpp * ircxmpp, const char * jid) { free(ircxmpp->jid); @@ -543,15 +645,45 @@ void ircxmpp_set_channel_password (struct ircxmpp * ircxmpp, const char * channe free(ircxmpp->channel_password); ircxmpp->channel_password = strdup(channel_password); } +static void obdelaj_bridge (const void * nodep, VISIT which __attribute__((unused)), + int depth __attribute__((unused))) { + struct bridge * bridge = *(struct bridge **) nodep; + if (bridge->irc && irc_run_once(bridge)) { + free_bridge(bridge, "irc connection dropped"); + return; + } + if (bridge->conn && !xmpp_conn_is_connected(bridge->conn) + && !xmpp_conn_is_connecting((bridge->conn))) { + if (bridge->side == IRC && bridge->messages_length) { + LOG(bridge->ircxmpp, IRCXMPP_WARN, "RECONNECTING dead BRIDGE with msgs!"); + xmpp_connect_client(bridge->conn, NULL, 0, conn_handler_bridge, bridge); + } else + free_bridge(bridge, "xmpp connection dropped"); + return; + } + while (bridge->conn && xmpp_conn_is_connected(bridge->conn) && bridge->messages_length + && xmpp_conn_get_bound_jid(bridge->conn)) { + char id[64]; + LOG(bridge->ircxmpp, IRCXMPP_DEBUG, "sending xmpp msg from %s", 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]); + } +} 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"); + LOG(ircxmpp, IRCXMPP_WARN, "XMPP control is DISCONNECTED! CONNECTING!"); 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->ctx = xmpp_ctx_new(NULL, &ircxmpp->xmpp_logger); ircxmpp->conn = xmpp_conn_new(ircxmpp->ctx); xmpp_conn_set_jid(ircxmpp->conn, ircxmpp->jid); xmpp_conn_set_pass(ircxmpp->conn, ircxmpp->password); @@ -560,48 +692,12 @@ void ircxmpp_run_once (struct ircxmpp * 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); + LOG(ircxmpp, IRCXMPP_WARN, "IRC control DISCONNECTED, code %d. CONNECTING!", 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; - } + twalk(ircxmpp->bridges, obdelaj_bridge); } void ircxmpp_free (struct ircxmpp * ircxmpp) { free_bridges(&ircxmpp->bridges); @@ -623,8 +719,8 @@ void ircxmpp_free (struct ircxmpp * ircxmpp) { } #else int shouldexit = 0; -void signalhandler (int s) { - shouldexit += s+1; /* only for -Wunused-parameter */ +void signalhandler (int s __attribute__((unused))) { + shouldexit++; } const char * str2str (const char * str) { return str; @@ -1,4 +1,12 @@ -#ifdef IX_LIB /* do not use functions until #else in programs that use libircxmpp. */ +enum ircxmpp_loglevel { + IRCXMPP_DEBUG, + IRCXMPP_INFO, + IRCXMPP_WARN, + IRCXMPP_ERROR +}; +typedef void (*ircxmpp_logger) + (void * const, const enum ircxmpp_loglevel, const char * const, const char * const); +#ifdef IX_LIB /* do not use functions until #endif in programs that use libircxmpp. */ #include <libircclient.h> /* do not use members of struct ircxmpp, use opaque ircxmpp type! */ #include <strophe.h> enum irc_numeric { /* numerics from rfc 1459 */ @@ -143,8 +151,6 @@ enum side { }; struct bridge { // xmpp connection is the same for every user, only IRC connection differs struct ircxmpp * ircxmpp; - struct bridge * next; - struct bridge * prev; char * identifier; /* jid if side is XMPP or nickname if side is IRC */ irc_session_t * irc; xmpp_conn_t * conn; /* isti xmpp_ctx_t ima lahko več connov */ @@ -153,7 +159,7 @@ struct bridge { // xmpp connection is the same for every user, only IRC connecti char ** messages; }; struct ircxmpp { - struct bridge * bridges; + void * bridges; irc_session_t * irc; char * ircnick; char * irchost; @@ -166,10 +172,19 @@ struct ircxmpp { char * channel; char * muc; char * channel_password; + void * log_userdata; + ircxmpp_logger log_handler; + xmpp_log_t xmpp_logger; }; -static void free_bridge (struct bridge **, const char *); -static void free_bridges (struct bridge **); -static struct bridge ** find_bridge (struct bridge **, const char *, enum side); +static void send_xmpp_logs_to_me ( + void * const, const xmpp_log_level_t, const char * const, const char * const); +static void logwrite (struct ircxmpp *, enum ircxmpp_loglevel, const char *, int, + const char *, const char *, ...); +static int bridge_compare (const void *, const void *); +static void free_bridge (struct bridge *, const char *); +static void free_bridges (void **); +static void free_bridge_global_reason (void *); +static struct bridge * find_bridge (void **, const char *, enum side); static void jid2ircnick (char *); static void jid2ircuser (char *); static void bridge_forward (const char *, const char *, struct ircxmpp *, enum side); @@ -180,6 +195,8 @@ static void conn_handler (xmpp_conn_t * const, const xmpp_conn_event_t, const in static void conn_handler_bridge (xmpp_conn_t * const, const xmpp_conn_event_t, const int, xmpp_stream_error_t * const, void * const); // IRC +static void dump_event_control ( + irc_session_t *, const char *, const char *, const char **, unsigned); static void dump_event (irc_session_t *, const char *, const char *, const char **, unsigned); static void event_connect (irc_session_t *, const char *, const char *, const char **, unsigned); static void event_connect_control ( @@ -201,11 +218,16 @@ static void event_numeric_control ( static int irc_run_once (struct bridge *); static void init_irc (struct bridge *); static void init_irc_control (struct ircxmpp *); +static void obdelaj_bridge (const void *, VISIT, int); +static void ircxmpp_default_logger ( + void * const, const enum ircxmpp_loglevel, const char * const, const char * const); // /IRC #endif // IX_LIB // ZUNANJE typedef struct ircxmpp ircxmpp; /* opaque handle */ ircxmpp * ircxmpp_init (void); +void ircxmpp_set_log_handler (ircxmpp *, ircxmpp_logger); +void ircxmpp_set_log_userdata (ircxmpp *, void *); void ircxmpp_set_jid (ircxmpp *, const char *); void ircxmpp_set_password (ircxmpp *, const char *); void ircxmpp_set_hostname (ircxmpp *, const char *); @@ -215,4 +237,5 @@ void ircxmpp_set_muc (ircxmpp *, const char *); void ircxmpp_set_channel_password (ircxmpp *, const char *); void ircxmpp_run_once (struct ircxmpp *); void ircxmpp_free (struct ircxmpp *); +int ircxmpp_version = 0; // /ZUNANJE |