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.
- data/gem_bin/ratchet +23 -0
- data/lib/ratchet.rb +613 -0
- data/lib/ratchet/aliases.rb +106 -0
- data/lib/ratchet/bufferparser.rb +409 -0
- data/lib/ratchet/commandbuffer.rb +66 -0
- data/lib/ratchet/commandparser.rb +668 -0
- data/lib/ratchet/configuration.rb +278 -0
- data/lib/ratchet/connections.rb +403 -0
- data/lib/ratchet/constants.rb +111 -0
- data/lib/ratchet/contrib/instance_exec.rb +21 -0
- data/lib/ratchet/eventparser.rb +486 -0
- data/lib/ratchet/gtk/bufferlistview.rb +514 -0
- data/lib/ratchet/gtk/bufferview.rb +167 -0
- data/lib/ratchet/gtk/configwindow.rb +229 -0
- data/lib/ratchet/gtk/connectionwindow.rb +218 -0
- data/lib/ratchet/gtk/keybinding.rb +356 -0
- data/lib/ratchet/gtk/linkwindow.rb +137 -0
- data/lib/ratchet/gtk/mainwindow.rb +504 -0
- data/lib/ratchet/gtk/networkpresenceconf.rb +567 -0
- data/lib/ratchet/gtk/pluginconfig.rb +94 -0
- data/lib/ratchet/gtk/pluginwindow.rb +146 -0
- data/lib/ratchet/gtk/userlistview.rb +161 -0
- data/lib/ratchet/help.rb +64 -0
- data/lib/ratchet/items.rb +271 -0
- data/lib/ratchet/lines.rb +63 -0
- data/lib/ratchet/networks.rb +652 -0
- data/lib/ratchet/plugins.rb +616 -0
- data/lib/ratchet/queue.rb +47 -0
- data/lib/ratchet/ratchet-version.rb +21 -0
- data/lib/ratchet/replies.rb +134 -0
- data/lib/ratchet/replyparser.rb +441 -0
- data/lib/ratchet/tabcomplete.rb +98 -0
- data/lib/ratchet/users.rb +237 -0
- data/lib/ratchet/utils.rb +178 -0
- data/share/defaults.yaml +169 -0
- data/share/glade/config.glade +2634 -0
- data/share/glade/connect.glade +950 -0
- data/share/glade/keybindings.glade +109 -0
- data/share/glade/linkwindow.glade +188 -0
- data/share/glade/mainwindow.glade +335 -0
- data/share/glade/network-presences.glade +1373 -0
- data/share/glade/pluginconf.glade +97 -0
- data/share/glade/plugins.glade +360 -0
- data/share/plugins/colorewrite.rb +193 -0
- data/share/plugins/highlighter.rb +115 -0
- data/share/plugins/mpdplay.rb +123 -0
- data/share/plugins/numberswitcher.rb +30 -0
- data/share/plugins/sysinfo.rb +82 -0
- metadata +96 -0
|
@@ -0,0 +1,278 @@
|
|
|
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
|
+
#### configuration.rb ####
|
|
21
|
+
# This file contains the Connection class, a class that contains ratchet's
|
|
22
|
+
# config, it provides default values (loaded from a yaml file), it also
|
|
23
|
+
# tracks changes and allows reversion to defaults. Additionally it encodes/decodes
|
|
24
|
+
# data for storage in icecap's config storage.
|
|
25
|
+
####
|
|
26
|
+
|
|
27
|
+
module Ratchet
|
|
28
|
+
class Configuration
|
|
29
|
+
attr_reader :values, :defaults
|
|
30
|
+
def initialize
|
|
31
|
+
#set some defaults... probably too soon be overriden by the user's config, but you gotta start somewhere :P
|
|
32
|
+
p = Pathname.new(__FILE__)
|
|
33
|
+
@values = YAML.load_file(DATADIR+'/defaults.yaml')
|
|
34
|
+
|
|
35
|
+
#store defaults
|
|
36
|
+
@defaults = duplicate_config
|
|
37
|
+
|
|
38
|
+
@statuscolors = [@values['defaultcolor'], @values['neweventcolor'], @values['newmessagecolor'], @values['highlightcolor']]
|
|
39
|
+
|
|
40
|
+
@oldvalues = {}
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def dump
|
|
44
|
+
File.open(DATADIR+'/defaults.yaml', "w+") {|f| YAML.dump(@values, f)}
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def gettabmodelconfig
|
|
48
|
+
|
|
49
|
+
if @values['tabsort'] == 'case sensitive'
|
|
50
|
+
sort = SENSITIVE
|
|
51
|
+
elsif @values['tabsort'] == 'case sensitive no hash'
|
|
52
|
+
sort = INSENSITIVE_NOHASH
|
|
53
|
+
elsif @values['tabsort'] == 'case sensitive no hash'
|
|
54
|
+
sort = SENSITIVE_NOHASH
|
|
55
|
+
else
|
|
56
|
+
sort = INSENSITIVE
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
if @values['tabstructure'] == 'flat'
|
|
60
|
+
structure = FLAT
|
|
61
|
+
else
|
|
62
|
+
structure = HIERARCHICAL
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
return [structure, sort]
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
#converts status into a color
|
|
69
|
+
def getstatuscolor(status)
|
|
70
|
+
if status.nil?
|
|
71
|
+
return @statuscolors[0]
|
|
72
|
+
end
|
|
73
|
+
return @statuscolors[status]
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
#retrieves a value
|
|
77
|
+
def [](value)
|
|
78
|
+
if @values[value]
|
|
79
|
+
return @values[value]
|
|
80
|
+
else
|
|
81
|
+
return false
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def []=(key, value)
|
|
86
|
+
value = decode_value(value) if value.class == String
|
|
87
|
+
@values[key] = value
|
|
88
|
+
#update the color array on the off-chance we changed it :(
|
|
89
|
+
@statuscolors = [@values['defaultcolor'], @values['neweventcolor'], @values['newmessagecolor'], @values['highlightcolor']]
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
#send the config to irssi2
|
|
93
|
+
def changes
|
|
94
|
+
cmdstring = ''
|
|
95
|
+
|
|
96
|
+
configs = []
|
|
97
|
+
|
|
98
|
+
@values.each do |k, v|
|
|
99
|
+
value = encode_value(v)
|
|
100
|
+
if @oldvalues[k] != value or !@oldvalues[k]
|
|
101
|
+
key = 'rirc_'+k unless k['rirc_']
|
|
102
|
+
|
|
103
|
+
configs.push(key+'='+escape(value)) if k and value
|
|
104
|
+
puts k+" HAS changed"
|
|
105
|
+
puts v.inspect
|
|
106
|
+
else
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
if configs.length == 0
|
|
111
|
+
puts 'no changes'
|
|
112
|
+
return
|
|
113
|
+
else
|
|
114
|
+
cmdstring = 'config set;'+configs.join(';')
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
cmdstring
|
|
118
|
+
#$main.send_command('sendconfig', cmdstring)
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
#request the config from irssi2
|
|
122
|
+
#~ def get_config
|
|
123
|
+
#~ $main.send_command('getconfig', 'config get;*')
|
|
124
|
+
#~ while $main.replies['getconfig']
|
|
125
|
+
#~ sleep 1
|
|
126
|
+
#~ end
|
|
127
|
+
#~ end
|
|
128
|
+
|
|
129
|
+
#encode the values so they can be stored by irssi2
|
|
130
|
+
#this function recursively encodes arrays in hashes to handle n dimensional structures
|
|
131
|
+
def encode_value(value)
|
|
132
|
+
if value.class == Color
|
|
133
|
+
colors = value.to_a[0..2]
|
|
134
|
+
'color{' + colors.map {|color| "#{color}" }.join(":")+'}'
|
|
135
|
+
elsif value.class == Array
|
|
136
|
+
'array{' + value.map {|v| encode_value(v)}.join(":")+'}'
|
|
137
|
+
elsif value.class == Hash
|
|
138
|
+
'hash{' + value.map {|k, v| encode_value(k) + ':' + encode_value(v) }.join("::")+'}'
|
|
139
|
+
elsif value =~ /^(color|array|hash)\:(\d+)\:(\d+)\:(\d+)$/
|
|
140
|
+
return value
|
|
141
|
+
elsif value.nil? #we don't want nil being encoded to "", this fux0rs arrays...
|
|
142
|
+
return 'nil'
|
|
143
|
+
elsif value.respond_to? :to_s #everythign else gets converted to a string
|
|
144
|
+
return value.to_s.gsub(':', '\,')
|
|
145
|
+
else #if we can't convert it to a string, don't store it
|
|
146
|
+
return nil
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
#decode values retrieved from irssi2
|
|
151
|
+
#recursively decodes arrays and hashes, gets a bit hairy though...
|
|
152
|
+
def decode_value(value)
|
|
153
|
+
value = unescape(value) if value
|
|
154
|
+
if value == 'true'
|
|
155
|
+
return true
|
|
156
|
+
elsif value == 'false'
|
|
157
|
+
return false
|
|
158
|
+
elsif value == 'nil'
|
|
159
|
+
return nil
|
|
160
|
+
else
|
|
161
|
+
if value.nil?
|
|
162
|
+
return nil
|
|
163
|
+
end
|
|
164
|
+
values = value.split('{', 2)
|
|
165
|
+
values[1].chomp!('}') if values[1]
|
|
166
|
+
if values[0] == 'array'
|
|
167
|
+
x = []
|
|
168
|
+
#~ values[1].split(':').each do |v|
|
|
169
|
+
#~ if v =~ /^(array|hash|color)\{/
|
|
170
|
+
#~ puts v
|
|
171
|
+
#~ end
|
|
172
|
+
#~ x.push(decode_value(v))
|
|
173
|
+
#~ end
|
|
174
|
+
string = values[1]
|
|
175
|
+
while string
|
|
176
|
+
#check if the next part of the source string is an object
|
|
177
|
+
if string =~ /^((?:array|hash|color)\{.+?\}(?:\:|$))/
|
|
178
|
+
match = $1
|
|
179
|
+
#trim the trailing : and decode it and then push it onto the array
|
|
180
|
+
x.push(decode_value($1.chomp(':')))
|
|
181
|
+
#remove the match from the source string and skip to the next loop iteration
|
|
182
|
+
string.slice!(match)
|
|
183
|
+
next
|
|
184
|
+
end
|
|
185
|
+
part, string = string.split(':', 2)
|
|
186
|
+
x.push(decode_value(part)) if part
|
|
187
|
+
end
|
|
188
|
+
x
|
|
189
|
+
elsif values[0] == 'hash'
|
|
190
|
+
x = {}
|
|
191
|
+
#~ values[1].split('::').each do |y|
|
|
192
|
+
#~ k, v = y.split(':')
|
|
193
|
+
#~ x[decode_value(k)] = decode_value(v)
|
|
194
|
+
#~ end
|
|
195
|
+
string = values[1]
|
|
196
|
+
while string
|
|
197
|
+
#check for an object
|
|
198
|
+
if string =~ /^((?:array|hash|color)\{.+?\}.+?(?:\:\:|\}$))/
|
|
199
|
+
#store the match in a variable so it doesn't get clobbered later on
|
|
200
|
+
match = $1
|
|
201
|
+
#heh, trim the trailing :: and split by the last :
|
|
202
|
+
key, value = $1.chomp('::').reverse.split(':', 2).map{|y| y.reverse}.reverse
|
|
203
|
+
#stick it in the hash
|
|
204
|
+
x[decode_value(key)] = decode_value(value)
|
|
205
|
+
#remove the match from the string
|
|
206
|
+
string.slice!(match)
|
|
207
|
+
next
|
|
208
|
+
end
|
|
209
|
+
part, string = string.split('::', 2)
|
|
210
|
+
if part
|
|
211
|
+
k, v = part.split(':', 2)
|
|
212
|
+
x[decode_value(k)] = decode_value(v)
|
|
213
|
+
end
|
|
214
|
+
end
|
|
215
|
+
x
|
|
216
|
+
elsif values[0] == 'color'
|
|
217
|
+
r, g, b = values[1].split(':').map {|x| x.to_i }
|
|
218
|
+
Color.new(r, g, b)
|
|
219
|
+
elsif value.numeric?
|
|
220
|
+
if value.include? '.'
|
|
221
|
+
value.to_f
|
|
222
|
+
else
|
|
223
|
+
value.to_i
|
|
224
|
+
end
|
|
225
|
+
else
|
|
226
|
+
value.gsub('\,', ':')
|
|
227
|
+
end
|
|
228
|
+
end
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
#parse the configs retrieved from irssi2
|
|
232
|
+
def parse_config(reply)
|
|
233
|
+
reply.lines.each do |line|
|
|
234
|
+
if line['key'] and line['value']
|
|
235
|
+
value = decode_value(line['value'])
|
|
236
|
+
@values[line['key'].sub('rirc_', '')] = value
|
|
237
|
+
#puts line['key'].sub('rirc_', '')+'=>'+value.to_s
|
|
238
|
+
end
|
|
239
|
+
end
|
|
240
|
+
create_config_snapshot
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
#create a copy of the config so we can compare for changes
|
|
244
|
+
def create_config_snapshot
|
|
245
|
+
@values.each do |k, v|
|
|
246
|
+
@oldvalues[k.dup] = encode_value(v)
|
|
247
|
+
end
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
def update_snapshot(hash)
|
|
251
|
+
hash.each{|k,v| hash[k] = encode_value(v)}
|
|
252
|
+
@oldvalues.merge!(hash)
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
def duplicate_config
|
|
256
|
+
vals = {}
|
|
257
|
+
@values.each do |k, v|
|
|
258
|
+
vals[k.dup] = decode_value(encode_value(v))
|
|
259
|
+
end
|
|
260
|
+
vals
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
def revert_to_defaults
|
|
264
|
+
@values = {}
|
|
265
|
+
@defaults.each do |k,v|
|
|
266
|
+
@values[k] = v
|
|
267
|
+
end
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
def get_pattern(name)
|
|
271
|
+
if @values[name].class == String
|
|
272
|
+
return @values[name].dup #don't escape_xml here
|
|
273
|
+
else
|
|
274
|
+
return ''
|
|
275
|
+
end
|
|
276
|
+
end
|
|
277
|
+
end
|
|
278
|
+
end
|
|
@@ -0,0 +1,403 @@
|
|
|
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
|
+
#### connections.rb ####
|
|
21
|
+
# This file contains the different classes for Connection objects
|
|
22
|
+
# which allow various methods of connection to icecap but present
|
|
23
|
+
# a unified interface
|
|
24
|
+
####
|
|
25
|
+
|
|
26
|
+
module Ratchet
|
|
27
|
+
class Connection
|
|
28
|
+
def listen(object)
|
|
29
|
+
@listenthread = Thread.new do
|
|
30
|
+
loop do
|
|
31
|
+
begin
|
|
32
|
+
if res = select([@output], nil, nil, nil) and res[0]
|
|
33
|
+
line = res[0][0].gets
|
|
34
|
+
@main.parse_line(line.chomp) if line
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def send(data)
|
|
42
|
+
begin
|
|
43
|
+
@input.puts(data)
|
|
44
|
+
rescue Errno::EPIPE
|
|
45
|
+
puts 'Write error: '+$!
|
|
46
|
+
return false
|
|
47
|
+
end
|
|
48
|
+
return true
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
class SSHConnection < Connection
|
|
52
|
+
def initialize(main, settings, connectionwindow)
|
|
53
|
+
@main = main
|
|
54
|
+
require 'open3'
|
|
55
|
+
require 'expect'
|
|
56
|
+
@input = nil
|
|
57
|
+
@output = nil
|
|
58
|
+
@error = nil
|
|
59
|
+
cmdstring = 'setsid ssh '
|
|
60
|
+
cmdstring += '-l '+settings['username']+' ' if settings['username']
|
|
61
|
+
cmdstring += '-p '+settings['port']+' ' if settings['port']
|
|
62
|
+
cmdstring += settings['host']+' '+settings['binpath']
|
|
63
|
+
# puts cmdstring
|
|
64
|
+
@input, @output, @error = Open3.popen3(cmdstring)
|
|
65
|
+
#puts @output.gets
|
|
66
|
+
|
|
67
|
+
begin
|
|
68
|
+
@output.expect(/^\*;preauth;time=(\d+);$/) do |x, y|
|
|
69
|
+
connectionwindow.send_text('logged in')
|
|
70
|
+
@main.calculate_clock_drift(y)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
rescue NoMethodError
|
|
74
|
+
connectionwindow.send_text('Something is borked, make sure sshd is running on selected host')
|
|
75
|
+
raise IOError, "one of the many things that could go wrong, has"
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# def send(data)
|
|
81
|
+
# begin
|
|
82
|
+
# @input.puts(data)
|
|
83
|
+
# #~ rescue SystemCallError
|
|
84
|
+
# #~ puts 'Write error: '+$!
|
|
85
|
+
# #~ return false
|
|
86
|
+
# rescue Errno::EPIPE
|
|
87
|
+
# puts 'Write error: '+$!
|
|
88
|
+
# return false
|
|
89
|
+
# end
|
|
90
|
+
# return true
|
|
91
|
+
# end
|
|
92
|
+
|
|
93
|
+
# def listen(object)
|
|
94
|
+
# @listenthread = Thread.new do
|
|
95
|
+
# loop do
|
|
96
|
+
# begin
|
|
97
|
+
# while line = @output.gets
|
|
98
|
+
# #puts 'o: '+line
|
|
99
|
+
# object.parse_lines(line)
|
|
100
|
+
# end
|
|
101
|
+
|
|
102
|
+
# rescue Errno::EPIPE
|
|
103
|
+
# puts 'listen: closed stream, disconnecting '+$!
|
|
104
|
+
# close
|
|
105
|
+
# object.disconnect
|
|
106
|
+
# object.connect
|
|
107
|
+
# break
|
|
108
|
+
# end
|
|
109
|
+
# end
|
|
110
|
+
# end
|
|
111
|
+
# end
|
|
112
|
+
|
|
113
|
+
def close
|
|
114
|
+
@listenthread.kill if @listenthread
|
|
115
|
+
@input.close
|
|
116
|
+
@output.close
|
|
117
|
+
@error.close
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
class LocalConnection < Connection
|
|
122
|
+
#attr_reader :connected
|
|
123
|
+
def initialize(main, settings, connectionwindow)
|
|
124
|
+
require 'open3'
|
|
125
|
+
require 'expect'
|
|
126
|
+
@main = main
|
|
127
|
+
@input = nil
|
|
128
|
+
@output = nil
|
|
129
|
+
@error = nil
|
|
130
|
+
if settings['binpath'][0].chr == '/'
|
|
131
|
+
unless File.exists? settings['binpath']
|
|
132
|
+
raise IOError, "Cannot find binary at #{settings['binpath']}"
|
|
133
|
+
end
|
|
134
|
+
elsif settings['binpath'].include? '/'
|
|
135
|
+
path = Pathname.new(settings['binpath'])
|
|
136
|
+
unless path.file?
|
|
137
|
+
raise IOError, "Cannot find binary at #{path.realpath}"
|
|
138
|
+
end
|
|
139
|
+
else
|
|
140
|
+
exists = false
|
|
141
|
+
ENV['PATH'].split(':').each do |d| #: is not windows compatible, but that's not really relevant
|
|
142
|
+
# puts d
|
|
143
|
+
if File.exists? d+'/'+settings['binpath']
|
|
144
|
+
exists = true
|
|
145
|
+
break
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
unless exists
|
|
149
|
+
raise IOError, "Cannot find binary #{settings['binpath']} in $PATH"
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
@input, @output, @error = Open3.popen3(settings['binpath'])
|
|
153
|
+
begin
|
|
154
|
+
@output.expect(/^\*;preauth;time=(\d+);\n/) do |x, y|
|
|
155
|
+
connectionwindow.send_text('logged in')
|
|
156
|
+
@main.calculate_clock_drift(y)
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
rescue NoMethodError
|
|
160
|
+
connectionwindow.send_text('Something is borked')
|
|
161
|
+
raise IOError, "one of the many things that could go wrong, has"
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
# def send(data)
|
|
166
|
+
# begin
|
|
167
|
+
# @input.puts(data)
|
|
168
|
+
# rescue Errno::EPIPE
|
|
169
|
+
# puts 'Write error: '+$!
|
|
170
|
+
# return false
|
|
171
|
+
# end
|
|
172
|
+
# true
|
|
173
|
+
# end
|
|
174
|
+
|
|
175
|
+
# def listen(object)
|
|
176
|
+
# @listenthread = Thread.new do
|
|
177
|
+
# loop do
|
|
178
|
+
# puts 'bar'
|
|
179
|
+
# begin
|
|
180
|
+
# while line = @output.gets
|
|
181
|
+
#puts 'o: '+line
|
|
182
|
+
# object.parse_lines(line)
|
|
183
|
+
# end
|
|
184
|
+
# if res = select([@output], nil, nil, nil) and res[0]
|
|
185
|
+
# puts res[0].inspect
|
|
186
|
+
# puts res[0][0].read.class
|
|
187
|
+
# @main.parse_line(res[0][0].gets.chomp)
|
|
188
|
+
|
|
189
|
+
# #Thread.new do
|
|
190
|
+
# @main.parse_lines(res[0][0].readlines("\n"))
|
|
191
|
+
# #end
|
|
192
|
+
# else
|
|
193
|
+
# puts 'no IO'
|
|
194
|
+
# end
|
|
195
|
+
# rescue Errno::EPIPE
|
|
196
|
+
# puts 'listen: closed stream, disconnecting '+$!
|
|
197
|
+
# close
|
|
198
|
+
# object.disconnect
|
|
199
|
+
# object.connect
|
|
200
|
+
# break
|
|
201
|
+
# end
|
|
202
|
+
# end
|
|
203
|
+
# end
|
|
204
|
+
# end
|
|
205
|
+
|
|
206
|
+
def close
|
|
207
|
+
@listenthread.kill if @listenthread
|
|
208
|
+
@input.close
|
|
209
|
+
@output.close
|
|
210
|
+
@error.close
|
|
211
|
+
end
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
class NetSSHConnection < Connection
|
|
215
|
+
def initialize(main, settings, connectionwindow)
|
|
216
|
+
@main = main
|
|
217
|
+
begin
|
|
218
|
+
@session = Net::SSH.start(settings['host'], settings['username'], :auth_methods => %w(password), :port => settings['port'])
|
|
219
|
+
|
|
220
|
+
@input, @output, @error = @session.process.popen3(settings['binpath'])
|
|
221
|
+
|
|
222
|
+
rescue Errno::EHOSTUNREACH
|
|
223
|
+
raise(IOError, 'Could not connect to host')
|
|
224
|
+
rescue Net::SSH::AuthenticationFailed
|
|
225
|
+
raise(IOError, 'Authentication Failed')
|
|
226
|
+
rescue Errno::ECONNREFUSED
|
|
227
|
+
raise(IOError, 'Connection Refused')
|
|
228
|
+
end
|
|
229
|
+
sleep 2
|
|
230
|
+
if @error.data_available?
|
|
231
|
+
error = @error.read
|
|
232
|
+
raise(IOError, error, caller)
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
#@sshthread = Thread.new{
|
|
236
|
+
# @session.loop
|
|
237
|
+
#}
|
|
238
|
+
puts 'connected via ssh'
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
def send(data)
|
|
242
|
+
begin
|
|
243
|
+
@input.puts(data)
|
|
244
|
+
rescue Errno::EPIPE
|
|
245
|
+
puts 'Write error: '+$!
|
|
246
|
+
return false
|
|
247
|
+
end
|
|
248
|
+
return true
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
def listen(object)
|
|
252
|
+
@listenthread = Thread.start do
|
|
253
|
+
while true
|
|
254
|
+
begin
|
|
255
|
+
if @output.data_available?
|
|
256
|
+
out = @output.read
|
|
257
|
+
object.parse_lines(out)
|
|
258
|
+
end
|
|
259
|
+
sleep 0.01 #sleep a little, this seems to be important
|
|
260
|
+
rescue IOError
|
|
261
|
+
puts 'listen: closed stream, disconnecting '+$!
|
|
262
|
+
close
|
|
263
|
+
object.disconnect
|
|
264
|
+
object.connect
|
|
265
|
+
break
|
|
266
|
+
end
|
|
267
|
+
end
|
|
268
|
+
end
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
def close
|
|
272
|
+
@session.close if @session
|
|
273
|
+
@sshthread.kill if @sshthread
|
|
274
|
+
@input = nil
|
|
275
|
+
@output = nil
|
|
276
|
+
@error = nil
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
class UnixSockConnection < Connection
|
|
283
|
+
def initialize(settings, connectionwindow)
|
|
284
|
+
if File.exist?(settings['location'])
|
|
285
|
+
begin
|
|
286
|
+
@socket = UNIXSocket.open(settings['location'])
|
|
287
|
+
rescue
|
|
288
|
+
raise(IOError, 'Could not connect to socket')
|
|
289
|
+
end
|
|
290
|
+
connectionwindow.send_text('Connected via unix socket')
|
|
291
|
+
else
|
|
292
|
+
raise(IOError, 'Socket File does not exist')
|
|
293
|
+
end
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
def send(data)
|
|
297
|
+
begin
|
|
298
|
+
@socket.send(data, 0)
|
|
299
|
+
rescue SystemCallError
|
|
300
|
+
puts 'Broken Pipe to Irssi, disconnecting '+$!
|
|
301
|
+
close
|
|
302
|
+
return false
|
|
303
|
+
end
|
|
304
|
+
return true
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
def listen(object)
|
|
308
|
+
@listenthread = Thread.start{
|
|
309
|
+
input = ''
|
|
310
|
+
begin
|
|
311
|
+
while line = @socket.recv(70)
|
|
312
|
+
if line.length == 0
|
|
313
|
+
sleep 1
|
|
314
|
+
end
|
|
315
|
+
input += line
|
|
316
|
+
if input.count("\n") > 0
|
|
317
|
+
pos = input.rindex("\n")
|
|
318
|
+
string = input[0, pos]
|
|
319
|
+
input = input[pos, input.length]
|
|
320
|
+
Thread.start{
|
|
321
|
+
object.parse_lines(string)
|
|
322
|
+
}
|
|
323
|
+
end
|
|
324
|
+
end
|
|
325
|
+
|
|
326
|
+
rescue SystemCallError
|
|
327
|
+
puts 'Broken Pipe to Irssi, disconnecting '+$!
|
|
328
|
+
close
|
|
329
|
+
end
|
|
330
|
+
}
|
|
331
|
+
end
|
|
332
|
+
|
|
333
|
+
def close
|
|
334
|
+
@listenthread.kill
|
|
335
|
+
@socket.close
|
|
336
|
+
@client = nil
|
|
337
|
+
end
|
|
338
|
+
|
|
339
|
+
end
|
|
340
|
+
|
|
341
|
+
|
|
342
|
+
class InetdConnection < Connection
|
|
343
|
+
def initialize(main, settings, connectionwindow)
|
|
344
|
+
@main = main
|
|
345
|
+
begin
|
|
346
|
+
@socket = TCPSocket.new(settings['host'], settings['port'].to_i)
|
|
347
|
+
rescue
|
|
348
|
+
raise(IOError, 'Could not connect to socket')
|
|
349
|
+
end
|
|
350
|
+
connectionwindow.send_text('Connected via TCP socket')
|
|
351
|
+
end
|
|
352
|
+
|
|
353
|
+
def send(data)
|
|
354
|
+
begin
|
|
355
|
+
@socket.send(data, 0)
|
|
356
|
+
rescue SystemCallError
|
|
357
|
+
puts 'Broken Pipe to Irssi, disconnecting '+$!
|
|
358
|
+
close
|
|
359
|
+
return false
|
|
360
|
+
end
|
|
361
|
+
return true
|
|
362
|
+
end
|
|
363
|
+
|
|
364
|
+
def listen(object)
|
|
365
|
+
@listenthread = Thread.start{
|
|
366
|
+
loop do
|
|
367
|
+
if res = select([@socket], nil, nil, nil) and res[0]
|
|
368
|
+
@main.parse_line(res[0][0].gets.chomp)
|
|
369
|
+
end
|
|
370
|
+
end
|
|
371
|
+
}
|
|
372
|
+
end
|
|
373
|
+
|
|
374
|
+
def close
|
|
375
|
+
@listenthread.kill
|
|
376
|
+
@socket.close
|
|
377
|
+
@client = nil
|
|
378
|
+
end
|
|
379
|
+
|
|
380
|
+
end
|
|
381
|
+
|
|
382
|
+
class ConnectionFactory
|
|
383
|
+
|
|
384
|
+
# Create a class variable constant of the various available connection
|
|
385
|
+
# types.
|
|
386
|
+
|
|
387
|
+
Types = {
|
|
388
|
+
"ssh" => SSHConnection,
|
|
389
|
+
"socket" => UnixSockConnection,
|
|
390
|
+
"inetd" => InetdConnection,
|
|
391
|
+
"local" => LocalConnection,
|
|
392
|
+
"net_ssh" => NetSSHConnection }
|
|
393
|
+
|
|
394
|
+
def self.spawn( type, *params )
|
|
395
|
+
object = Types[type]
|
|
396
|
+
unless object
|
|
397
|
+
raise( ArgumentError, "Supplied connection type \"" + type + "\" is not available." )
|
|
398
|
+
end
|
|
399
|
+
return object.new( *params )
|
|
400
|
+
end
|
|
401
|
+
|
|
402
|
+
end
|
|
403
|
+
end
|