| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431 | #!/usr/bin/env rubyrequire 'eventmachine'require 'digest/md5'require 'fiber'require 'geoip'# .. allowed commands .. ROLES = CAPABILITIES# normal users have ROLE broadcast. Roles are defined on a per-user basis .. in a config.$version = "0.2EventMachineSocketServer"$debug = 0$role_commands = Hash[	"everyone" => ["PING", "WHO", "C", "PART"], 		"broadcast_admin" => ["BC_ID", "BC", "BC_ENDCOUNT"],	"broadcast" => ["REQ_BC", "BC_RE"],		"specbot_admin" => ["REQ_ASSIGN", "REQ_UNASSIGN", "REQ_PING", "REQ_ASSIGNMENTS"],	"specbot" => ["ASSIGN_RE", "UNASSIGN_RE", "PING_RE", "ASSIGNMENTS_RE"],	]$default_role = "everyone"# which role is talking to which role?# effectively it says: this (local) command is sent to that (remote) topic .. that certain topic is read by that user with that role.$role_dialogs = Hash[	"everyone" => ["everyone"],		"broadcast_admin" => ["broadcast"],	"broadcast" => ["broadcast_admin"],		"specbot_admin" => ["specbot"],	"specbot" => ["specbot_admin"],	]$user_roles = Hash[	"paul_dev_eggdrop" => ["everyone", "broadcast"],	"paul_eggdrop" => ["everyone", "broadcast"],		"paul_dev_specbot" => ["everyone", "broadcast", "specbot"],	"paul_specbot" => ["everyone", "broadcast", "specbot"],		"qw.nu" => ["everyone", "broadcast"],	"qw.nu_poster" => ["everyone", "broadcast"],		"mihawk_dev_specbot" => ["everyone", "broadcast", "specbot"],	"mihawk_specbot" => ["everyone", "broadcast", "specbot"],		"central_brain" => ["everyone", "broadcast_admin", "specbot_admin"],	]module InterconnectionPointProtocolHandler		@@connected_clients = Array.new	@@broadcasts = Hash.new	# default method that is being run on connection!	def post_init # connection of someone starts here...		@username = nil	end	### getters	def entered_username?		!@username.nil? && !@username.empty? # then it's true	end	def username		@username	end	def my_roles		return $user_roles[@username]	end	def my_cmds		return my_roles.collect {|v| $role_commands[v]}.flatten	end	# returns online users by default by searching through saved connections	def ousers		users = Array.new		@@connected_clients.each {|c| users.push(c.username)}		return users	end # of ousers	### checkers	def allowed_cmd(inputmessage)		my_cmds.each{|c|			#print "A #{c} B #{inputmessage}\n"			if inputmessage =~ /^#{c}/				return true			end		}		return false	end	def online(user)		if ousers.include? user			return true		else			return false		end	end	### actions	def put_log(msg)		puts "#{Time.now.utc.strftime("%Y-%m-%d %H:%M:%S")}: #{msg}"	end	# searches through our hash of saved connections and writes them a messages.	def	write_user(input, username = nil)		if not username			username = @username		end		if connection = @@connected_clients.find { |c| c.username == username }			sometime = "#{Time.now.utc.strftime("%Y-%m-%d %H:%M:%S %z")}"			line = "#{sometime}: #{input}\n"			connection.send_data line		end	end # of write_user	# searches through roles and writes those users on their connections	def write_role(role, input, *exempts)		#find users with role		users = Array.new		$user_roles.each {|k, v|		                 if v.include? role			                 users.push(k)		                 end		                 }		# find users that are online and inside Array "users"		lala = Array.new		users.each {|v|		           if ousers.include? v			           lala.push(v)		           end		           }		# now write to them		lala.each {|user|		          if not exempts.include? user			          write_user(input, user)		          end		          }	end	# of write_role()	# what happens with what input?!	def inputting(input)		write_user("SYS You typed: #{input}")		if input =~ /^([A-Z_]+)/			cmd = $1		end		# now we have the cmd .. or not ;)		if input =~ /^[A-Z_]+ (.+)/			payload = $1		end		# now we can use payload		if allowed_cmd(cmd)			### typical system commands follow			if cmd == "PING"				write_user("PONG")			elsif cmd == "C"				if payload =~ /^(.+)$/					write_role($default_role, "C #{@username}: #{$1}")				else					write_user("SYS Format is C <chattext>")				end		  elsif cmd == "WHO"				ousers.each {|ouser| write_user("WHO_RE #{ouser} ROLES: #{$user_roles[ouser].join(", ")}")}			elsif cmd == "PART"				write_user("SYS Goodbye '#{@username}'.")				write_role($default_role, "PARTED User '#{@username}' just left the party.", @username)				return "bye"			###now for the good stuff, broadcasting			elsif cmd == "REQ_BC"				# help with format .. but send the raw payload				if payload =~ /^(.+),(.+),(.+),'(.+)','(.+)'$/					#              n    f    s     ni     txt					error = 0					# $&					# The string matched by the last successful pattern match in this scope, or nil if the last pattern match failed. (Mnemonic: like & in some editors.) This variable is r					network = $1					freqname = $2					source = $3					nickname = $4					text = $5					if not network =~ /^(QWalt)|(QDEV)/						write_user("SYS Network name #{network} is unknown: QWalt or QDEV allowed.")						error = 1					end					if not freqname =~ /^(-qw-)|(-spam-)|(-dev-)/						write_user("SYS Frequency name is unknown #{freqname}")						error = 1					end					if source =~ /^(#.+)|(qw:\/\/)|(http:\/\/)/          else						write_user("SYS Source string is not in the form ^(#.+)|(qw:\/\/)|(http:\/\/)  was: #{source}")						error = 1					end					if not nickname =~ /^.+/						write_user("SYS Nickname string is not in the form ^.+  #{nickname}")						error = 1					end					if not text =~ /^.+/						write_user("SYS Message string is not in the form ^.+ #{text}")						error = 1					end				else # of check syntax					write_user("SYS Command format is REQ_BC <network>,<frequency>,<source/channel>,'<nickname>','<message>'")					error = 1				end				if error == 0					# send REQ_BC to the corresponding role.					bcid = Digest::MD5.hexdigest("%d %s %s %s %s %s" % [Time.now.utc.to_i, network, freqname, source, nickname, text])					# so it only reaches the issuer of REQ_BC					write_user("BC_ID #{bcid} for: #{network},#{freqname},#{source}")					@@broadcasts[bcid] = @username          # qw:// ip resolving          if source =~ /^qw:\/\/(.+):(\d+)(.*)$/            # ip address is 15 in length            iphost = $1            ipport = $2            rest = $3            if iphost =~ /^\d+\.\d+\.\d+\.\d+/              # find country code              c = GeoIP.new('GeoIP.dat').country(iphost)[3].to_s.downcase              rest.prepend " (#{c}) " unless c.nil?              # resolve it to a dns name              f = Fiber.new do                Fiber.yield Resolv.getname(iphost)              end              domainname = f.resume              #domainname = ""              if domainname.nil? || domainname.empty?                domainname = iphost              else                put_log "domainname: #{domainname}"                # if the resulting dns is too long, use ip-address instead.                # if the resulting dns has too many dots, use ip-address instead.                if domainname.length > 23 || domainname.split(".").size > 3 || domainname.scan(/\d/).size > 3                  put_log "cutting down host_name: #{domainname}, because:"                  put_log "#{domainname.length}"                  put_log "#{domainname.split(".").size}"                  put_log "#{domainname.scan(/\d/).size}"                  domainname = iphost                end              end              source = "qw://#{domainname}:#{ipport}#{rest}"            end          end # of if source qw://x.x.x.x:portzzz          # resolve					finalmessage = "BC %s %s,%s,%s,'%s','%s'" % [bcid, network, freqname, source, nickname, text]					write_role("broadcast", finalmessage, @username) # write to the role with the exempt of myself.				end				#end of REQ_BC here..			elsif cmd == "BC_RE"				if payload =~ /^(.+) (.+)=(\d+),(.+)=(\d+)$/					bcid = $1					user_string = $2					user_count = $3					item_string = $4					item_count = $5					payload = "%s %s=%d,%s=%d" % [bcid, user_string, user_count, item_string, item_count]					# according bcid it is possible to get the originating user.. so send only to his topic!					if @@broadcasts[bcid]						to_user = @@broadcasts[bcid]						write_user("SYS Broadcast reply bcid: #{bcid} underway to #{to_user}.")						write_user("#{cmd} #{payload}", to_user)					else						write_user("SYS Broadcast reply bcid: #{bcid} underway.")						write_role("broadcast", "#{cmd} #{payload}", @username)					end				else # of bc_re format check					put_log "SYS Format is BC_RE <bcid> <userstring>=<usercount>,<itemstring>=<itemcount>"				end			end		else  # of if allowed command			write_user("SYS Command #{input} not allowed, your commands are: #{my_cmds.join(", ")}.")		end  # of if allowed command	end # of inputting!	# default method that is being run on receiving data!	def receive_data(data) # connection receives a line on the server end    line = data.chomp unless data.chomp.nil?    if line      if entered_username? # oh, it's a known user        # it's alive!        inputter = inputting(line)        if inputter == "bye"          close_connection        end      else # of username known, then it seems to be the first connect        # and this line is the user name coming in!        username = line        if online(username)          put_log "SYS User '#{username}' tried to double connect. Say bye bye."          send_data "SYS Hey '#{username}', only one connection allowed. Bye.\n"          EventMachine.add_timer 1, proc { close_connection }          return false        end        if $user_roles.any? {|k| k.include? username}          @username = username          @@connected_clients.push(self)          put_log("SYS '#{@username}' connected. Online now: #{ousers.join(", ")}")          write_user("HELLO Hi user '#{@username}'! How are you? I'm '#{$version}'")          write_user("ROLES #{my_roles.join(", ")}")          write_user("COMMANDS #{my_cmds.join(", ")}")          write_role($default_role, "JOINED User '#{@username}' just joined the party.", @username)        else          put_log "SYS User '#{username}' unknown to me. Saying bye."          send_data "SYS User '#{username}' unknown to me. Bye.\n"          # disconnect him          close_connection        end      end # of username known    end # of line	end	# default method that is being run on disconnection!	def unbind # a connection goes bye bye		if @username			@@connected_clients.delete(self)			put_log "SYS '#{@username}' disconnected. Online now: #{ousers.join(", ")}"		end	endenddef main	# #run: Note that this will block current thread.	EventMachine.run do    EventMachine.start_server("0.0.0.0", 7337, InterconnectionPointProtocolHandler)	endendmain
 |