summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore4
-rw-r--r--Makefile26
-rw-r--r--ircxmpp.h195
-rw-r--r--main.c589
4 files changed, 814 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..68e3894
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+valgrind-out.txt
+core
+ircxmpp
+.gdb_history
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..13dfd0d
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,26 @@
+DESTDIR=/
+CC = cc
+cflags = -O0 -Wall -I. -Wextra -pedantic -g $(shell pkg-config --cflags libstrophe)
+SRCFILE = main.c
+ldflags = $(shell pkg-config --libs libstrophe) -lircclient
+BINFILE = ircxmpp
+# cflags and ldflags are used so that users specifying CFLAGS and LDFLAGS do not override my flags
+# += is not used, because gcc usually accepts last option, for example -O0 -O2 will use -O2
+.NOTPARALLEL:
+default:
+ $(CC) $(cflags) $(CFLAGS) $(SRCFILE) $(ldflags) $(LDFLAGS) -o$(BINFILE)
+
+install:
+ mkdir -p $(DESTDIR)/usr/bin/
+ cp $(BINFILE) $(DESTDIR)/usr/bin/
+
+distclean:
+ rm $(BINFILE) tmp -rf
+
+clean:
+ rm $(BINFILE) tmp -rf
+
+valgrind:
+ valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes --verbose --log-file=valgrind-out.txt $(COMMAND)
+
+.PHONY: valgrind clean distclean install default
diff --git a/ircxmpp.h b/ircxmpp.h
new file mode 100644
index 0000000..72557e4
--- /dev/null
+++ b/ircxmpp.h
@@ -0,0 +1,195 @@
+#include <libircclient.h>
+enum irc_numeric { /* numerics from rfc 1459 */
+ ERR_NOSUCHNICK = 401,
+ ERR_NOSUCHSERVER,
+ ERR_NOSUCHCHANNEL,
+ ERR_CANNOTSENDTOCHAN,
+ ERR_TOOMANYCHANNELS,
+ ERR_WASNOSUCHNICK,
+ ERR_TOOMANYTARGETS,
+ ERR_NOORIGIN = 409,
+ ERR_NORECIPIENT = 411,
+ ERR_NOTEXTTOSEND,
+ ERR_NOTOPLEVEL,
+ ERR_WILDTOPLEVEL,
+ ERR_UNKNOWNCOMMAND = 421,
+ ERR_NOMOTD,
+ ERR_NOADMININFO,
+ ERR_FILEERROR,
+ ERR_NONICKNAMEGIVEN = 431,
+ ERR_ERRONEUSNICKNAME,
+ ERR_NICKNAMEINUSE,
+ ERR_NICKCOLLISION = 436,
+ ERR_USERNOTINCHANNEL = 441,
+ ERR_NOTONCHANNEL,
+ ERR_USERONCHANNEL,
+ ERR_NOLOGIN,
+ ERR_SUMMONDISABLED,
+ ERR_USERDISABLED,
+ ERR_NOTREGISTERED = 451,
+ ERR_NEEDMOREPARAMS = 461,
+ ERR_ALREADYREGISTERED,
+ ERR_NOPERMFORHOST,
+ ERR_PASSWDMISMATCH,
+ ERR_YOUREBANNEDCREEP,
+ ERR_KEYSET = 467,
+ ERR_CHANNELISFULL = 471,
+ ERR_UNKNOWNMODE,
+ ERR_INVITEONLYCHAN,
+ ERR_BANNEDFROMCHAN,
+ ERR_BADCHANNELKEY,
+ ERR_NOPRIVILEGES = 481,
+ ERR_CHANOPRIVSNEEDED,
+ ERR_CANTKILLSERVER,
+ ERR_NOOPERHOST = 491,
+ ERR_UMODEUNKNOWNFLAG = 501,
+ ERR_USERSDONTMATCH,
+ RPL_NONE = 300,
+ RPL_AWAY,
+ RPL_USERHOST,
+ RPL_ISON,
+ RPL_UNAWAY,
+ RPL_NOWAWAY,
+ RPL_WHOISUSER = 311,
+ RPL_WHOISSERVER,
+ RPL_WHOISOPERATOR,
+ RPL_WHOISIDLE = 317,
+ RPL_ENDOFWHOIS,
+ RPL_WHOISCHANNELS,
+ RPL_WHOWASUSER = 314,
+ RPL_ENDOFWHOWAS = 369,
+ RPL_LISTSTART = 321,
+ RPL_LIST,
+ RPL_LISTEND,
+ RPL_CHANNELMODEIS,
+ RPL_NOTOPIC = 331,
+ RPL_TOPIC,
+ RPL_INVITING = 341,
+ RPL_SUMMONING = 342,
+ RPL_VERSION = 351,
+ RPL_WHOREPLY = 352,
+ RPL_ENDOFWHO = 315,
+ RPL_NAMREPLY = 353,
+ RPL_ENDOFNAMES = 366,
+ RPL_LINKS = 364,
+ RPL_ENDOFLINKS,
+ RPL_BANLIST = 367,
+ RPL_ENDOFBANLIST,
+ RPL_INFO = 371,
+ RPL_ENDOFINFO = 374,
+ RPL_MOTDSTART,
+ RPL_MOTD = 372,
+ RPL_ENDOFMOTD = 376,
+ RPL_YOUREOPER = 381,
+ RPL_REHASHING = 382,
+ RPL_TIME = 391,
+ RPL_USERSSTART,
+ RPL_USERS,
+ RPL_ENDOFUSERS,
+ RPL_NOUSERS,
+ RPL_TRACELINK = 200,
+ RPL_TRACECONNECTING,
+ RPL_TRACEHANDSHAKE,
+ RPL_TRACEUNKNOWN,
+ RPL_TRACEOPERATOR,
+ RPL_TRACEUSER,
+ RPL_TRACESERVER,
+ RPL_TRACENEWTYPE = 208,
+ RPL_TRACELOG = 261,
+ RPL_STATSLINKINFO = 211,
+ RPL_STATSCOMMANDS,
+ RPL_STATSCLINE,
+ RPL_STATSNLINE,
+ RPL_STATSILINE,
+ RPL_STATSKLINE,
+ RPL_STATSYLINE = 218,
+ RPL_ENDOFSTATS,
+ RPL_STATSLLINE = 241,
+ RPL_STATSUPTIME,
+ RPL_STATSOLINE,
+ RPL_STATSHLINE,
+ RPL_UMODEIS = 221,
+ RPL_LUSERCLIENT = 251,
+ RPL_LUSEROP,
+ RPL_LUSERUNKNOWN,
+ RPL_LUSERCHANNELS,
+ RPL_LUSERME,
+ RPL_ADMINME,
+ RPL_ADMINLOC1,
+ RPL_ADMINLOC2,
+ RPL_ADMINEMAIL,
+ RPL_TRACECLASS = 209,
+ RPL_SERVICEINFO = 231,
+ RPL_SERVICE = 233,
+ RPL_SERVLISTEND = 235,
+ RPL_WHOISCHANOP = 316,
+ RPL_CLOSING = 362,
+ RPL_INFOSTART = 373,
+ ERR_YOUWILLBEBANNED = 466,
+ ERR_NOSERVICEHOST = 492,
+ RPL_STATSQLINE = 217,
+ RPL_ENDOFSERVICES = 232,
+ RPL_SERVLIST = 234,
+ RPL_KILLDONE = 361,
+ RPL_CLOSEEND = 363,
+ RPL_MYPORTIS = 384,
+ ERR_BADCHANMASK = 476
+};
+enum side {
+ IRC,
+ XMPP
+};
+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 */
+ enum side side; /* which side is the real user */
+ size_t messages_length;
+ char ** messages;
+};
+struct ircxmpp {
+ struct bridge * bridges;
+ irc_session_t * irc;
+ char * ircnick;
+ char * irchost;
+ xmpp_ctx_t * ctx;
+ xmpp_conn_t * conn;
+ char * jid;
+ char * password;
+ char * hostname;
+ int port;
+ char * channel;
+ char * muc;
+};
+void free_bridge (struct bridge **, const char *);
+void free_bridges (struct bridge **);
+struct bridge ** find_bridge (struct bridge **, const char *, enum side);
+void jid2ircnick (char *);
+void jid2ircuser (char *);
+void bridge_forward (const char *, const char *, struct ircxmpp *, enum side);
+int message_handler (xmpp_conn_t * const, xmpp_stanza_t * const, void * const);
+int presence_handler (xmpp_conn_t * const, xmpp_stanza_t * const, void * const);
+void conn_handler (xmpp_conn_t * const, const xmpp_conn_event_t, const int,
+ xmpp_stream_error_t * const, void * const);
+void conn_handler_bridge (xmpp_conn_t * const, const xmpp_conn_event_t, const int,
+ xmpp_stream_error_t * const, void * const);
+// IRC
+void dump_event (irc_session_t *, const char *, const char *, const char **, unsigned);
+void event_connect (irc_session_t *, const char *, const char *, const char **, unsigned);
+void event_connect_control (irc_session_t *, const char *, const char *, const char **, unsigned);
+void event_privmsg (irc_session_t *, const char *, const char *, const char **, unsigned);
+void event_partquit_control (irc_session_t *, const char *, const char *, const char **, unsigned);
+void event_join_control (irc_session_t *, const char *, const char *, const char **, unsigned);
+void event_channel_control (irc_session_t *, const char *, const char *, const char **, unsigned);
+void event_nick_control(irc_session_t *, const char *, const char *, const char **, unsigned);
+void event_topic_control (irc_session_t *, const char *, const char *, const char **, unsigned);
+void event_numeric (irc_session_t *, unsigned int, const char *, const char **, unsigned);
+void event_numeric_control (irc_session_t *, unsigned int, const char *, const char **, unsigned);
+int irc_run_once (struct bridge *);
+void init_irc (struct bridge *);
+void init_irc_control (struct ircxmpp *);
+// /IRC
+int main (int argc, char ** argv);
diff --git a/main.c b/main.c
new file mode 100644
index 0000000..4f3cbad
--- /dev/null
+++ b/main.c
@@ -0,0 +1,589 @@
+#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 "ircxmpp.h"
+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;
+ else
+ *bridge = (*bridge)->next;
+ free(tofree);
+}
+void free_bridges (struct bridge ** bridges) {
+ while (*bridges) // enkrat bo *bridges NULL, ker ne bo nobenega več notri
+ free_bridge(bridges, "vsi mostovi se podirajo, ker se ircxmpp izklaplja");
+}
+
+struct bridge ** find_bridge (struct bridge ** bridges, const char * identifier, enum side side) {
+s:
+ if (!bridges || !*bridges)
+ return NULL;
+ if ((*bridges)->side == side && !strcmp((*bridges)->identifier, identifier))
+ return bridges;
+ bridges = &((*bridges)->next);
+ goto s;
+}
+
+void jid2ircnick (char * jid) { /* edits a jid into an irc-compatible nick. libera trims nicks. */
+ char * c = strchr(jid, '/');
+ if (c) {
+ c++;
+ memmove(jid, c, strlen(c)+1);
+ }
+ if (*jid && isdigit(*jid))
+ *jid = '_';
+#define VALID_NICK "{}[]_^`|\\"
+ while (*++jid != '\0')
+ if (!(isalnum(*jid) || memchr(VALID_NICK, *jid, strlen(VALID_NICK))))
+ *jid = '_';
+}
+void jid2ircuser (char * jid) {
+ char * c = strchr(jid, '/');
+ if (c) {
+ c++;
+ memmove(jid, c, strlen(c)+1);
+ }
+ if (*jid && isdigit(*jid)) {
+ *jid = '_';
+ }
+#define VALID_USER ""
+ while (*++jid != '\0')
+ 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)))
+ return;
+ fprintf(stderr, "sending text from %s to %s: %s\n", from, side == IRC ? "IRC" : "XMPP",
+ msg ? msg : "[join only]");
+ struct bridge * bridge;
+ if (!bridge_resp) {
+ bridge = calloc(1, sizeof(struct bridge));
+ if (ircxmpp->bridges)
+ ircxmpp->bridges->prev = bridge;
+ bridge->next = ircxmpp->bridges;
+ ircxmpp->bridges = bridge;
+ bridge->identifier = strdup(from);
+ bridge->ircxmpp = ircxmpp;
+ bridge->side = !side;
+ if (side == IRC)
+ init_irc(bridge);
+ else {
+ bridge->conn = xmpp_conn_new(bridge->ircxmpp->ctx);
+ xmpp_conn_set_jid(bridge->conn, bridge->ircxmpp->jid);
+ 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 (side == IRC) {
+ irc_cmd_join(bridge->irc, ircxmpp->channel, NULL /* password */); /* da smo gotovo joinani */
+ irc_run_once(bridge);
+ if (msg)
+ irc_cmd_msg(bridge->irc, ircxmpp->channel, msg);
+ irc_run_once(bridge);
+ } else if (msg) {
+ bridge->messages = realloc(bridge->messages, bridge->messages_length+1);
+ bridge->messages[bridge->messages_length++] = strdup(msg);
+ }
+} /* 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 */
+ return 1;
+ struct ircxmpp * ircxmpp = (struct ircxmpp *) userdata;
+ xmpp_stanza_t * body;
+ const char * type;
+ char * intext;
+ body = xmpp_stanza_get_child_by_name(stanza, "body");
+ if (body == NULL)
+ return 1;
+ type = xmpp_stanza_get_type(stanza);
+ if (type != NULL && strcmp(type, "error") == 0)
+ return 1;
+ if (!strncmp(ircxmpp->jid, xmpp_stanza_get_from(stanza), strlen(ircxmpp->jid)))
+ 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);
+ return 1;
+}
+int presence_handler (xmpp_conn_t * const conn, 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), '/'))
+ 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));
+ 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));
+ bridge_forward(xmpp_stanza_get_from(stanza), NULL, ircxmpp, IRC);
+ }
+ 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) {
+ 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");
+ 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 */
+ // pres = xmpp_presence_new(ctx); // somehow send can only be called once?
+ // xmpp_send(conn, pres);
+ // xmpp_stanza_release(pres);
+ pres = xmpp_presence_new(ircxmpp->ctx); // joining a MUC
+ char b[512];
+ 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);
+ }
+}
+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);
+ 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);
+ 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);
+ 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(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);
+ }
+
+}
+
+/* IRC */
+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);
+ for (unsigned int i = 0; i < c; i++) {
+ if (i)
+ fprintf(stderr, "|");
+ fprintf(stderr, p[i]);
+ }
+ fprintf(stderr, "]\n");
+}
+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, NULL /* password */);
+}
+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, NULL);
+}
+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) {
+ 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) {
+ 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) {
+ dump_event(s, e, o, p, c);
+ struct ircxmpp * ircxmpp = (struct ircxmpp *) irc_get_ctx(s);
+ char buf[512];
+ strncpy(buf, o, 512-1);
+ buf[511] = '\0';
+ if (!strlen(buf))
+ strcpy(buf, "a");
+ char * cp = strchr(buf, '!');
+ if (cp)
+ *cp = '\0';
+ else
+ cp = buf;
+ if (ircxmpp->ircnick && !strcmp(buf, ircxmpp->ircnick)) {
+ cp = strchr(++cp, '@');
+ if (cp) {
+ free(ircxmpp->irchost);
+ ircxmpp->irchost = strdup(++cp);
+ fprintf(stderr, "FOUND OUR HOSTNAME: %s\n", ircxmpp->irchost);
+ }
+ }
+ 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) {
+ dump_event(s, e, o, p, c); /* o je originalen nick, p[0] je nov nick */
+ 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");
+ bridge_forward(o, 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) {
+ dump_event(s, e, o, p, c); /* o je avtor, p[0] je kanal, p[1] je nova tema/zadeva */
+} /* TODO */
+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);
+ dump_event(s, b, o, p, c);
+ switch (e) {
+ case ERR_NONICKNAMEGIVEN:
+ case ERR_ERRONEUSNICKNAME:
+ case ERR_NICKNAMEINUSE:
+ case ERR_NICKCOLLISION:
+ strncpy(b, bridge->identifier, sizeof(b));
+ b[511] = '\0';
+ jid2ircnick(b); /* RFC 1495 says that nicks are at least 9 characters */
+ if (strlen(b))
+ sprintf(strlen(b) >= 7 ? b+7 : b+strlen(b), "_%x", rand());
+ else
+ sprintf(b, "xmpp_%x", rand());
+ irc_cmd_nick(s, b);
+ break;
+ }
+}
+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);
+ dump_event(s, b, o, p, c);
+ switch (e) {
+ case ERR_NONICKNAMEGIVEN:
+ case ERR_ERRONEUSNICKNAME:
+ case ERR_NICKNAMEINUSE:
+ case ERR_NICKCOLLISION:
+ sprintf(b, "ircxmpp%X", rand());
+ free(ircxmpp->ircnick);
+ ircxmpp->ircnick = strdup(b);
+ irc_cmd_nick(s, b);
+ break;
+ }
+}
+int irc_run_once (struct bridge * bridge) { /* returns nonzero if connection failed (dropped brid) */
+ if (!irc_is_connected(bridge->irc)) {
+ char b[512], u[512];
+ strncpy(b, bridge->identifier, sizeof(b)-1);
+ b[511] = '\0';
+ strcpy(u, b);
+ jid2ircnick(b);
+ jid2ircuser(u);
+ if (!strlen(b))
+ sprintf(b, "xmpp_%x", rand());
+ if (!strlen(u))
+ strcpy(u, "ircxmpp");
+ fprintf(stderr, "CONNECTING bridge %s!%s@host\n", 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",
+ irc_strerror(irc_errno(bridge->irc)));
+ return 1;
+ }
+ }
+ struct timeval tv;
+ fd_set in_set, out_set;
+ int maxfd = 0;
+ tv.tv_usec = 0; /* polling */
+ tv.tv_sec = 0;
+ FD_ZERO(&in_set);
+ FD_ZERO(&out_set);
+ irc_add_select_descriptors(bridge->irc, &in_set, &out_set, &maxfd);
+ if (select(maxfd+1, &in_set, &out_set, NULL, &tv) < 0) {
+ if (errno == EINTR)
+ return 0;
+ fprintf(stderr, "SELECT failed in bridge!\n");
+ return 1;
+ }
+ if (irc_process_select_descriptors(bridge->irc, &in_set, &out_set)) {
+ fprintf(stderr, "PROCESS_SELECT failed in bridge!\n");
+ return 1;
+ }
+ return 0;
+}
+int irc_run_once_control (struct ircxmpp * ircxmpp) { /* returns nonzero if failed (dropped conne) */
+ if (!irc_is_connected(ircxmpp->irc)) {
+ char b[512];
+ sprintf(b, "ircxmpp_%X", rand());
+ fprintf(stderr, "CONNECTING control %s!ircxmpp@host\n", 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",
+ irc_strerror(irc_errno(ircxmpp->irc)));
+ return 1;
+ }
+ }
+ struct timeval tv;
+ fd_set in_set, out_set;
+ int maxfd = 0;
+ tv.tv_usec = 0; /* polling */
+ tv.tv_sec = 0;
+ FD_ZERO(&in_set);
+ FD_ZERO(&out_set);
+ irc_add_select_descriptors(ircxmpp->irc, &in_set, &out_set, &maxfd);
+ if (select(maxfd+1, &in_set, &out_set, NULL, &tv) < 0) {
+ if (errno == EINTR)
+ return 0;
+ return 1;
+ }
+ if (irc_process_select_descriptors(ircxmpp->irc, &in_set, &out_set))
+ return 1;
+ return 0;
+}
+void init_irc (struct bridge * bridge) {
+ irc_callbacks_t callbacks;
+ irc_session_t * s;
+ memset(&callbacks, 0, sizeof(callbacks));
+ callbacks.event_connect = event_connect;
+ callbacks.event_nick = dump_event;
+ callbacks.event_quit = dump_event;
+ callbacks.event_join = dump_event;
+ callbacks.event_part = dump_event;
+ callbacks.event_mode = dump_event;
+ callbacks.event_umode = dump_event;
+ callbacks.event_topic = dump_event;
+ callbacks.event_kick = dump_event;
+ callbacks.event_channel = dump_event;
+ 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_numeric = event_numeric;
+ // 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");
+ return;
+ }
+ bridge->irc = s;
+ irc_set_ctx(s, bridge);
+ irc_run_once(bridge);
+ return;
+}
+void init_irc_control (struct ircxmpp * ircxmpp) {
+ irc_callbacks_t callbacks;
+ irc_session_t * s;
+ memset(&callbacks, 0, sizeof(callbacks));
+ callbacks.event_connect = event_connect_control;
+ callbacks.event_nick = event_nick_control;
+ 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_topic = event_topic_control;
+ callbacks.event_kick = dump_event;
+ 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_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");
+ return;
+ }
+ ircxmpp->irc = s;
+ irc_set_ctx(s, ircxmpp);
+ irc_run_once_control(ircxmpp);
+ return;
+}
+/* /IRC */ /* irc_is_connected(irc_session_t * session): 1 ali 0 */ /* TODO: if nick is used */
+int shouldexit = 0;
+void signalhandler (int s) {
+ shouldexit += s+1; /* only for -Wunused-parameter */
+}
+int main (int argc, char ** argv) {
+ srand(time(NULL));
+ struct ircxmpp ircxmpp;
+ memset(&ircxmpp, '\0', sizeof(ircxmpp));
+ xmpp_log_t *log;
+ if (argc != 1+6) {
+ fprintf(stderr, "Usage: ircxmpp <jid> <pass> <hostname> <port> <channel> <muc>\n");
+ return 1;
+ }
+ ircxmpp.jid = argv[1];
+ ircxmpp.password = argv[2];
+ ircxmpp.hostname = argv[3];
+ ircxmpp.port = atoi(argv[4]);
+ ircxmpp.channel = argv[5];
+ ircxmpp.muc = argv[6];
+ 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);
+ }
+ struct timespec ts = {
+ .tv_sec = 0,
+ .tv_nsec = 1e8 /* 0.1s per event loop */
+ };
+ 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();
+ free(ircxmpp.ircnick);
+ free(ircxmpp.irchost);
+ return 0;
+}