ratchet 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. data/gem_bin/ratchet +23 -0
  2. data/lib/ratchet.rb +613 -0
  3. data/lib/ratchet/aliases.rb +106 -0
  4. data/lib/ratchet/bufferparser.rb +409 -0
  5. data/lib/ratchet/commandbuffer.rb +66 -0
  6. data/lib/ratchet/commandparser.rb +668 -0
  7. data/lib/ratchet/configuration.rb +278 -0
  8. data/lib/ratchet/connections.rb +403 -0
  9. data/lib/ratchet/constants.rb +111 -0
  10. data/lib/ratchet/contrib/instance_exec.rb +21 -0
  11. data/lib/ratchet/eventparser.rb +486 -0
  12. data/lib/ratchet/gtk/bufferlistview.rb +514 -0
  13. data/lib/ratchet/gtk/bufferview.rb +167 -0
  14. data/lib/ratchet/gtk/configwindow.rb +229 -0
  15. data/lib/ratchet/gtk/connectionwindow.rb +218 -0
  16. data/lib/ratchet/gtk/keybinding.rb +356 -0
  17. data/lib/ratchet/gtk/linkwindow.rb +137 -0
  18. data/lib/ratchet/gtk/mainwindow.rb +504 -0
  19. data/lib/ratchet/gtk/networkpresenceconf.rb +567 -0
  20. data/lib/ratchet/gtk/pluginconfig.rb +94 -0
  21. data/lib/ratchet/gtk/pluginwindow.rb +146 -0
  22. data/lib/ratchet/gtk/userlistview.rb +161 -0
  23. data/lib/ratchet/help.rb +64 -0
  24. data/lib/ratchet/items.rb +271 -0
  25. data/lib/ratchet/lines.rb +63 -0
  26. data/lib/ratchet/networks.rb +652 -0
  27. data/lib/ratchet/plugins.rb +616 -0
  28. data/lib/ratchet/queue.rb +47 -0
  29. data/lib/ratchet/ratchet-version.rb +21 -0
  30. data/lib/ratchet/replies.rb +134 -0
  31. data/lib/ratchet/replyparser.rb +441 -0
  32. data/lib/ratchet/tabcomplete.rb +98 -0
  33. data/lib/ratchet/users.rb +237 -0
  34. data/lib/ratchet/utils.rb +178 -0
  35. data/share/defaults.yaml +169 -0
  36. data/share/glade/config.glade +2634 -0
  37. data/share/glade/connect.glade +950 -0
  38. data/share/glade/keybindings.glade +109 -0
  39. data/share/glade/linkwindow.glade +188 -0
  40. data/share/glade/mainwindow.glade +335 -0
  41. data/share/glade/network-presences.glade +1373 -0
  42. data/share/glade/pluginconf.glade +97 -0
  43. data/share/glade/plugins.glade +360 -0
  44. data/share/plugins/colorewrite.rb +193 -0
  45. data/share/plugins/highlighter.rb +115 -0
  46. data/share/plugins/mpdplay.rb +123 -0
  47. data/share/plugins/numberswitcher.rb +30 -0
  48. data/share/plugins/sysinfo.rb +82 -0
  49. metadata +96 -0
@@ -0,0 +1,23 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ #get the datadir, which is relative to this file's location
4
+ DATADIR = File.expand_path(__FILE__).
5
+ split(File::SEPARATOR)[0...-2].join(File::SEPARATOR)+File::SEPARATOR+'share'
6
+ begin
7
+ require 'ratchet'
8
+ rescue LoadError
9
+ require 'rubygems'
10
+ require_gem 'ratchet'
11
+ end
12
+
13
+ $0 = "ratchet"
14
+
15
+ #start the ball rolling...
16
+ begin
17
+ main = Ratchet::Main.new
18
+ main.start
19
+ rescue Interrupt => detail
20
+ puts 'got keyboard interrupt'
21
+ main.windows.each{|win| win.quit(false)}
22
+ main.quit
23
+ end
@@ -0,0 +1,613 @@
1
+ =begin
2
+ This file is part of the Ratchet project, a client for Icecap.
3
+ Copyright (C) 2005-6 Andrew Thompson
4
+
5
+ This program is free software; you can redistribute it and/or modify
6
+ it under the terms of the GNU General Public License as published by
7
+ the Free Software Foundation; either version 2 of the License, or
8
+ (at your option) any later version.
9
+
10
+ This program is distributed in the hope that it will be useful,
11
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ GNU General Public License for more details.
14
+
15
+ You should have received a copy of the GNU General Public License
16
+ along with this program; if not, write to the Free Software
17
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
+ =end
19
+
20
+ #### ratchet.rb ####
21
+ # This file contains the core methods of the Ratchet::Main class,
22
+ # which is the object at the heart of a ratchet instance
23
+ ####
24
+
25
+ require 'libglade2'
26
+ require 'socket'
27
+ require 'rbconfig'
28
+ require 'base64'
29
+ require 'thread'
30
+ require 'monitor'
31
+ require 'yaml'
32
+ require 'scw'
33
+ require 'observer'
34
+ require 'pathname'
35
+
36
+ require 'ratchet/ratchet-version'
37
+
38
+ require 'ratchet/utils'
39
+
40
+ require 'ratchet/contrib/instance_exec'
41
+
42
+ if RUBY_PLATFORM.include?('win32') or RUBY_PLATFORM.include?('mingw32')
43
+ $platform = 'win32'
44
+ $ratchetfolder = File.join(ENV['APPDATA'], 'ratchet')
45
+ else
46
+ $platform = 'linux'
47
+ $ratchetfolder = File.join(ENV['HOME'], '.ratchet')
48
+ end
49
+
50
+ # begin
51
+ # $:.unshift "lib"
52
+ # require 'net/ssh'
53
+ # $netssh = true
54
+ # rescue LoadError
55
+ $netssh = false
56
+ # end
57
+
58
+ $args = {}
59
+
60
+ Thread.current.priority = 1
61
+
62
+ def parse_args
63
+ args = ARGV
64
+ args.each do |arg|
65
+ while arg[0].chr == '-'
66
+ arg = arg[1, arg.length]
67
+ end
68
+ name, value = arg.split('=')
69
+ if value
70
+ $args[name] = value
71
+ else
72
+ $args[name] = true
73
+ end
74
+ end
75
+ end
76
+
77
+ parse_args
78
+
79
+ #useful for debugging
80
+ Thread.abort_on_exception = true
81
+
82
+ #load all my home rolled ruby files here
83
+ require 'ratchet/help'
84
+ require 'ratchet/constants'
85
+ require 'ratchet/queue'
86
+ require 'ratchet/lines'
87
+ require 'ratchet/configuration'
88
+ require 'ratchet/items'
89
+ require 'ratchet/bufferparser'
90
+ require 'ratchet/plugins'
91
+ require 'ratchet/aliases'
92
+ require 'ratchet/commandparser'
93
+ require 'ratchet/eventparser'
94
+ require 'ratchet/replyparser'
95
+ require 'ratchet/tabcomplete'
96
+ require 'ratchet/users'
97
+ require 'ratchet/gtk/userlistview'
98
+ require 'ratchet/gtk/bufferview'
99
+ require 'ratchet/commandbuffer'
100
+ # #require 'ratchet/buffers'
101
+ require 'ratchet/networks'
102
+ require 'ratchet/gtk/bufferlistview'
103
+ require 'ratchet/replies'
104
+ require 'ratchet/connections'
105
+ require 'ratchet/gtk/keybinding'
106
+ require 'ratchet/gtk/mainwindow'
107
+ require 'ratchet/gtk/configwindow'
108
+ require 'ratchet/gtk/connectionwindow'
109
+ require 'ratchet/gtk/networkpresenceconf'
110
+ require 'ratchet/gtk/linkwindow'
111
+ require 'ratchet/gtk/pluginwindow'
112
+ require 'ratchet/gtk/pluginconfig'
113
+
114
+ module Ratchet
115
+ class Main
116
+ attr_reader :windows, :replies, :connectionwindow, :drift, :config, :console, :networks, :protocols, :buffers
117
+ #extend Plugins
118
+ include PluginAPI
119
+ include EventParser
120
+ include ReplyParser
121
+ #include CommandParser
122
+
123
+ def initialize
124
+ @connection = nil
125
+ @replies = {}
126
+ @keys = {}
127
+ @drift = 0
128
+ @config = Configuration.new
129
+ @windows = []
130
+ @networks = ItemList.new(Network)
131
+ @protocols = ItemList.new(Protocol)
132
+ @inputqueue = MessageQueue.new
133
+ @outputqueue = MessageQueue.new
134
+ @console = ConsoleBuffer.new(self)
135
+
136
+ @buffers = {}
137
+ @inputwatcher = Watcher.new(@inputqueue) {|x| command_parse(*x)}
138
+ @outputwatcher = Watcher.new(@outputqueue) {|x| handle_output(x)}
139
+ Plugin.main = self
140
+ end
141
+
142
+ # start the GUI
143
+ def start
144
+ Gtk.init
145
+ settings = Gtk::Settings.default
146
+ settings.gtk_entry_select_on_focus = false
147
+
148
+ reply_reaper
149
+ @connectionwindow = ConnectionWindow.new(self) unless @connectionwindow and @connectionwindow.open?
150
+ if @connectionwindow.autoconnect == true
151
+ @connectionwindow.start_connect
152
+ end
153
+ Gtk.main
154
+ end
155
+
156
+ #connect to icecap, called from connectionwindow
157
+ def connect(type, settings)
158
+ return if @connection
159
+ @connectionwindow.send_text('Connecting...')
160
+ begin
161
+ @connection = ConnectionFactory.spawn(type, self, settings, @connectionwindow)
162
+ rescue IOError
163
+ @connectionwindow.send_text("Error: "+$!)
164
+ return
165
+ rescue ArgumentError
166
+ @connectionwindow.send_text("Error: "+$!+" This is a bug. Please report it.")
167
+ return
168
+ end
169
+
170
+ @connectionwindow.send_text("Connected to irssi2!")
171
+ @connection.listen(self)
172
+
173
+ #@config.get_config
174
+ unless $args['noconfig']
175
+ send_command('getconfig', 'config get;*')
176
+ while @replies['getconfig']
177
+ sleep 1
178
+ end
179
+ end
180
+ restyle
181
+ @console.buffer.redraw
182
+
183
+ @config['plugins'].each {|plugin| plugin_load(plugin)}
184
+ @config['plugins'] = Plugin.list.values.map{|x| x[:name]}#trim any plugins that failed to load
185
+ @config['windows'].each do |hash|
186
+ puts hash.inspect
187
+ window = MainWindow.new(self, hash)
188
+ @windows.push(window)
189
+ window.draw_from_config
190
+ end
191
+
192
+ @connectionwindow.destroy
193
+
194
+ send_command('protocols', 'protocol list')
195
+ end
196
+
197
+ # Creates a thread which polls events without completed replies and
198
+ # processes depending on the state of the reply.
199
+ # TODO: This is an ugly function
200
+ def reply_reaper
201
+ # Create the thread, turn down the priority, and loop forever
202
+ @reaperthread = Thread.new do
203
+ Thread.current.priority = -3
204
+ while true
205
+ @replies.each do |key, reply|
206
+ if reply.complete
207
+ @replies.delete(key)
208
+ reply_parse(reply)
209
+ elsif (Time.new - reply.start).to_i > 15
210
+ if reply.retries < 1
211
+ @replies.delete(key)
212
+ send_command(key, reply.origcommand)
213
+ @replies[key].retries = reply.retries+1
214
+ else
215
+ @replies.delete(key)
216
+ end
217
+ end
218
+ end
219
+ sleep 5
220
+ end
221
+ end
222
+ end
223
+
224
+
225
+ #TODO: This is an ugly function
226
+ def syncchannels
227
+ @syncchannels = true
228
+ Thread.new do
229
+ Thread.current.priority = -2
230
+ @windows.each do |win|
231
+ win.buffers.channels.each do |channel|
232
+ if !channel.usersync and channel.joined?
233
+ send_command('listchan-'+channel.network.name+channel.name, "channel names;network="+channel.network.name+";channel="+channel.name+";mypresence="+channel.presence)
234
+ while !channel.usersync
235
+ sleep 2
236
+ end
237
+ end
238
+ end
239
+
240
+ win.buffers.channels.each do |channel|
241
+ if !channel.eventsync and channel.joined?
242
+ send_command('events-'+channel.network.name+channel.name, 'event get;end=*;limit=100;filter=&(channel='+channel.name+')(network='+channel.network.name+')(mypresence='+channel.presence+')(!(|(event=client_command_reply)(init=*)(deinit=*)(raw=*)))(time>1)')
243
+ while !channel.eventsync
244
+ sleep 2
245
+ end
246
+ end
247
+ end
248
+
249
+ #~ @serverlist.servers.each do |server|
250
+ #~ server.channels.each do |channel|
251
+ #~ if !channel.usersync and channel.connected
252
+ #~ send_command('listchan-'+server.name+channel.name, "channel names;network="+server.name+";channel="+channel.name+";mypresence="+server.presence)
253
+ #~ while !channel.usersync
254
+ #~ sleep 2
255
+ #~ end
256
+ #~ end
257
+ #~ end
258
+
259
+ #~ server.channels.each do |channel|
260
+ #~ if !channel.eventsync and channel.connected
261
+ #~ send_command('events-'+server.name+channel.name, 'event get;end=*;limit=100;filter=&(channel='+channel.name+')(network='+server.name+')(mypresence='+server.presence+')(!(|(event=client_command_reply)(init=*)(deinit=*)(raw=*)))(time>1)')
262
+ #~ while !channel.eventsync
263
+ #~ sleep 2
264
+ #~ end
265
+ #~ end
266
+ #~ end
267
+ #~ if server.connected
268
+ #~ end
269
+ #~ end
270
+ end
271
+ end
272
+ @syncchannels = nil
273
+ end
274
+
275
+ # Resets the scw configuration after a ratchet configuration change
276
+ # This is an ugly function, just by the means it has to be done,
277
+ # but there's nothing that can be done about it for the time being
278
+ # since the SCW API is shitty in this respect.
279
+ def restyle
280
+ even = @config['scw_even'].to_hex
281
+ odd = @config['scw_odd'].to_hex
282
+
283
+ # puts even, odd
284
+
285
+ Gtk::RC.parse_string("style \"scwview\" {\
286
+ ScwView::even-row-color = \"#{even}\"\
287
+ ScwView::odd-row-color = \"#{odd}\"\
288
+ ScwView::column-spacing = 5\
289
+ ScwView::row-padding = 2\
290
+ }\n\
291
+ widget \"*.ScwView\" style \"scwview\"")
292
+
293
+ end
294
+
295
+ #CRITICAL TODO: AUGH MY EYES!
296
+ def remove_buffer(buffer)
297
+ @buffers.delete_if{|k,v| v == buffer}
298
+ end
299
+
300
+ def add_buffer(*key)
301
+ if buffer = find_buffer(*key)
302
+ return buffer
303
+ end
304
+ #puts key.inspect
305
+ if key[2]
306
+ testkey = key[0..2]
307
+ if network = find_buffer(*key[0..1])
308
+ buffer = ChannelBuffer.new(key[2], network, self)
309
+ else
310
+ puts "undefined network #{key[0..1].inspect}"
311
+ end
312
+ elsif key[3]
313
+ testkey = [key[0], key[1], key[3]]
314
+ if network = find_buffer(*key[0..1])
315
+ buffer = ChatBuffer.new(key[3], network, self)
316
+ else
317
+ puts "undefined network #{key[0..1].inspect}"
318
+ end
319
+ else
320
+ testkey = key[0..1]
321
+ buffer = NetworkBuffer.new(key[0], key[1], self)
322
+ end
323
+ assign_buffer_to_window(buffer) if buffer
324
+ @buffers[testkey] = buffer
325
+ buffer
326
+ end
327
+
328
+ def find_buffer(*key)
329
+ if key[2]
330
+ testkey = key[0..2]
331
+ elsif key[3]
332
+ testkey = [key[0], key[1], key[3]]
333
+ else
334
+ testkey = key[0..1]
335
+ end
336
+
337
+ @buffers[testkey]
338
+ end
339
+
340
+ def assign_buffer_to_window(buffer)
341
+ #TODO - filter to allow intelligent buffer assignment
342
+ @windows[0].buffers.add_buffer(buffer)#if @windows[0]
343
+ end
344
+
345
+ def reassign_buffer_to_window(buffer, window)
346
+ #TODO
347
+ end
348
+
349
+ def find_windows_with_buffer(buffer)
350
+ res = []
351
+ @windows.each do |window|
352
+ res << window if window.buffers.include? buffer
353
+ end
354
+ res
355
+ end
356
+
357
+ #what do do when we get disconnected from icecap
358
+ def disconnect
359
+ @connection.close if @connection
360
+ @connection = nil
361
+ @serverlist.servers.each do |server|
362
+ server.disconnect
363
+ server.channels.each {|channel| channel.disconnect}
364
+ end
365
+ @connectionwindow = ConnectionWindow.new unless @connectionwindow and @connectionwindow.open?
366
+ end
367
+
368
+ #connect to a network
369
+ def network_add(name, protocol, address, port)
370
+ unless @serverlist.get_network_by_name(name)
371
+ send_command('addnet', "network add;network="+name+";protocol="+protocol)
372
+ temp = "gateway add;host="+address+";network="+name
373
+ temp += ";port="+port if port != '' and port
374
+ send_command('addhost', temp)
375
+ @networks.add(name, protocol)
376
+ else
377
+ throw_error('Network '+network+' is already defined')
378
+ end
379
+ end
380
+
381
+ def network_connect(network, presence)
382
+ if !@serverlist.get_network_by_name(network) and !@networks[network]
383
+ throw_error('Undefined network '+network)
384
+ elsif !@serverlist[network, presence] and !@networks[network].presences[presence]
385
+ throw_error('Undefined presence '+presence)
386
+ else
387
+ send_command('connect', "presence connect;network="+network+";mypresence="+presence)
388
+ end
389
+ end
390
+
391
+ def presence_add(network, presence)
392
+ if !@serverlist.get_network_by_name(network) and !@networks[network]
393
+ throw_error('Undefined network '+network)
394
+ elsif @serverlist[network, presence] or @networks[network].presences[presence]
395
+ return true
396
+ else
397
+ cmdstring = "presence add;mypresence="+presence+";network="+network
398
+ if @keys[presence] and @keys[presence]['silc_pub']
399
+ cmdstring += ";pub_key="+@keys[presence]['silc_pub']+";prv_key="+@keys[presence]['silc_priv']
400
+ cmdstring += ";passphrase="+@keys[presence]['silc_pass'] if @keys[presence]['silc_pass']
401
+ end
402
+
403
+ cmdstring.gsub!("\n", "\\n")
404
+ send_command('addpres', cmdstring)
405
+ @networks[network].add_presence(presence)
406
+ return true
407
+ end
408
+ return false
409
+ end
410
+
411
+ def channel_add(network, presence, channel)
412
+ if @serverlist[network, presence] and channel
413
+ send_command('add', 'channel add;network='+network+';mypresence='+presence+';channel='+channel)
414
+ else
415
+ throw_error('Invalid Network')
416
+ end
417
+ end
418
+
419
+ def throw_error(error, buffer=@console)
420
+ line = Line[ERR => 'Client Error: '+error]
421
+ buffer.send_user_event(line, EVENT_ERROR)
422
+ end
423
+
424
+ def throw_message(message, buffer=@console)
425
+ line = Line[MSG => 'Client Message: '+message]
426
+ @console.send_user_event(line, EVENT_NOTICE)
427
+ end
428
+
429
+ def queue_input(msg)
430
+ @inputqueue.enq(msg)
431
+ end
432
+
433
+ def queue_output(msg)
434
+ @outputqueue.enq(msg)
435
+ end
436
+
437
+ def parse_line(line)
438
+ @console.send_user_event({'msg' =>line.chomp}, EVENT_NOTICE) if $args['debug']
439
+ queue_output(line)
440
+ end
441
+
442
+ #split by line and parse each line
443
+ def parse_lines(lines)
444
+ lines.each do |line|
445
+ @console.send_user_event({'msg' =>line.chomp}, EVENT_NOTICE) if $args['debug']
446
+ queue_output(line)
447
+ end
448
+ end
449
+
450
+ #send a command to irssi2
451
+ def send_command(tag, command, length=nil)
452
+ if !@connection
453
+ disconnect
454
+ return
455
+ end
456
+
457
+ @replies[tag] = Reply.new(self, tag, command)
458
+
459
+ if length
460
+ cmdstr = '+'+length.to_s+';'+tag+';'+command+"\n"
461
+ else
462
+ cmdstr = tag+';'+command+"\n"
463
+ end
464
+
465
+ puts "[SENT] #{cmdstr}" if $args['debug']
466
+
467
+ sent = @connection.send(cmdstr)
468
+
469
+ if !sent
470
+ @connection = nil
471
+ disconnect
472
+ end
473
+ return @replies[tag]
474
+ end
475
+
476
+ #handle output from icecap
477
+ def handle_output(string)
478
+ return if string.length == 0
479
+
480
+ tag, event = string.split(';', 2)
481
+
482
+ #its an event
483
+ if tag == '*'
484
+ type, event = event.split(';', 2)
485
+ #puts type, event
486
+ line= Line.new
487
+
488
+ line[:event_type] = type
489
+ line['original'] = string
490
+
491
+ items = event.split(';')
492
+
493
+ items.each do |x|
494
+ vals = x.split('=', 2)
495
+ if vals[1] and vals[1] != ''
496
+ line[vals[0].to_sym] = unescape(vals[1])
497
+ elsif x.count('=') == 0
498
+ line[x.to_sym] = true
499
+ end
500
+ end
501
+ calculate_clock_drift(line['time']) if line['time']
502
+
503
+ if @config['canonicaltime'] == 'client'
504
+ line[:time] = Time.at(line[:time].to_i + @drift)
505
+ end
506
+
507
+ event_parse(line)
508
+ #its a reply
509
+ else
510
+ if @replies[tag]
511
+ reply = @replies[tag]
512
+ Thread.new do
513
+ #Thread.current.priority = -3
514
+ reply.addline(string)
515
+ end
516
+ if @replies[tag] and @replies[tag].complete
517
+ Thread.new do
518
+ reply_parse(reply)
519
+ @replies.delete(reply.name)
520
+ end
521
+ end
522
+ end
523
+ return
524
+ end
525
+ end
526
+
527
+ #keep an eye on the difference between client & server time
528
+ #TODO: There could be a better way to do this
529
+ def calculate_clock_drift(servertime)
530
+ server = Time.at(servertime.to_i)
531
+ client = Time.new
532
+ @drift = (client - server).to_i
533
+ end
534
+
535
+ def handle_error(line, reply)
536
+ channel ||= reply.command['channel']
537
+ network ||= reply.command['network']
538
+ presence ||= reply.command['mypresence']
539
+
540
+ puts line
541
+ return
542
+
543
+ if network = assign_window.buffers.find_network(network, presence)
544
+ target = network
545
+ elsif @serverlist[network, presence] and channel = @serverlist[network, presence][channel]
546
+ target = channel
547
+ else
548
+ target = @window.networks.console
549
+ end
550
+
551
+ if line['bad']
552
+ err = 'Bad line - '+reply.origcommand
553
+ elsif line['args']
554
+ err = 'Bad arguments - '+reply.origcommand
555
+ elsif line['state']
556
+ err = 'Bad state - '+reply.origcommand
557
+ elsif line['unknown']
558
+ err = 'Unknown command - '+reply.command['command']
559
+ elsif line['nogateway']
560
+ err = 'No gateway'
561
+ err += ' for network - '+reply.command['network'] if reply.command['network']
562
+ elsif line['noprotocol']
563
+ err = 'Invalid Protocol'
564
+ err += ' - '+reply.command['protocol'] if reply.command['protocol']
565
+ elsif line['noconnection']
566
+ elsif line['nonetwork']
567
+ err = 'Invalid network'
568
+ err += ' - '+reply.command['network'] if reply.command['network']
569
+ elsif line['nopresence']
570
+ err = 'Invalid or protected presence'
571
+ err += ' - '+reply.command['mypresence'] if reply.command['mypresence']
572
+ elsif line['exists']
573
+ err ='Already Exists'
574
+ elsif line['notfound']
575
+ err = 'Not Found'
576
+ elsif line['reply_lost']
577
+ err = 'Reply to command - '+reply.origcommand+' lost.'
578
+ else
579
+ puts 'unhandled error '+line['original']
580
+ return
581
+ end
582
+
583
+ @window.networks.send_user_event(target, Line['err' => err], EVENT_ERROR)
584
+ end
585
+
586
+ def quit
587
+ #if the connection is dead, don't bother trying to comunicate with irssi2
588
+ if !@connection
589
+ do_quit
590
+
591
+ #update the config and wait for quit response
592
+ else
593
+ send_command('sendconfig', @config.changes)
594
+ send_command('quit', 'quit')
595
+ # puts 'sending quit (timeout 5 seconds...)'
596
+ # sleep 5
597
+ # unless @quit
598
+ # puts 'failed to get quit confirmation, doing it manually'
599
+ # do_quit
600
+ # end
601
+ end
602
+ true
603
+ end
604
+
605
+ def do_quit
606
+ @connection.close if @connection
607
+ @reaperthread.kill if @reaperthread
608
+ Gtk.main_quit
609
+ puts 'bye byeeeeee...'
610
+ exit
611
+ end
612
+ end
613
+ end