diff options
author | Silvio Rhatto <rhatto@riseup.net> | 2012-01-28 14:09:30 -0200 |
---|---|---|
committer | Silvio Rhatto <rhatto@riseup.net> | 2012-01-28 14:09:30 -0200 |
commit | afade3b215bc5c10fdbf30a5d8b6d97e87302aaa (patch) | |
tree | a25deb96cf1616cda6278929a5408e31edf428e7 /files | |
download | puppet-qwebirc-afade3b215bc5c10fdbf30a5d8b6d97e87302aaa.tar.gz puppet-qwebirc-afade3b215bc5c10fdbf30a5d8b6d97e87302aaa.tar.bz2 |
Initial import
Diffstat (limited to 'files')
-rw-r--r-- | files/ircclient-ssl.py | 173 | ||||
-rw-r--r-- | files/qwebirc-init.d.sh | 154 | ||||
-rw-r--r-- | files/qwebirc-ssl.patch | 38 | ||||
-rw-r--r-- | files/qwebirc-ssl.patch.orig | 38 |
4 files changed, 403 insertions, 0 deletions
diff --git a/files/ircclient-ssl.py b/files/ircclient-ssl.py new file mode 100644 index 0000000..2fd09c0 --- /dev/null +++ b/files/ircclient-ssl.py @@ -0,0 +1,173 @@ +import twisted, sys, codecs, traceback +from twisted.words.protocols import irc +from twisted.internet import reactor, protocol, abstract, ssl +from twisted.web import resource, server +from twisted.protocols import basic +from twisted.names.client import Resolver +import hmac, time, config, random, qwebirc.config_options as config_options +from config import HMACTEMPORAL + +if config.get("CONNECTION_RESOLVER"): + CONNECTION_RESOLVER = Resolver(servers=config.get("CONNECTION_RESOLVER")) +else: + CONNECTION_RESOLVER = None + +if hasattr(config, "WEBIRC_MODE") and config.WEBIRC_MODE == "hmac": + HMACKEY = hmac.HMAC(key=config.HMACKEY) + +def hmacfn(*args): + h = HMACKEY.copy() + h.update("%d %s" % (int(time.time() / HMACTEMPORAL), " ".join(args))) + return h.hexdigest() + +def utf8_iso8859_1(data, table=dict((x, x.decode("iso-8859-1")) for x in map(chr, range(0, 256)))): + return (table.get(data.object[data.start]), data.start+1) + +codecs.register_error("mixed-iso-8859-1", utf8_iso8859_1) + +def irc_decode(x): + try: + return x.decode("utf-8", "mixed-iso-8859-1") + except UnicodeDecodeError: + return x.decode("iso-8859-1", "ignore") + +class QWebIRCClient(basic.LineReceiver): + delimiter = "\n" + def __init__(self, *args, **kwargs): + self.__nickname = "(unregistered)" + + def dataReceived(self, data): + basic.LineReceiver.dataReceived(self, data.replace("\r", "")) + + def lineReceived(self, line): + line = irc_decode(irc.lowDequote(line)) + + try: + prefix, command, params = irc.parsemsg(line) + self.handleCommand(command, prefix, params) + except irc.IRCBadMessage: + # emit and ignore + traceback.print_exc() + return + + if command == "001": + self.__nickname = params[0] + + if self.__perform is not None: + for x in self.__perform: + self.write(x) + self.__perform = None + elif command == "NICK": + nick = prefix.split("!", 1)[0] + if nick == self.__nickname: + self.__nickname = params[0] + + def handleCommand(self, command, prefix, params): + self("c", command, prefix, params) + + def __call__(self, *args): + self.factory.publisher.event(args) + + def write(self, data): + self.transport.write("%s\r\n" % irc.lowQuote(data.encode("utf-8"))) + + def connectionMade(self): + basic.LineReceiver.connectionMade(self) + + self.lastError = None + f = self.factory.ircinit + nick, ident, ip, realname, hostname, pass_ = f["nick"], f["ident"], f["ip"], f["realname"], f["hostname"], f.get("password") + self.__nickname = nick + self.__perform = f.get("perform") + + if not hasattr(config, "WEBIRC_MODE"): + self.write("USER %s bleh bleh %s :%s" % (ident, ip, realname)) + elif config.WEBIRC_MODE == "hmac": + hmac = hmacfn(ident, ip) + self.write("USER %s bleh bleh %s %s :%s" % (ident, ip, hmac, realname)) + elif config.WEBIRC_MODE == "webirc": + self.write("WEBIRC %s qwebirc %s %s" % (config.WEBIRC_PASSWORD, hostname, ip)) + self.write("USER %s bleh %s :%s" % (ident, ip, realname)) + elif config.WEBIRC_MODE == "cgiirc": + self.write("PASS %s_%s_%s" % (config.CGIIRC_STRING, ip, hostname)) + self.write("USER %s bleh %s :%s" % (ident, ip, realname)) + elif config.WEBIRC_MODE == config_options.WEBIRC_REALNAME or config.WEBIRC_MODE is None: # last bit is legacy + if ip == hostname: + dispip = ip + else: + dispip = "%s/%s" % (hostname, ip) + + self.write("USER %s bleh bleh :%s - %s" % (ident, dispip, realname)) + + if pass_ is not None: + self.write("PASS :%s" % pass_) + self.write("NICK %s" % nick) + + self.factory.client = self + self("connect") + + def __str__(self): + return "<QWebIRCClient: %s!%s@%s>" % (self.__nickname, self.factory.ircinit["ident"], self.factory.ircinit["ip"]) + + def connectionLost(self, reason): + if self.lastError: + self.disconnect("Connection to IRC server lost: %s" % self.lastError) + else: + self.disconnect("Connection to IRC server lost.") + self.factory.client = None + basic.LineReceiver.connectionLost(self, reason) + + def error(self, message): + self.lastError = message + self.write("QUIT :qwebirc exception: %s" % message) + self.transport.loseConnection() + + def disconnect(self, reason): + self("disconnect", reason) + self.factory.publisher.disconnect() + +class QWebIRCFactory(protocol.ClientFactory): + protocol = QWebIRCClient + def __init__(self, publisher, **kwargs): + self.client = None + self.publisher = publisher + self.ircinit = kwargs + + def write(self, data): + self.client.write(data) + + def error(self, reason): + self.client.error(reason) + + def clientConnectionFailed(self, connector, reason): + protocol.ClientFactory.clientConnectionFailed(self, connector, reason) + self.publisher.event(["disconnect", "Connection to IRC server failed."]) + self.publisher.disconnect() + +def createIRC(*args, **kwargs): + f = QWebIRCFactory(*args, **kwargs) + + tcpkwargs = {} + if hasattr(config, "OUTGOING_IP"): + tcpkwargs["bindAddress"] = (config.OUTGOING_IP, 0) + + if CONNECTION_RESOLVER is None: + if hasattr(config, "SSLPORT"): + reactor.connectSSL(config.IRCSERVER, config.SSLPORT, f, ssl.ClientContextFactory(), **tcpkwargs) + else: + reactor.connectTCP(config.IRCSERVER, config.IRCPORT, f, **tcpkwargs) + return f + + def callback(result): + name, port = random.choice(sorted((str(x.payload.target), x.payload.port) for x in result[0])) + reactor.connectTCP(name, port, f, **tcpkwargs) + def errback(err): + f.clientConnectionFailed(None, err) # None?! + + d = CONNECTION_RESOLVER.lookupService(config.IRCSERVER, (1, 3, 11)) + d.addCallbacks(callback, errback) + return f + +if __name__ == "__main__": + e = createIRC(lambda x: 2, nick="slug__moo", ident="mooslug", ip="1.2.3.6", realname="mooooo", hostname="1.2.3.4") + reactor.run() diff --git a/files/qwebirc-init.d.sh b/files/qwebirc-init.d.sh new file mode 100644 index 0000000..c0cea70 --- /dev/null +++ b/files/qwebirc-init.d.sh @@ -0,0 +1,154 @@ +#! /bin/sh +### BEGIN INIT INFO +# Provides: qwebirc +# Required-Start: $remote_fs $syslog +# Required-Stop: $remote_fs $syslog +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: QwebIRC web interface +# Description: This script launches the QWebIRC daemon. +### END INIT INFO + +# Author: Antoine Beaupre <anarcat@koumbit.org> + +# PATH should only include /usr/* if it runs after the mountnfs.sh script +PATH=/sbin:/usr/sbin:/bin:/usr/bin +DESC="Description of the service" +NAME=qwebirc +HOME=/var/lib/qwebirc +DAEMON=$HOME/run.py +DAEMON_ARGS="" +PIDFILE=$HOME/twistd.pid +SCRIPTNAME=/etc/init.d/$NAME + +# Exit if the package is not installed +[ -x "$DAEMON" ] || exit 0 + +# Read configuration variable file if it is present +[ -r /etc/default/$NAME ] && . /etc/default/$NAME + +# Load the VERBOSE setting and other rcS variables +. /lib/init/vars.sh + +# Define LSB log_* functions. +# Depend on lsb-base (>= 3.2-14) to ensure that this file is present +# and status_of_proc is working. +. /lib/lsb/init-functions + +# +# Function that starts the daemon/service +# +do_start() +{ + # Return + # 0 if daemon has been started + # 1 if daemon was already running + # 2 if daemon could not be started + start-stop-daemon --start --quiet --pidfile $PIDFILE --startas $DAEMON --test > /dev/null \ + || return 1 + start-stop-daemon --chdir $HOME --chuid $NAME --start --quiet --pidfile $PIDFILE --startas $DAEMON -- \ + $DAEMON_ARGS \ + || return 2 + # Add code here, if necessary, that waits for the process to be ready + # to handle requests from services started subsequently which depend + # on this one. As a last resort, sleep for some time. +} + +# +# Function that stops the daemon/service +# +do_stop() +{ + # Return + # 0 if daemon has been stopped + # 1 if daemon was already stopped + # 2 if daemon could not be stopped + # other if a failure occurred + start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE + RETVAL="$?" + [ "$RETVAL" = 2 ] && return 2 + # Wait for children to finish too if this is a daemon that forks + # and if the daemon is only ever run from this initscript. + # If the above conditions are not satisfied then add some other code + # that waits for the process to drop all resources that could be + # needed by services started subsequently. A last resort is to + # sleep for some time. + start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 --user $NAME --name python + [ "$?" = 2 ] && return 2 + # Many daemons don't delete their pidfiles when they exit. + rm -f $PIDFILE + return "$RETVAL" +} + +# +# Function that sends a SIGHUP to the daemon/service +# +do_reload() { + # + # If the daemon can reload its configuration without + # restarting (for example, when it is sent a SIGHUP), + # then implement that here. + # + start-stop-daemon --stop --signal 1 --quiet --pidfile $PIDFILE --name $NAME + return 0 +} + +case "$1" in + start) + [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME" + do_start + case "$?" in + 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; + 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; + esac + ;; + stop) + [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME" + do_stop + case "$?" in + 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; + 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; + esac + ;; + status) + status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $? + ;; + #reload|force-reload) + # + # If do_reload() is not implemented then leave this commented out + # and leave 'force-reload' as an alias for 'restart'. + # + #log_daemon_msg "Reloading $DESC" "$NAME" + #do_reload + #log_end_msg $? + #;; + restart|force-reload) + # + # If the "reload" option is implemented then remove the + # 'force-reload' alias + # + log_daemon_msg "Restarting $DESC" "$NAME" + do_stop + case "$?" in + 0|1) + do_start + case "$?" in + 0) log_end_msg 0 ;; + 1) log_end_msg 1 ;; # Old process is still running + *) log_end_msg 1 ;; # Failed to start + esac + ;; + *) + # Failed to stop + log_end_msg 1 + ;; + esac + ;; + *) + #echo "Usage: $SCRIPTNAME {start|stop|restart|reload|force-reload}" >&2 + echo "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload}" >&2 + exit 3 + ;; +esac + +: diff --git a/files/qwebirc-ssl.patch b/files/qwebirc-ssl.patch new file mode 100644 index 0000000..9c9e921 --- /dev/null +++ b/files/qwebirc-ssl.patch @@ -0,0 +1,38 @@ +diff -r 19d6068a1aa6 config.py.example +--- a/config.py.example Sun Apr 03 23:58:29 2011 +0100 ++++ b/config.py.example Sat Jan 28 13:31:49 2012 -0200 +@@ -20,6 +20,11 @@ + # Port of IRC server to connect to. + IRCSERVER, IRCPORT = "irc.myserver.com", 6667 + ++# OPTION: SSLPORT ++# SSL port of IRC server to connect to. ++# If this option is uncommented it will override IRCPORT. ++#SSLPORT = 6697 ++ + # OPTION: REALNAME + # The realname field of IRC clients will be set to this value. + REALNAME = "http://moo.com/" +diff -r 19d6068a1aa6 qwebirc/ircclient.py +--- a/qwebirc/ircclient.py Sun Apr 03 23:58:29 2011 +0100 ++++ b/qwebirc/ircclient.py Sat Jan 28 13:31:49 2012 -0200 +@@ -1,6 +1,6 @@ + import twisted, sys, codecs, traceback + from twisted.words.protocols import irc +-from twisted.internet import reactor, protocol, abstract ++from twisted.internet import reactor, protocol, abstract, ssl + from twisted.web import resource, server + from twisted.protocols import basic + from twisted.names.client import Resolver +@@ -152,7 +152,10 @@ + tcpkwargs["bindAddress"] = (config.OUTGOING_IP, 0) + + if CONNECTION_RESOLVER is None: +- reactor.connectTCP(config.IRCSERVER, config.IRCPORT, f, **tcpkwargs) ++ if hasattr(config, "SSLPORT"): ++ reactor.connectSSL(config.IRCSERVER, config.SSLPORT, f, ssl.ClientContextFactory(), **tcpkwargs) ++ else: ++ reactor.connectTCP(config.IRCSERVER, config.IRCPORT, f, **tcpkwargs) + return f + + def callback(result): diff --git a/files/qwebirc-ssl.patch.orig b/files/qwebirc-ssl.patch.orig new file mode 100644 index 0000000..f0626f9 --- /dev/null +++ b/files/qwebirc-ssl.patch.orig @@ -0,0 +1,38 @@ +diff -r 19d6068a1aa6 config.py.example +--- a/config.py.example Sun Apr 03 23:58:29 2011 +0100 ++++ b/config.py.example Wed Oct 12 17:43:46 2011 -0400 +@@ -19,6 +19,11 @@ + # OPTION: IRCPORT + # Port of IRC server to connect to. + IRCSERVER, IRCPORT = "irc.myserver.com", 6667 ++ ++# OPTION: SSLPORT ++# SSL port of IRC server to connect to. ++# If this option is uncommented it will override IRCPORT. ++#SSLPORT = 6697 + + # OPTION: REALNAME + # The realname field of IRC clients will be set to this value. +diff -r 19d6068a1aa6 qwebirc/ircclient.py +--- a/qwebirc/ircclient.py Sun Apr 03 23:58:29 2011 +0100 ++++ b/qwebirc/ircclient.py Wed Oct 12 17:43:46 2011 -0400 +@@ -1,6 +1,6 @@ + import twisted, sys, codecs, traceback + from twisted.words.protocols import irc +-from twisted.internet import reactor, protocol, abstract ++from twisted.internet import reactor, protocol, abstract, ssl + from twisted.web import resource, server + from twisted.protocols import basic + from twisted.names.client import Resolver +@@ -152,7 +152,10 @@ + tcpkwargs["bindAddress"] = (config.OUTGOING_IP, 0) + + if CONNECTION_RESOLVER is None: +- reactor.connectTCP(config.IRCSERVER, config.IRCPORT, f, **tcpkwargs) ++ if hasattr(config, "SSLPORT"): ++ reactor.connectSSL(config.IRCSERVER, config.SSLPORT, f, ssl.ClientContextFactory(), **tcpkwargs) ++ else: ++ reactor.connectTCP(config.IRCSERVER, config.IRCPORT, f, **tcpkwargs) + return f + + def callback(result): |