summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAnton Luka Šijanec <anton@sijanec.eu>2022-04-27 14:54:50 +0200
committerAnton Luka Šijanec <anton@sijanec.eu>2022-04-27 14:54:50 +0200
commit0d2646b246def5144dee17963d165fb222b27d26 (patch)
tree21475f9850d31a83d65e5f6692daca28355ae5c5
parentinitial commit (diff)
downloadircxmpp-0d2646b246def5144dee17963d165fb222b27d26.tar
ircxmpp-0d2646b246def5144dee17963d165fb222b27d26.tar.gz
ircxmpp-0d2646b246def5144dee17963d165fb222b27d26.tar.bz2
ircxmpp-0d2646b246def5144dee17963d165fb222b27d26.tar.lz
ircxmpp-0d2646b246def5144dee17963d165fb222b27d26.tar.xz
ircxmpp-0d2646b246def5144dee17963d165fb222b27d26.tar.zst
ircxmpp-0d2646b246def5144dee17963d165fb222b27d26.zip
-rw-r--r--Makefile5
-rw-r--r--README66
l---------debian/README.Debian1
-rw-r--r--debian/changelog5
-rw-r--r--debian/compat1
-rw-r--r--debian/control13
-rw-r--r--debian/copyright7
-rw-r--r--debian/ircxmpp.service15
-rwxr-xr-xdebian/rules3
-rw-r--r--ircxmpp.conf21
-rw-r--r--ircxmpp.h3
-rw-r--r--main.c40
12 files changed, 164 insertions, 16 deletions
diff --git a/Makefile b/Makefile
index 13dfd0d..26f6f02 100644
--- a/Makefile
+++ b/Makefile
@@ -2,6 +2,8 @@ DESTDIR=/
CC = cc
cflags = -O0 -Wall -I. -Wextra -pedantic -g $(shell pkg-config --cflags libstrophe)
SRCFILE = main.c
+CFGFILE = ircxmpp.conf
+CFGDEST = ircxmpp
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
@@ -11,8 +13,9 @@ default:
$(CC) $(cflags) $(CFLAGS) $(SRCFILE) $(ldflags) $(LDFLAGS) -o$(BINFILE)
install:
- mkdir -p $(DESTDIR)/usr/bin/
+ mkdir -p $(DESTDIR)/usr/bin $(DESTDIR)/etc
cp $(BINFILE) $(DESTDIR)/usr/bin/
+ cp $(CFGFILE) $(DESTDIR)/etc/$(CFGDEST)
distclean:
rm $(BINFILE) tmp -rf
diff --git a/README b/README
new file mode 100644
index 0000000..437edbd
--- /dev/null
+++ b/README
@@ -0,0 +1,66 @@
+ircxmpp is a simple one-to-one bridge between IRC and XMPP.
+
+Usage: IX_JID=j@xmpp IX_PASS=xmpp IX_HOST=irc.srv IX_PORT=6666 IX_CHANNEL=#c IX_MUC=muc@xmpp ircxmpp
+ - one XMPP user needs to be created on XMPP server, identified by jid and password
+ - prepend # to irc.srv hostname (#irc.srv) to connect with TLS
+ - change port to 6697 (or whatever is used) when connecting with TLS
+ - replace muc with the MUC XMPP group to connect to
+
+Configure the MUC room and XMPP server if possible:
+ - disable sending full jids, because every IRC user has same bare JID, only nickname changes
+ - default visitors as participants or make ircxmpp XMPP a participant in MUC room
+ - disable any per-JID or per-IP limits, ircxmpp connects multiple times
+ - MAM may be enabled, since old messages will never be sent to IRC
+ - disable any MUC password protection or allow JID of ircxmpp to connect
+
+Configure the IRC channel or server if possible:
+ - disable any per-IP limits, ircxmpp connects multiple times. libera chat is okay with that.
+ - use a bot to set XMPP users to have +o, +v or +h to prevent flood kicks
+ - disable invite-only mode on channel. you may use IRC channel passwords.
+
+Required environment variables for configuration:
+ - IX_JID JID of ircxmpp user on XMPP server to connect with
+ - IX_PASS password for XMPP authentication of ircxmpp user
+ - IX_HOST hostname of the IRC server, prefix the value with # to connect with TLS
+ - IX_PORT port of the IRC server to connect to, choose apropriate for plaintext/TLS
+ - IX_CHANNEL channel on IRC server to bridge
+ - IX_MUC multi-user chat on XMPP server to bridge
+
+Optional environment variables for configuration:
+ - IX_CHPASS set to IRC channel password if channel on IRC is password protected
+ - IX_LOOPDELAY delay after each event loop cycle in microseconds, defaults to 100 ms.
+
+Operation principle:
+ - ircxmpp initiates two control connections, one to XMPP server, one to IRC server, and joins
+ - when user joins in XMPP, a bridge connection is established to IRC and joins the channel
+ - when user joins in IRC, a bridge connection is established to XMPP and joins the MUC
+ - when a chat is made in IRC, it's reflected on the correct bridge connection in XMPP
+ - when a chat is made in XMPP, it's reflected on the correct bridge connection in IRC
+ - when user quits in IRC, resources are freed for the bridge, connection to XMPP is dropped
+ - when user quits in XMPP, resources are freed for the bridge, connection to IRC is dropped
+ - control connections are reconnected automatically and remain connected till exit.
+ - program can be stopped by sending a SIGTERM or SIGINT (well, or SIGKILL for that matter)
+ - topic changes are reflected in XMPP MUC as messages
+
+To implement:
+ - private messages
+ - ctcp messages (ACTION - /me) and perhaps file upload (that'd be hard)
+ - subject changing
+ - automatic +v/+h/+o botnet juggling between bridge IRC connections and control IRC conn
+ - setting user@host of bridge bots on IRC to JIDs of XMPP users by temporary changing rDNS
+
+Notes:
+ - when connecting to a channel with already joined nicks, they're not connected to XMPP until they send a message or change their nick. only after that are they bridged, and the sent message is not lost. implementing this would require parsing NAMES list, which only contains nicks and not usernames and hostnames, and would also impact performance, especially on MAM-enabled MUCs that send a lot of history on XMPP join for every connection. XMPP MUCs also tend to slow down with a large number of connections, whereas IRC channels don't.
+
+Debian installation and systemd service:
+ - this program is packaged in my personal package archive on http://prog.sijanec.eu/
+ - after adding the archive and running apt update, install with apt install ircxmpp
+ - edit the configuration file with environment variables in /etc/ircxmpp
+ - run the service with service ircxmpp start, enable it at boot with systemctl enable ircxmpp
+
+Security:
+ - Do not trust this program.
+ - If you find a (security) vulnerability or any issue, please email anton at šijanec.eu
+ - Whenever possible, run the program in a chroot jail as an unpriviledged user. Running with systemd (service ircxmpp start) does that.
+
+ -- Anton Luka Šijanec <anton@sijanec.eu> Wed, 27 Apr 2022 14:30:00 +0200
diff --git a/debian/README.Debian b/debian/README.Debian
new file mode 120000
index 0000000..59a23c4
--- /dev/null
+++ b/debian/README.Debian
@@ -0,0 +1 @@
+../README \ No newline at end of file
diff --git a/debian/changelog b/debian/changelog
new file mode 100644
index 0000000..34f2644
--- /dev/null
+++ b/debian/changelog
@@ -0,0 +1,5 @@
+ircxmpp (0.0.1-1) stable; urgency=low
+
+ * Initial release.
+
+ -- Anton Luka Šijanec <anton@sijanec.eu> Wed, 27 Apr 2021 14:00:00 +0200
diff --git a/debian/compat b/debian/compat
new file mode 100644
index 0000000..b4de394
--- /dev/null
+++ b/debian/compat
@@ -0,0 +1 @@
+11
diff --git a/debian/control b/debian/control
new file mode 100644
index 0000000..df3a181
--- /dev/null
+++ b/debian/control
@@ -0,0 +1,13 @@
+Source: ircxmpp
+Section: net
+Priority: optional
+Maintainer: Anton Luka Šijanec <anton@sijanec.eu>
+Build-Depends: debhelper (>=11~), libircclient-dev, libstrophe-dev
+Standards-Version: 4.1.4
+Homepage: http://git.sijanec.eu/sijanec/ircxmpp
+
+Package: ircxmpp
+Architecture: any
+Multi-Arch: foreign
+Depends: ${misc:Depends}, ${shlibs:Depends}
+Description: one-to-one bridge between IRC and XMPP
diff --git a/debian/copyright b/debian/copyright
new file mode 100644
index 0000000..11569a2
--- /dev/null
+++ b/debian/copyright
@@ -0,0 +1,7 @@
+Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
+Upstream-Name: ircxmpp
+Upstream-Contact: Anton Luka Šijanec <anton@sijanec.eu>
+Source: http://git.sijanec.eu/sijanec/sear.c
+Copyright: 2022 Anton Luka Šijanec
+Disclaimer: This package is not licensed under any licence.
+Comment: At least in current version of this program, no licensing information was published.
diff --git a/debian/ircxmpp.service b/debian/ircxmpp.service
new file mode 100644
index 0000000..18eb171
--- /dev/null
+++ b/debian/ircxmpp.service
@@ -0,0 +1,15 @@
+[Unit]
+Description=one-to-one bridge between IRC and XMPP
+After=network.target
+
+[Service]
+TemporaryFileSystem=/:ro
+BindReadOnlyPaths=/lib /etc /usr/bin
+EnvironmentFile=-/etc/ircxmpp
+Type=simple
+DynamicUser=yes
+ExecStart=/usr/bin/ircxmpp
+Restart=no
+
+[Install]
+WantedBy=multi-user.target
diff --git a/debian/rules b/debian/rules
new file mode 100755
index 0000000..cbe925d
--- /dev/null
+++ b/debian/rules
@@ -0,0 +1,3 @@
+#!/usr/bin/make -f
+%:
+ dh $@
diff --git a/ircxmpp.conf b/ircxmpp.conf
new file mode 100644
index 0000000..fd635c7
--- /dev/null
+++ b/ircxmpp.conf
@@ -0,0 +1,21 @@
+## configuration file for ircxmpp.
+## see http://git.sijanec.eu/sijanec/ircxmpp
+## or /usr/share/doc/ircxmpp/README.Debian
+####################### REQUIRED VARIABLES #######################
+## JID of ircxmpp user on XMPP server to connect with
+# IX_JID=change@me
+## password for XMPP authentication of ircxmpp user
+# IX_PASS=xmpp_password
+## hostname of the IRC server, prefix the value with # to connect with TLS
+# IX_HOST=irc.server
+## port of the IRC server to connect to, choose apropriate for plaintext/TLS
+# IX_PORT=6666
+## channel on IRC server to bridge
+# IX_CHANNEL=#xmppbridge
+## multi-user chat on XMPP server to bridge
+# IX_MUC=muc@conference.xmpp.server
+####################### OPTIONAL VARIABLES #######################
+## set to IRC channel password if channel on IRC is password protected
+# IX_CHPASS=somepassword
+## delay after each event loop cycle in microseconds, defaults to 100ms.
+# IX_LOOPDELAY=100000
diff --git a/ircxmpp.h b/ircxmpp.h
index 72557e4..da03aa5 100644
--- a/ircxmpp.h
+++ b/ircxmpp.h
@@ -163,6 +163,7 @@ struct ircxmpp {
int port;
char * channel;
char * muc;
+ char * channel_password;
};
void free_bridge (struct bridge **, const char *);
void free_bridges (struct bridge **);
@@ -192,4 +193,4 @@ int irc_run_once (struct bridge *);
void init_irc (struct bridge *);
void init_irc_control (struct ircxmpp *);
// /IRC
-int main (int argc, char ** argv);
+int main (void);
diff --git a/main.c b/main.c
index 4f3cbad..e38ec0a 100644
--- a/main.c
+++ b/main.c
@@ -101,7 +101,7 @@ void bridge_forward (const char * from, const char * msg, struct ircxmpp * ircxm
} else
bridge = *bridge_resp;
if (side == IRC) {
- irc_cmd_join(bridge->irc, ircxmpp->channel, NULL /* password */); /* da smo gotovo joinani */
+ irc_cmd_join(bridge->irc, ircxmpp->channel, ircxmpp->channel_password);
irc_run_once(bridge);
if (msg)
irc_cmd_msg(bridge->irc, ircxmpp->channel, msg);
@@ -244,13 +244,13 @@ void dump_event (irc_session_t * s, const char * e, const char * o, const char *
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 */);
+ 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) {
dump_event(s, e, o, p, c);
struct ircxmpp * ircxmpp = (struct ircxmpp *) irc_get_ctx(s);
- irc_cmd_join(s, ircxmpp->channel, NULL);
+ 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) {
dump_event(s, e, o, p, c); /* SAME FOR _control. do NOT use irc_get_ctx here!!! */
@@ -300,16 +300,25 @@ void event_join_control (irc_session_t * s, const char * e, const char * o, cons
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;
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 */
+ char buf[512];
+ snprintf(buf, 512, "%s%s", p[0], strchr(o, '!') ? strchr(o, '!')
+ : "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) {
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) {
char b[512];
@@ -494,21 +503,24 @@ int shouldexit = 0;
void signalhandler (int s) {
shouldexit += s+1; /* only for -Wunused-parameter */
}
-int main (int argc, char ** argv) {
+int main (void) {
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");
+ 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;
}
- 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];
+ 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);
@@ -574,7 +586,7 @@ cont:
}
struct timespec ts = {
.tv_sec = 0,
- .tv_nsec = 1e8 /* 0.1s per event loop */
+ .tv_nsec = getenv("IX_LOOPDELAY") ? atoi(getenv("IX_LOOPDELAY"))/1000 : 1e8
};
nanosleep(&ts, NULL);
}