Class: Muby::Connection
- Inherits:
-
Object
- Object
- Muby::Connection
- Includes:
- Configurable, Displayer, Logger
- Defined in:
- lib/muby/connection.rb
Overview
The class that encapsulates the actual Connection
Constant Summary collapse
- TELNET_COMMANDS =
{ 240 => :end_of_sub_negotiation, 241 => :noop, 242 => :data_mark, 243 => :break, 249 => :go_ahead, 250 => :sub_negotiation, 251 => :will, 252 => :wont, 253 => :do, 254 => :dont, 255 => :iac }
Instance Method Summary collapse
- #append_show_buffer(c) ⇒ Object
- #close ⇒ Object
-
#connect! ⇒ Object
Almost ripped from the documentation: www.ruby-doc.org/core/classes/Socket.src/M002114.html.
- #display_buffer ⇒ Object
- #feed(s) ⇒ Object
-
#gag(matchBuffer) ⇒ Object
Check if we want to gag the current matchBuffer and debug about it.
- #getc ⇒ Object
- #handle(c) ⇒ Object
-
#handle_regular_character(c) ⇒ Object
This is not telnet command OR ansi, lets treat it as nice MUD text!.
- #homemade_split(s, r) ⇒ Object
-
#initialize(inputWindow, outputWindow, host, port) ⇒ Connection
constructor
Set it up with windows, handle and host info.
-
#listen ⇒ Object
Keep listening to the remote socket and react accordingly.
- #manage_buffers(c) ⇒ Object
-
#nongag(matchBuffer) ⇒ Object
Check if we should definitely NOT gag the current matchBuffer, and debug about it.
-
#send(s) ⇒ Object
Just plain send the string we got.
-
#status(message) ⇒ Object
Display a status message in the outputwindow if wanted (conf.connection_status).
- #substitute(buffer, hash) ⇒ Object
-
#trigger(matchBuffer, hash, skip_used = true) ⇒ Object
Check if we should trigger a Proc on the current matchBuffer, and debug about it.
Methods included from Displayer
debug, error, exception, info, trace, warn
Methods included from Configurable
Methods included from Logger
Constructor Details
#initialize(inputWindow, outputWindow, host, port) ⇒ Connection
Set it up with windows, handle and host info.
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
# File 'lib/muby/connection.rb', line 16 def initialize(inputWindow, outputWindow, host, port) @inputWindow = inputWindow @outputWindow = outputWindow status("Connecting to " + host.inspect + ":" + port.inspect) @host = host @port = port connect! conf.connect_triggers.each do |command| @inputWindow.execute(command, @inputWindow, @outputWindow) end status("Connected to " + host.inspect + ":" + port.inspect) @matchBuffer = "" @showBuffer = [] @used_triggers = {} @listener = Thread.new do begin status("Listening thread started") listen status("Listening thread finished") rescue Exception => e exception(e) ensure close end end if @socket end |
Instance Method Details
#append_show_buffer(c) ⇒ Object
295 296 297 298 299 300 301 |
# File 'lib/muby/connection.rb', line 295 def append_show_buffer(c) if String === @showBuffer.last @showBuffer.last << c else @showBuffer << c end end |
#close ⇒ Object
318 319 320 321 322 323 324 325 326 327 |
# File 'lib/muby/connection.rb', line 318 def close if Thread.current != @listener && @listener && @listener.alive? status("Killing listening thread") @listener.kill end if @socket && !@socket.closed? status("Disconnecting our end") @socket.close if @socket end end |
#connect! ⇒ Object
Almost ripped from the documentation: www.ruby-doc.org/core/classes/Socket.src/M002114.html
46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
# File 'lib/muby/connection.rb', line 46 def connect! @socket = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0) addr = Socket.sockaddr_in(@port, @host) begin @socket.connect_nonblock(addr) rescue Errno::EINPROGRESS IO.select(nil, [@socket], nil, conf.connect_timeout) begin @socket.connect_nonblock(addr) rescue Errno::EISCONN rescue Errno::EALREADY raise "Connection timed out" end end end |
#display_buffer ⇒ Object
376 377 378 379 380 381 382 383 384 385 |
# File 'lib/muby/connection.rb', line 376 def display_buffer unless @showBuffer.empty? if !gag(@matchBuffer) || nongag(@matchBuffer) @showBuffer = substitute(@showBuffer, conf.remote_substitutions) Muby::Completer.get_instance.store(@matchBuffer) if conf.feed_completer_with_input @outputWindow.print(*@showBuffer) end @showBuffer = [] end end |
#feed(s) ⇒ Object
140 141 142 143 144 145 |
# File 'lib/muby/connection.rb', line 140 def feed(s) s.unpack("C*").each do |c| handle(c) end nil end |
#gag(matchBuffer) ⇒ Object
Check if we want to gag the current matchBuffer and debug about it.
Remove the gag regexp if it is broken.
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 |
# File 'lib/muby/connection.rb', line 76 def gag(matchBuffer) returnValue = false conf.gags.each do |gag| begin if matchBuffer.match(gag) trace(matchBuffer + " matches " + gag.inspect + ": gag active") returnValue = true end rescue RegexpError => error conf.gags.delete(key) exception(e) end end returnValue end |
#getc ⇒ Object
147 148 149 150 151 |
# File 'lib/muby/connection.rb', line 147 def getc c = @socket.getc log_input(c.chr) if c c end |
#handle(c) ⇒ Object
181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 |
# File 'lib/muby/connection.rb', line 181 def handle(c) # # The telnet support (just so we dont explode or something) # # We are just plain ignoring most of it atm. # if TELNET_COMMANDS[c] == :iac # this is an "interpret as command" c = getc if (next_command = TELNET_COMMANDS[c]) == :iac # if the next one as well is an iac, we actually want to display a 255 handle_regular_character(c) else case next_command when :do # ignore the option negotiation c = getc when :dont # ignore the option negotiation c = getc when :will # ignore the option negotiation c = getc when :wont # ignore the option negotiation c = getc when :noop # waf? # do nothing when :data_mark warn("Got a data_mark (TELNET protocol) which I don't know how to handle. Expect strange behaviour...") when :break warn("Got a break (TELNET protocol) which I don't know how to handle. Expect strange behaviour...") when :go_ahead display_buffer # on a go ahead the server obviously wants us to display the @displayBuffer regardless of our conf.flush setting when :sub_negotiation # we just skip ahead to the end of the sub negotiation while TELNET_COMMANDS[c] != :end_of_sub_negotiation c = getc end else warn("Got an unknown TELNET command (#{c.chr}) which I don't know how to handle. Expect strange behaviour...") end end elsif c == 27 # escape char! this is probably some ANSI color or shite c = getc if c.chr == "[" # this is an ansi-something that is more than one char long ansiString = "" while !"cnRhl()HABCfsurhgKJipm".include?((c = getc).chr) ansiString << c.chr end if c.chr == "m" && Ncurses.has_colors? # ah, text property! i understand this! properties = ansiString.split(";") attributes = 0 bgcolor = false fgcolor = false reset = properties.index("0") if reset properties.delete("0") end properties.each do |property| case property.to_i when 1 attributes = attributes | Ncurses.const_get("A_BOLD") when 2 attributes = attributes | Ncurses.const_get("A_DIM") when 4 attributes = attributes | Ncurses.const_get("A_UNDERLINE") when 5 attributes = attributes | Ncurses.const_get("A_BLINK") unless conf.disable_blink when 7 attributes = attributes | Ncurses.const_get("A_REVERSE") when 8 attributes = attributes | Ncurses.const_get("A_INVIS") when 30 fgcolor = Ncurses.const_get("COLOR_BLACK") when 31 fgcolor = Ncurses.const_get("COLOR_RED") when 32 fgcolor = Ncurses.const_get("COLOR_GREEN") when 33 fgcolor = Ncurses.const_get("COLOR_YELLOW") when 34 fgcolor = Ncurses.const_get("COLOR_BLUE") when 35 fgcolor = Ncurses.const_get("COLOR_MAGENTA") when 36 fgcolor = Ncurses.const_get("COLOR_CYAN") when 37 fgcolor = Ncurses.const_get("COLOR_WHITE") when 40 bgcolor = Ncurses.const_get("COLOR_BLACK") when 41 bgcolor = Ncurses.const_get("COLOR_RED") when 42 bgcolor = Ncurses.const_get("COLOR_GREEN") when 43 bgcolor = Ncurses.const_get("COLOR_YELLOW") when 44 bgcolor = Ncurses.const_get("COLOR_BLUE") when 45 bgcolor = Ncurses.const_get("COLOR_MAGENTA") when 46 bgcolor = Ncurses.const_get("COLOR_CYAN") when 47 bgcolor = Ncurses.const_get("COLOR_WHITE") end end style = nil if reset style = Muby::Style.new(attributes, fgcolor, bgcolor, false) else style = Muby::Style.new(attributes, fgcolor, bgcolor, true) end @showBuffer.push(style) end end elsif !conf.broken_keycodes.include?(c) handle_regular_character(c) end end |
#handle_regular_character(c) ⇒ Object
This is not telnet command OR ansi, lets treat it as nice MUD text!
Debug about it, add it to match buffer, add it to show buffer and show if we want to show. Then check it for triggers.
175 176 177 178 179 |
# File 'lib/muby/connection.rb', line 175 def handle_regular_character(c) @matchBuffer = @matchBuffer + c.chr append_show_buffer(c.chr) manage_buffers(c) end |
#homemade_split(s, r) ⇒ Object
341 342 343 344 345 346 347 348 349 350 |
# File 'lib/muby/connection.rb', line 341 def homemade_split(s, r) rval = [] rest = s while match = rest.match(/(.*?)#{r.source}(.*)/) rval << match[1] rest = match[2] end rval << rest rval end |
#listen ⇒ Object
Keep listening to the remote socket and react accordingly.
306 307 308 309 310 311 312 313 314 315 316 |
# File 'lib/muby/connection.rb', line 306 def listen c = true # We have to look at each byte for ncurses reasons as well as triggers etc. while c while select([@socket],nil,nil,1) && c = getc handle(c) end end status("Connection closed by remote end") @inputWindow.disconnect end |
#manage_buffers(c) ⇒ Object
329 330 331 332 333 334 335 336 337 338 339 |
# File 'lib/muby/connection.rb', line 329 def manage_buffers(c) trigger(@matchBuffer, conf.remote_character_triggers, true) if c == 10 || conf.flush display_buffer if c == 10 trigger(@matchBuffer, conf.remote_triggers, false) @matchBuffer = "" @used_triggers = {} end end end |
#nongag(matchBuffer) ⇒ Object
Check if we should definitely NOT gag the current matchBuffer, and debug about it.
Remove the nongag regexp if it is broken.
97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 |
# File 'lib/muby/connection.rb', line 97 def nongag(matchBuffer) returnValue = false conf.anti_gags.each do |nongag| begin if matchBuffer.match(nongag) trace(matchBuffer + " matches " + nongag.inspect + ": nongag active") returnValue = true end rescue RegexpError => error conf.anti_gags.delete(key) exception(e) end end returnValue end |
#send(s) ⇒ Object
Just plain send the string we got.
390 391 392 393 394 395 396 397 398 |
# File 'lib/muby/connection.rb', line 390 def send(s) log_output(s) conf.local_substitutions.each do |key, value| # It might be useful to allow colour codes for highlighting local substitutions.. but only when echo is on. # No, cause this only gets sent to the server, it never shows up to the user. s.gsub!(key, value) end @socket.print(s) if @socket end |
#status(message) ⇒ Object
Display a status message in the outputwindow if wanted (conf.connection_status)
65 66 67 68 69 |
# File 'lib/muby/connection.rb', line 65 def status() if conf.connection_status @outputWindow.print_on_newline(*[ + "\n"]) end end |
#substitute(buffer, hash) ⇒ Object
352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 |
# File 'lib/muby/connection.rb', line 352 def substitute(buffer, hash) return_value = [] buffer.each do |part| if String === part part_to_append = [part] hash.each do |regexp, substitution| case substitution when String part_to_append = [part.gsub(regexp, substitution)] when Array if part.match(regexp) split_part = homemade_split(part, regexp) part_to_append = split_part.zip([substitution] * (split_part.size - 1)).flatten.compact end end end return_value += part_to_append else return_value << part end end return_value end |
#trigger(matchBuffer, hash, skip_used = true) ⇒ Object
Check if we should trigger a Proc on the current matchBuffer, and debug about it.
Remove the trigger regexp if it is broken.
118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 |
# File 'lib/muby/connection.rb', line 118 def trigger(matchBuffer, hash, skip_used = true) hash.each do |key, value| if skip_used && @used_triggers.include?(key) trace("#{key.inspect} has already been matched on this line, skipping it") else trace("checking if " + matchBuffer + " matches " + key.inspect) begin if match = matchBuffer.match(key) trace(matchBuffer + " matches " + key.inspect + ": .call'ing Proc") # Run the procedure associated with that trigger: @inputWindow.execute(value, @inputWindow, @outputWindow, match) @used_triggers[key] = true if skip_used end # If we received an error with the regular expression: rescue RegexpError => error hash.delete(key) exception(error) end end end end |