From c0e8f01a25ed8f81daa4f6a56fee81c2053477bd Mon Sep 17 00:00:00 2001 From: Peter Palfrader Date: Wed, 23 May 2007 21:25:21 +0000 Subject: Add nsa git-svn-id: svn+ssh://asteria.noreply.org/svn/weaselutils/trunk@270 bc3d92e2-beff-0310-a7cd-cc87d7ac0ede --- nsa | 388 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 388 insertions(+) create mode 100755 nsa (limited to 'nsa') diff --git a/nsa b/nsa new file mode 100755 index 0000000..47c484e --- /dev/null +++ b/nsa @@ -0,0 +1,388 @@ +#!/usr/bin/ruby + +# (c) 2005 Peter Palfrader + +require 'socket' +require 'yaml' +require 'monitor' + +default_irc = { + 'server' => 'irc.oftc.net', + 'port' => 6667, + 'username' => 'unknown_nsa', + 'nick' => 'unknown_nsa', + 'realname' => 'Unknown NSA instance' +} + +CONFIG = YAML::load( File.open( 'config' ) ) +default_irc.each_pair do |k,v| + CONFIG['irc'][k] = v unless CONFIG['irc'][k] +end +CONFIG['mailin'] = '/home/commit/Maildir' unless CONFIG['mailin']; + +Log = Object.new +class << Log + def init + @ignore_list = [] + @ignore_list << "dispatch thread" + @ignore_list << "connection" + @ignore_list << "waituntilonline - waiter" + @ignore_list << "waituntilonline - runthread" + end + + def log section, message + puts message unless @ignore_list.include?(section) + end +end + + +class Connection + def initialize + @inQueue = [] + @inQueue.extend(MonitorMixin) + @inEmpty = @inQueue.new_cond + + @outQueue = [] + @outQueue.extend(MonitorMixin) + @outEmpty = @outQueue.new_cond + + @sock = TCPSocket.new(CONFIG['irc']['server'], CONFIG['irc']['port']); + puts "Connected!" + + createInThread + createOutThread + end + + def print line + @outQueue.synchronize do + @outQueue << line + @outEmpty.signal + end + end + + def getline + line = "" + @inQueue.synchronize do + @inEmpty.wait_while { @inQueue.empty? } + line = @inQueue.shift + end + return line + end + + private + + def createInThread + @inThread = Thread.new { + begin + while true + line = @sock.readline + Log.log "connection", "[connection] <<< " + line + line.chop! + + @inQueue.synchronize do + @inQueue << line + @inEmpty.signal + end + end + rescue => e + puts e.class.to_s+": "+e.message + puts e.backtrace + end + Thread.main.exit + } + end + + def createOutThread + @outThread = Thread.new { + begin + while true + @outQueue.synchronize do + @outEmpty.wait_while { @outQueue.empty? } + line = @outQueue.shift + @sock.puts line + Log.log "connection", "[connection] >>> " + line + end + end + rescue => e + puts e.class.to_s+": "+e.message + puts e.backtrace + end + Thread.main.exit + } + end +end + +IrcHandleDevNull = Object.new +class << IrcHandleDevNull + def handle(irc, parsed) + end +end + +IrcHandlePing = Object.new +class << IrcHandlePing + def handle(irc, parsed) + irc.print "PONG "+irc.getNick + end +end + +IrcHandle001 = Object.new +class << IrcHandle001 + def handle(irc, parsed) + irc.weAreOnline + end +end + +IrcHandlePickAnotherNick = Object.new +class << IrcHandlePickAnotherNick + def handle(irc, parsed) + irc.useADifferentNick + end +end + +IrcHandleMode = Object.new +class << IrcHandleMode + def handle(irc, parsed) + irc.modeUpdate(parsed) + end +end + +class Irc + IRC_NOT_CONNECTED = 0 + IRC_CONNECTED = 1 + IRC_SENT_NICK = 2 + IRC_PICK_NEW_NICK = 3 + IRC_ONLINE = 4 + + def initialize + @handler = {} + @nick = "#{CONFIG['irc']['nick']}" + @onlineMonitor = Monitor.new; + @onlineCond = @onlineMonitor.new_cond + + + @handler['PING'] = IrcHandlePing # PING + @handler['NOTICE'] = IrcHandleDevNull # NOTICE + @handler['MODE'] = IrcHandleMode # NOTICE + @handler['001'] = IrcHandle001 # w :Welcome to the OFTC Internet + @handler['002'] = IrcHandleDevNull # w :Your host is neutron.oftc.net.. + @handler['003'] = IrcHandleDevNull # w :This server was created Fri .... + @handler['004'] = IrcHandleDevNull # w neutron.oftc.net hybrid-7.1+oftc1.... + @handler['005'] = IrcHandleDevNull # w WALLCHOPS KNOCK EXCEPTS INVEX.... + @handler['375'] = IrcHandleDevNull # RPL_MOTDSTART + @handler['372'] = IrcHandleDevNull # RPL_MOTD + @handler['376'] = IrcHandleDevNull # RPL_ENDOFMOTD + @handler['250'] = IrcHandleDevNull # Highest connection count: 2 (2 clients) (7 connections received) + @handler['251'] = IrcHandleDevNull # RPL_LUSERCLIENT + @handler['252'] = IrcHandleDevNull # RPL_LUSEROP + @handler['253'] = IrcHandleDevNull # 1 :unknown connection(s) + @handler['254'] = IrcHandleDevNull # 575 :channels formed + @handler['255'] = IrcHandleDevNull # RPL_LUSERME + @handler['265'] = IrcHandleDevNull # Current local users: 2 Max: 2 + @handler['266'] = IrcHandleDevNull # Current global users: 2 Max: 2 + @handler['353'] = IrcHandleDevNull # RAB = #rab :RAB @weasel· + @handler['366'] = IrcHandleDevNull # RAB #rab :End of /NAMES list. + @handler['433'] = IrcHandlePickAnotherNick # * oftc-bot :Nickname is already in use. + + dispatchToHandlers + run + end + + def print line + @connection.print line + end + + def getNick + @nick + end + + def weAreOnline + throw "Current run_state is @{run_state}. that's unexpected" unless @run_state == IRC_SENT_NICK + @run_state = IRC_ONLINE + @run_thread.wakeup + end + + def useADifferentNick + throw "Current run_state is @{run_state}. that's unexpected" unless @run_state == IRC_SENT_NICK + @run_state = IRC_PICK_NEW_NICK + @run_thread.wakeup + end + + def modeUpdate(message) + puts "[irc] Received mode update: " + message.to_yaml.gsub("\n","\n ") + end + + + def waitUntilOnline + Log.log "waituntilonline - waiter", "[waituntilonline] entering monitor" + @onlineMonitor.synchronize do + Log.log "waituntilonline - waiter", "[waituntilonline] entered monitor" + @onlineCond.wait_while { @run_state != IRC_ONLINE } + Log.log "waituntilonline - waiter", "[waituntilonline] waiting done" + @onlineCond.signal + Log.log "waituntilonline - waiter", "[waituntilonline] woke up the rest" + end + Log.log "waituntilonline - waiter", "[waituntilonline] left monitor" + end + + private + + def run + @run_thread = Thread.new { + @run_state = IRC_NOT_CONNECTED + while true + puts "[run thread] state '#{@run_state}'" + case @run_state + when IRC_NOT_CONNECTED + @connection = Connection.new + @dispatch_thread.wakeup + @run_state = IRC_CONNECTED + when IRC_CONNECTED + @connection.print "USER #{CONFIG['irc']['username']} . . :#{CONFIG['irc']['realname']}" + issueNick + @run_state = IRC_SENT_NICK + when IRC_SENT_NICK + sleep + when IRC_PICK_NEW_NICK + @nick.succ! + issueNick + @run_state = IRC_SENT_NICK + when IRC_ONLINE + @connection.print "MODE #{@nick} +w" + + Log.log "waituntilonline - runthread", "[run thread] entering monitor onlineMonitor" + @onlineMonitor.synchronize do + Log.log "waituntilonline - runthread", "[run thread] entered monitor onlineMonitor" + @onlineCond.signal + Log.log "waituntilonline - runthread", "[run thread] sent signal on onlineCond" + end + Log.log "waituntilonline - runthread", "[run thread] left monitor onlineMonitor" + sleep + end + end + } + end + + def dispatchToHandlers + @dispatch_thread = Thread.new { + while true + while not @connection + Log.log "dispatch thread", "[dispatch thread] waiting for connection" + sleep + Log.log "dispatch thread", "[dispatch thread] waiting for connection done" + end + + Log.log "dispatch thread", "[dispatch thread] waiting for line" + line = @connection.getline + Log.log "dispatch thread", "[dispatch thread] waiting for line done" + + parsed = parseLine line + + if @handler.has_key?(parsed['command']) + Log.log "dispatch thread", "[dispatch thread] dispatching #{parsed['command']}: " + parsed['params'].join(' ') + @handler[parsed['command']].handle( self, parsed ) + else + Log.log "dispatch thread - unhandled", "[dispatch thread] Unhandled: #{line}" + end + end + } + end + + def issueNick + @connection.print "NICK #{@nick}" + end + + def parseLine line + source = nil + (source, line) = line.split(' ', 2) if line[0,1] == ':' + source = source[1,source.length-1] if source + (command, line) = line.split(' ', 2) + params = [] + while line and line[0,1] != ':' + (middle, line) = line.split(' ', 2) + params << middle + end + params << line[1,line.length-1] if line and line[0,1] == ':' + throw "hmmmm. line is '#{line}'." if line and line[0,1] != ':' + + return { + 'source' => source, + 'command' => command, + 'params' => params + } + end +end + +Thread.abort_on_exception = true + +Dir.chdir( CONFIG['mailin'] + "/new" ) + +Log.init +bot = Irc.new +bot.waitUntilOnline +sleep 1 +if CONFIG['irc']['nickserv_is_smart'] + bot.print "NICKSERV :identify #{CONFIG['irc']['nickservpassword']} #{CONFIG['irc']['nick']}" +else + bot.print "NICKSERV :identify #{CONFIG['irc']['nickservpassword']}" +end +sleep 5 + +channels = {} +CONFIG['projects'].each_value do |cl| + cl.each do |c| + channels[c] = true + end +end +channels.each_key do |c| + bot.print "JOIN #{c}" +end + +while (1) do + oldcommitmsg = nil + Dir.foreach('.') { |filename| + next if filename == "." + next if filename == ".." + + in_headers = true + fh = File.open(filename, "r") + project = nil + lines = [] + commitmsg = "" + fh.readlines.each { |line| + line.chomp! + in_headers = false if line == "" + if (in_headers and not line =~ /^\s/) + (header, content) = line.split(':', 2); + content.strip! + if header.upcase == "SUBJECT" + project = /Announce\s+([A-Za-z0-9_-]+)/.match(content)[1]; + end + elsif (not in_headers) + lines.push line + commitmsg = commitmsg + line + end + } + fh.close + File.unlink(filename) + + puts "Project "+project + puts "commitmsg "+commitmsg + + if project and commitmsg != oldcommitmsg + pr = "%c"%(002) + project + "%c: "%(002) + lines.each{ |line| + if (line != '') + line = pr + line + channellist = CONFIG['projects'].has_key?(project) ? CONFIG['projects'][project] : CONFIG['*'] + channellist.each do |c| + bot.print "PRIVMSG #{c} :#{line}" + end + end + } + end + oldcommitmsg = commitmsg + } + sleep 5 +end + +Thread.stop -- cgit v1.2.3