X-Git-Url: http://git.tremily.us/?a=blobdiff_plain;f=irkerd;h=2b2a9dffd63634f094bdeb3dc73078de6b933c0f;hb=0c2b37e65bf3a13117604d2790f2b79b277b0d3e;hp=a0231f42cd74138b7fb46540cea0a483fb662c4c;hpb=e4ffca75c642e65eb5192daaf2a71c9e7c8ebc02;p=irker.git diff --git a/irkerd b/irkerd index a0231f4..2b2a9df 100755 --- a/irkerd +++ b/irkerd @@ -1,4 +1,5 @@ #!/usr/bin/env python +from __future__ import with_statement """ irkerd - a simple IRC multiplexer daemon @@ -13,7 +14,9 @@ all listed channels. Note that the channel portion of the URL need *not* have a leading '#' unless the channel name itself does. Options: -d sets the debug-message level (probably only of interest to -developers). The -V option prints the program version and exits. +developers). -l sets a logfile to capture message traffic from +channels. -n sets the nick and -p the nickserv password. The -V +option prints the program version and exits. Design and code by Eric S. Raymond . See the project resource page at . @@ -28,7 +31,6 @@ http://pypi.python.org/pypi/irc/ HOST = "localhost" PORT = 6659 -NAMESTYLE = "irker%03d" # IRC nick template - must contain '%d' XMIT_TTL = (3 * 60 * 60) # Time to live, seconds from last transmit PING_TTL = (15 * 60) # Time to live, seconds from last PING HANDSHAKE_TTL = 60 # Time to live, seconds from nick transmit @@ -42,9 +44,9 @@ CONNECTION_MAX = 200 # To avoid hitting a thread limit # No user-serviceable parts below this line -version = "1.17" +version = "1.18" -import sys, getopt, urlparse, time, random, socket, signal +import sys, getopt, urlparse, time, random, socket, signal, re import threading, Queue, SocketServer import irc.client, logging try: @@ -112,7 +114,10 @@ class Connection: "Return a name for the nth server connection." if n is None: n = self.nick_trial - return (NAMESTYLE % n) + if fallback: + return (namestyle % n) + else: + return namestyle def handle_ping(self): "Register the fact that the server has pinged this connection." self.last_ping = time.time() @@ -120,14 +125,20 @@ class Connection: "The server says we're OK, with a non-conflicting nick." self.status = "ready" self.irker.debug(1, "nick %s accepted" % self.nickname()) + if password: + self.connection.privmsg("nickserv", "identify %s" % password) def handle_badnick(self): - "The server says our nick has a conflict." + "The server says our nick is ill-formed or has a conflict." self.irker.debug(1, "nick %s rejected" % self.nickname()) - # Randomness prevents a malicious user or bot from anticipating the - # next trial name in order to block us from completing the handshake. - self.nick_trial += random.randint(1, 3) - self.last_xmit = time.time() - self.connection.nick(self.nickname()) + if fallback: + # Randomness prevents a malicious user or bot from + # anticipating the next trial name in order to block us + # from completing the handshake. + self.nick_trial += random.randint(1, 3) + self.last_xmit = time.time() + self.connection.nick(self.nickname()) + # Otherwise fall through, it might be possible to + # recover manually. def handle_disconnect(self): "Server disconnected us for flooding or some other reason." self.connection = None @@ -244,19 +255,23 @@ class Connection: if channel not in self.channels_joined: self.connection.join(channel) self.irker.debug(1, "joining %s on %s." % (channel, self.servername)) - for segment in message.split("\n"): - # Truncate the message if it's too long, - # but we're working with characters here, - # not bytes, so we could be off. - # 500 = 512 - CRLF - 'PRIVMSG ' - ' :' - maxlength = 500 - len(channel) - if len(segment) > maxlength: - segment = segment[:maxlength] - try: - self.connection.privmsg(channel, segment) - except ValueError as err: - self.irker.debug(1, "irclib rejected a message to %s on %s because: %s" % (channel, self.servername, str(err))) - time.sleep(ANTI_FLOOD_DELAY) + # An empty message might be used as a keepalive or + # to join a channel for logging, so suppress the + # privmsg send unless there is actual traffic. + if message: + for segment in message.split("\n"): + # Truncate the message if it's too long, + # but we're working with characters here, + # not bytes, so we could be off. + # 500 = 512 - CRLF - 'PRIVMSG ' - ' :' + maxlength = 500 - len(channel) + if len(segment) > maxlength: + segment = segment[:maxlength] + try: + self.connection.privmsg(channel, segment) + except ValueError as err: + self.irker.debug(1, "irclib rejected a message to %s on %s because: %s" % (channel, self.servername, str(err))) + time.sleep(ANTI_FLOOD_DELAY) self.last_xmit = self.channels_joined[channel] = time.time() self.irker.debug(1, "XMIT_TTL bump (%s transmission) at %s" % (self.servername, time.asctime())) self.queue.task_done() @@ -462,10 +477,11 @@ class Irker: self.debug(1, "irker has been kicked from %s on %s" % (target, connection.server)) if connection.context: connection.context.handle_kick(target) - def _handle_all_raw_messages(self, connection, event): + def _handle_all_raw_messages(self, _connection, event): "Log all messages when in watcher mode." - with open(logfile, "w") as logfp: - logfp.write("%03f|%s|%s\n" % \ + if logfile: + with open(logfile, "a") as logfp: + logfp.write("%03f|%s|%s\n" % \ (time.time(), event.source, event.arguments[0])) def handle(self, line): "Perform a JSON relay request." @@ -538,8 +554,10 @@ class IrkerUDPHandler(SocketServer.BaseRequestHandler): if __name__ == '__main__': debuglvl = 0 + namestyle = "irker%03d" + password = None logfile = None - (options, arguments) = getopt.getopt(sys.argv[1:], "d:l:V:") + (options, arguments) = getopt.getopt(sys.argv[1:], "d:l:n:p:V:") for (opt, val) in options: if opt == '-d': # Enable debug/progress messages debuglvl = int(val) @@ -547,9 +565,14 @@ if __name__ == '__main__': logging.basicConfig(level=logging.DEBUG) elif opt == '-l': # Logfile mode - report traffic read in logfile = val + elif opt == '-n': # Force the nick + namestyle = val + elif opt == '-p': # Set a nickserv password + password = val elif opt == '-V': # Emit version and exit sys.stdout.write("irkerd version %s\n" % version) sys.exit(0) + fallback = re.search("%.*d", namestyle) irker = Irker(debuglevel=debuglvl) irker.debug(1, "irkerd version %s" % version) try: