xmpp4r 0.3

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 (142) hide show
  1. data/COPYING +340 -0
  2. data/ChangeLog +28 -0
  3. data/LICENSE +59 -0
  4. data/README +20 -0
  5. data/Rakefile +103 -0
  6. data/UPDATING +40 -0
  7. data/data/doc/xmpp4r/examples/advanced/adventure/README +57 -0
  8. data/data/doc/xmpp4r/examples/advanced/adventure/adventure.rb +23 -0
  9. data/data/doc/xmpp4r/examples/advanced/adventure/adventuremuc.rb +136 -0
  10. data/data/doc/xmpp4r/examples/advanced/adventure/cube.xml +15 -0
  11. data/data/doc/xmpp4r/examples/advanced/adventure/tower.xml +69 -0
  12. data/data/doc/xmpp4r/examples/advanced/adventure/world.rb +425 -0
  13. data/data/doc/xmpp4r/examples/advanced/fileserve.conf +11 -0
  14. data/data/doc/xmpp4r/examples/advanced/fileserve.rb +344 -0
  15. data/data/doc/xmpp4r/examples/advanced/getonline.rb +56 -0
  16. data/data/doc/xmpp4r/examples/advanced/gtkmucclient.rb +315 -0
  17. data/data/doc/xmpp4r/examples/advanced/migrate.rb +89 -0
  18. data/data/doc/xmpp4r/examples/advanced/minimuc.rb +266 -0
  19. data/data/doc/xmpp4r/examples/advanced/recvfile.rb +83 -0
  20. data/data/doc/xmpp4r/examples/advanced/rosterdiscovery.rb +130 -0
  21. data/data/doc/xmpp4r/examples/advanced/sendfile.conf +10 -0
  22. data/data/doc/xmpp4r/examples/advanced/sendfile.rb +72 -0
  23. data/data/doc/xmpp4r/examples/advanced/shellmgr/shellmgr.rb +51 -0
  24. data/data/doc/xmpp4r/examples/advanced/shellmgr/shellmgr_jabber.rb +43 -0
  25. data/data/doc/xmpp4r/examples/advanced/shellmgr/shellmgr_test.rb +10 -0
  26. data/data/doc/xmpp4r/examples/advanced/versionpoll.rb +90 -0
  27. data/data/doc/xmpp4r/examples/advanced/xmpping.rb +134 -0
  28. data/data/doc/xmpp4r/examples/advanced/xmppingrc.sample +9 -0
  29. data/data/doc/xmpp4r/examples/basic/change_password.rb +41 -0
  30. data/data/doc/xmpp4r/examples/basic/client.rb +68 -0
  31. data/data/doc/xmpp4r/examples/basic/component.rb +11 -0
  32. data/data/doc/xmpp4r/examples/basic/echo_nonthreaded.rb +32 -0
  33. data/data/doc/xmpp4r/examples/basic/echo_threaded.rb +32 -0
  34. data/data/doc/xmpp4r/examples/basic/jabbersend.rb +41 -0
  35. data/data/doc/xmpp4r/examples/basic/mass_sender.rb +67 -0
  36. data/data/doc/xmpp4r/examples/basic/mucinfo.rb +39 -0
  37. data/data/doc/xmpp4r/examples/basic/mucsimplebot.rb +83 -0
  38. data/data/doc/xmpp4r/examples/basic/register.rb +25 -0
  39. data/data/doc/xmpp4r/examples/basic/remove_registration.rb +18 -0
  40. data/data/doc/xmpp4r/examples/basic/roster.rb +42 -0
  41. data/data/doc/xmpp4r/examples/basic/rosterprint.rb +50 -0
  42. data/data/doc/xmpp4r/examples/basic/rosterrename.rb +34 -0
  43. data/data/doc/xmpp4r/examples/basic/rosterwatch.rb +172 -0
  44. data/data/doc/xmpp4r/examples/basic/send_vcard.rb +68 -0
  45. data/data/doc/xmpp4r/examples/basic/versionbot.rb +75 -0
  46. data/data/doc/xmpp4r/examples/buggy/jabber2jabber/jabber2jabber.rb +18 -0
  47. data/data/doc/xmpp4r/examples/buggy/miniedgarr_cgi.rb +192 -0
  48. data/data/doc/xmpp4r/examples/buggy/miniedgarr_watch.rb +82 -0
  49. data/lib/callbacks.rb +122 -0
  50. data/lib/xmpp4r/authenticationfailure.rb +13 -0
  51. data/lib/xmpp4r/bytestreams/helper/filetransfer.rb +315 -0
  52. data/lib/xmpp4r/bytestreams/helper/ibb/base.rb +256 -0
  53. data/lib/xmpp4r/bytestreams/helper/ibb/initiator.rb +30 -0
  54. data/lib/xmpp4r/bytestreams/helper/ibb/target.rb +46 -0
  55. data/lib/xmpp4r/bytestreams/helper/socks5bytestreams/base.rb +151 -0
  56. data/lib/xmpp4r/bytestreams/helper/socks5bytestreams/initiator.rb +85 -0
  57. data/lib/xmpp4r/bytestreams/helper/socks5bytestreams/server.rb +178 -0
  58. data/lib/xmpp4r/bytestreams/helper/socks5bytestreams/socks5.rb +56 -0
  59. data/lib/xmpp4r/bytestreams/helper/socks5bytestreams/target.rb +61 -0
  60. data/lib/xmpp4r/bytestreams/iq/bytestreams.rb +177 -0
  61. data/lib/xmpp4r/bytestreams/iq/si.rb +221 -0
  62. data/lib/xmpp4r/bytestreams.rb +11 -0
  63. data/lib/xmpp4r/client.rb +249 -0
  64. data/lib/xmpp4r/component.rb +103 -0
  65. data/lib/xmpp4r/connection.rb +166 -0
  66. data/lib/xmpp4r/dataforms/x/data.rb +248 -0
  67. data/lib/xmpp4r/dataforms.rb +1 -0
  68. data/lib/xmpp4r/debuglog.rb +34 -0
  69. data/lib/xmpp4r/delay/x/delay.rb +100 -0
  70. data/lib/xmpp4r/delay.rb +1 -0
  71. data/lib/xmpp4r/discovery/iq/discoinfo.rb +225 -0
  72. data/lib/xmpp4r/discovery/iq/discoitems.rb +162 -0
  73. data/lib/xmpp4r/discovery.rb +2 -0
  74. data/lib/xmpp4r/error.rb +230 -0
  75. data/lib/xmpp4r/errorexception.rb +32 -0
  76. data/lib/xmpp4r/feature_negotiation/iq/feature.rb +42 -0
  77. data/lib/xmpp4r/feature_negotiation.rb +1 -0
  78. data/lib/xmpp4r/idgenerator.rb +37 -0
  79. data/lib/xmpp4r/iq.rb +229 -0
  80. data/lib/xmpp4r/jid.rb +167 -0
  81. data/lib/xmpp4r/message.rb +171 -0
  82. data/lib/xmpp4r/muc/helper/mucbrowser.rb +107 -0
  83. data/lib/xmpp4r/muc/helper/mucclient.rb +382 -0
  84. data/lib/xmpp4r/muc/helper/simplemucclient.rb +222 -0
  85. data/lib/xmpp4r/muc/x/muc.rb +98 -0
  86. data/lib/xmpp4r/muc/x/mucuserinvite.rb +58 -0
  87. data/lib/xmpp4r/muc/x/mucuseritem.rb +148 -0
  88. data/lib/xmpp4r/muc.rb +6 -0
  89. data/lib/xmpp4r/presence.rb +255 -0
  90. data/lib/xmpp4r/query.rb +43 -0
  91. data/lib/xmpp4r/rexmladdons.rb +826 -0
  92. data/lib/xmpp4r/roster/helper/roster.rb +514 -0
  93. data/lib/xmpp4r/roster/iq/roster.rb +244 -0
  94. data/lib/xmpp4r/roster/x/roster.rb +155 -0
  95. data/lib/xmpp4r/roster.rb +4 -0
  96. data/lib/xmpp4r/sasl.rb +167 -0
  97. data/lib/xmpp4r/stream.rb +543 -0
  98. data/lib/xmpp4r/streamparser.rb +77 -0
  99. data/lib/xmpp4r/vcard/helper/vcard.rb +86 -0
  100. data/lib/xmpp4r/vcard/iq/vcard.rb +102 -0
  101. data/lib/xmpp4r/vcard.rb +3 -0
  102. data/lib/xmpp4r/version/helper/responder.rb +71 -0
  103. data/lib/xmpp4r/version/helper/simpleresponder.rb +44 -0
  104. data/lib/xmpp4r/version/iq/version.rb +118 -0
  105. data/lib/xmpp4r/version.rb +3 -0
  106. data/lib/xmpp4r/x.rb +43 -0
  107. data/lib/xmpp4r/xmlstanza.rb +174 -0
  108. data/lib/xmpp4r/xmpp4r.rb +16 -0
  109. data/lib/xmpp4r.rb +122 -0
  110. data/setup.rb +1360 -0
  111. data/test/bytestreams/tc_ibb.rb +186 -0
  112. data/test/bytestreams/tc_socks5bytestreams.rb +57 -0
  113. data/test/delay/tc_xdelay.rb +51 -0
  114. data/test/lib/clienttester.rb +110 -0
  115. data/test/muc/tc_muc_mucclient.rb +569 -0
  116. data/test/muc/tc_muc_simplemucclient.rb +72 -0
  117. data/test/roster/.tc_helper.rb.swp +0 -0
  118. data/test/roster/tc_helper.rb +389 -0
  119. data/test/roster/tc_iqqueryroster.rb +140 -0
  120. data/test/roster/tc_xroster.rb +70 -0
  121. data/test/tc_callbacks.rb +128 -0
  122. data/test/tc_class_names.rb +129 -0
  123. data/test/tc_client.rb +30 -0
  124. data/test/tc_error.rb +103 -0
  125. data/test/tc_idgenerator.rb +30 -0
  126. data/test/tc_iq.rb +109 -0
  127. data/test/tc_iqquery.rb +31 -0
  128. data/test/tc_jid.rb +202 -0
  129. data/test/tc_message.rb +114 -0
  130. data/test/tc_presence.rb +148 -0
  131. data/test/tc_stream.rb +182 -0
  132. data/test/tc_streamError.rb +87 -0
  133. data/test/tc_streamSend.rb +59 -0
  134. data/test/tc_streamThreaded.rb +168 -0
  135. data/test/tc_xmlstanza.rb +76 -0
  136. data/test/ts_xmpp4r.rb +34 -0
  137. data/test/vcard/tc_iqvcard.rb +52 -0
  138. data/test/version/tc_helper.rb +46 -0
  139. data/test/version/tc_iqqueryversion.rb +96 -0
  140. data/tools/doctoweb.bash +30 -0
  141. data/tools/gen_requires.bash +10 -0
  142. metadata +232 -0
@@ -0,0 +1,344 @@
1
+ require 'yaml'
2
+ require 'xmpp4r'
3
+ require 'xmpp4r/bytestreams'
4
+ require 'xmpp4r/roster'
5
+ require 'xmpp4r/version'
6
+
7
+ Jabber::debug = true
8
+
9
+ def human_readable(num)
10
+ unit = ''
11
+ units = %w(K M G T P E)
12
+ while num > 10240 and units.size > 0
13
+ num /= 1024
14
+ unit = units.shift
15
+ end
16
+ "#{num} #{unit}B"
17
+ end
18
+
19
+ class Transfer
20
+ attr_reader :peer, :filename, :bytes, :filesize
21
+
22
+ def initialize(filetransfer, peer, filename, filesize, msgblock)
23
+ @filetransfer = filetransfer
24
+ @peer = peer
25
+ @filename = filename
26
+ @filesize = filesize
27
+ @msgblock = msgblock
28
+
29
+ @done = false
30
+ @bytes = 0
31
+ @stats = [0]
32
+ @stats_lock = Mutex.new
33
+ @stats_thread = Thread.new { stats_loop }
34
+
35
+
36
+ @streamhost_cb = lambda { |streamhost,state,e|
37
+ case state
38
+ when :connecting
39
+ say "Connecting to #{streamhost.jid} (#{streamhost.host}:#{streamhost.port})"
40
+ when :success
41
+ say "Successfully using #{streamhost.jid} (#{streamhost.host}:#{streamhost.port})"
42
+ when :failure
43
+ say "Error using #{streamhost.jid} (#{streamhost.host}:#{streamhost.port}): #{e}"
44
+ end
45
+ }
46
+ end
47
+
48
+ def done?
49
+ @stats_thread.kill if @done
50
+ @done
51
+ end
52
+
53
+ def stats_loop
54
+ loop {
55
+ sleep 1
56
+ @stats_lock.synchronize {
57
+ @stats.push @bytes
58
+ @stats.shift if @stats.size > 5
59
+ }
60
+ }
61
+ end
62
+
63
+ def stats
64
+ @stats_lock.synchronize {
65
+ if @stats.first >= @stats.last
66
+ 0
67
+ else
68
+ (@stats.last - @stats.first) / (@stats.size - 1)
69
+ end
70
+ }
71
+ end
72
+
73
+ def say(text)
74
+ @msgblock.call(text)
75
+ end
76
+
77
+ def transfer(from, to)
78
+ while buf = from.read
79
+ @bytes += to.write buf
80
+ end
81
+ end
82
+ end
83
+
84
+ class Upload < Transfer
85
+ def initialize(filetransfer, iq, filename, filesize, can_range, msgblock)
86
+ super(filetransfer, iq.from, filename, filesize, msgblock)
87
+
88
+ if filename.size < 1
89
+ say "What is this file for?"
90
+ @done = true
91
+ return
92
+ end
93
+
94
+ offset = nil
95
+ if File::exist?(filename)
96
+ if File::size(filename) < filesize and can_range
97
+ @bytes = offset = File::size(filename)
98
+ say "Will retrieve #{filename} starting at #{offset}"
99
+ else
100
+ say "#{filename} already exists"
101
+ filetransfer.decline(iq)
102
+ @done = true
103
+ return
104
+ end
105
+ end
106
+
107
+ Thread.new {
108
+ begin
109
+ stream = filetransfer.accept(iq, offset)
110
+ if stream.kind_of?(Jabber::Bytestreams::SOCKS5Bytestreams)
111
+ stream.connect_timeout = 5
112
+ stream.add_streamhost_callback(nil, nil, &@streamhost_cb)
113
+ end
114
+
115
+ if stream.accept
116
+ outfile = File.new(filename, (offset ? 'a' : 'w'))
117
+
118
+ transfer(stream, outfile)
119
+
120
+ outfile.close
121
+ stream.close
122
+ @done = true
123
+ else
124
+ say "Stream failed"
125
+ @done = true
126
+ end
127
+ rescue
128
+ puts $!.backtrace.first
129
+ say "Error: #{$!}"
130
+ @done = true
131
+ end
132
+ }
133
+ end
134
+ end
135
+
136
+ class Download < Transfer
137
+ def initialize(filetransfer, peer, filename, msgblock, socksconf)
138
+ begin
139
+ filesize = File.size(filename)
140
+ rescue
141
+ filesize = 0
142
+ end
143
+
144
+ super(filetransfer, peer, filename, filesize, msgblock)
145
+
146
+ Thread.new {
147
+ begin
148
+ raise "No regular file" unless File.file?(filename)
149
+
150
+ source = Jabber::FileTransfer::FileSource.new(filename)
151
+ stream = filetransfer.offer(peer, source)
152
+ unless stream
153
+ raise "Well, you should accept what you request..."
154
+ @done = true
155
+ end
156
+
157
+ if stream.kind_of?(Jabber::Bytestreams::SOCKS5Bytestreams)
158
+ socksconf.call(stream)
159
+ stream.add_streamhost_callback(nil, nil, &@streamhost_cb)
160
+ end
161
+
162
+ stream.open
163
+ transfer(source, stream)
164
+ stream.close
165
+ @done = true
166
+ rescue
167
+ say "Error: #{$!}"
168
+ @done = true
169
+ end
170
+ }
171
+ end
172
+ end
173
+
174
+ class FileServe
175
+ def initialize(conf)
176
+ @uploads = 0
177
+ @downloads = 0
178
+
179
+ @transfers = []
180
+ @transfers_lock = Mutex.new
181
+
182
+ @client = Jabber::Client.new Jabber::JID.new(conf['jabber']['jid'])
183
+ @client.connect
184
+ @client.auth(conf['jabber']['password'])
185
+
186
+ @ft = Jabber::FileTransfer::Helper.new(@client)
187
+ Jabber::Version::SimpleResponder.new(@client,
188
+ "XMPP4R FileServe example",
189
+ Jabber::XMPP4R_VERSION,
190
+ IO.popen('uname -sr').readlines.to_s.strip)
191
+ register_handlers
192
+
193
+ @directory = conf['directory']
194
+ @directory.gsub!(/\/+$/, '')
195
+
196
+ @socksserver = Jabber::Bytestreams::SOCKS5BytestreamsServer.new(conf['socks']['port'])
197
+
198
+ conf['socks']['addresses'].each { |addr| @socksserver.add_address(addr) }
199
+
200
+ @proxies = []
201
+ conf['socks']['proxies'].collect { |jid|
202
+ puts "Querying proxy #{jid}..."
203
+ begin
204
+ @proxies.push(Jabber::Bytestreams::SOCKS5Bytestreams::query_streamhost(@client, jid))
205
+ rescue
206
+ puts "Error: #{$!}"
207
+ end
208
+ }
209
+
210
+ Thread.new { presence }
211
+ Thread.new { cleaner }
212
+
213
+ # Panic reboot ;-)
214
+ @client.on_exception { initialize(conf) }
215
+ end
216
+
217
+ def presence
218
+ rate_upload = 0
219
+ rate_download = 0
220
+
221
+ old_status = nil
222
+ loop {
223
+ # Update the @rate_* variables
224
+ @transfers_lock.synchronize {
225
+ @transfers.each { |t|
226
+ if t.kind_of?(Upload) and t.stats > rate_upload
227
+ rate_upload = t.stats
228
+ elsif t.kind_of?(Download) and t.stats > rate_download
229
+ rate_download = t.stats
230
+ end
231
+ }
232
+ }
233
+ status = "Attempted #{@downloads} downloads (max. #{human_readable rate_download}/s) and #{@uploads} uploads (max. #{human_readable rate_upload}/s)"
234
+
235
+ @client.send(Jabber::Presence.new(:chat, status)) if status != old_status
236
+ old_status = status
237
+
238
+ sleep 1
239
+
240
+ }
241
+ end
242
+
243
+ def register_handlers
244
+ @client.add_message_callback { |msg|
245
+ if msg.type == :chat and msg.body and msg.from != 'pentabarf@pentabarf.org/rails'
246
+ puts "<#{msg.from}> #{msg.body.strip}"
247
+ cmd, arg = msg.body.split(/ /, 2)
248
+
249
+ command(msg.from, cmd, arg)
250
+ end
251
+ }
252
+
253
+ @ft.add_incoming_callback { |iq,file|
254
+
255
+ say = lambda { |text|
256
+ say(iq.from, text)
257
+ }
258
+
259
+ puts "Incoming file transfer from #{iq.from}: #{file.fname} (#{file.size / 1024} KB)"
260
+ filename = file.fname.split(/\//).last
261
+ filename.gsub!(/^\.+/, '')
262
+
263
+ puts "Range: #{file.range != nil}"
264
+ transfer = Upload.new(@ft, iq, "#{@directory}/#{filename}", file.size, file.range != nil, say)
265
+ @uploads += 1
266
+
267
+ @transfers_lock.synchronize {
268
+ @transfers.push(transfer)
269
+ }
270
+
271
+ }
272
+
273
+ roster = Jabber::Roster::Helper.new(@client)
274
+
275
+ roster.add_subscription_request_callback { |item,presence|
276
+ roster.accept_subscription(presence.from)
277
+ }
278
+
279
+ end
280
+
281
+ def command(from, cmd, arg)
282
+ say = lambda { |text|
283
+ say(from, text)
284
+ }
285
+ socksconf = lambda { |stream|
286
+ stream.add_streamhost(@socksserver)
287
+ @proxies.each { |sh|
288
+ stream.add_streamhost(sh)
289
+ }
290
+ }
291
+
292
+ case cmd
293
+ when 'get'
294
+ arg.gsub!(/\//, '')
295
+ arg.gsub!(/^\.+/, '')
296
+ transfer = Download.new(@ft, from, "#{@directory}/#{arg}", say, socksconf)
297
+ @downloads += 1
298
+ @transfers_lock.synchronize {
299
+ @transfers.push(transfer)
300
+ }
301
+ when 'ls'
302
+ text = ""
303
+ Dir.foreach(@directory) { |file|
304
+ next if file =~ /^\./
305
+ path = "#{@directory}/#{file}"
306
+ text += "#{file} (#{human_readable File.size(path)})\n" if File.file?(path)
307
+ }
308
+ say.call(text.strip)
309
+ when 'stat'
310
+ @transfers_lock.synchronize {
311
+ text = "#{@transfers.size} transfers:\n"
312
+ @transfers.each { |t|
313
+ text += "#{t.filename} (#{t.peer}): #{(t.bytes * 100) / t.filesize}% (#{human_readable t.stats}/s)\n"
314
+ }
315
+ }
316
+ say.call(text.strip)
317
+ when 'help'
318
+ say.call "Download a file: get <filename>\nList directory contents: ls\nLook who is currently wasting bandwidth: stat\nUpload a file, simply send this file"
319
+ else
320
+ say.call "Unknown command: #{cmd}, try help"
321
+ end
322
+ end
323
+
324
+ def say(to, text)
325
+ puts ">#{to}< #{text.strip}"
326
+ @client.send(Jabber::Message.new(to, text).set_type(:chat))
327
+ end
328
+
329
+ def cleaner
330
+ loop {
331
+
332
+ @transfers_lock.synchronize {
333
+ @transfers.delete_if { |t| t.done? }
334
+ }
335
+
336
+ sleep 1
337
+ }
338
+ end
339
+ end
340
+
341
+
342
+ FileServe.new(YAML::load(File.new('fileserve.conf')))
343
+ puts "Up and running!"
344
+ Thread.stop
@@ -0,0 +1,56 @@
1
+ #!/usr/bin/ruby
2
+
3
+ # This script can get all roster entries
4
+
5
+ require 'optparse'
6
+ require 'xmpp4r'
7
+ include Jabber
8
+
9
+ get = true
10
+ jid = JID::new('lucastest@linux.ensimag.fr/rosterget')
11
+ password = 'lucastest'
12
+ domains = []
13
+
14
+ OptionParser::new do |opts|
15
+ opts.banner = 'Usage: roster.rb -j jid -p password -d domain'
16
+ opts.separator ''
17
+ opts.on('-j', '--jid JID', 'sets the jid') { |j| jid = JID::new(j) }
18
+ opts.on('-p', '--password PASSWORD', 'sets the password') { |p| password = p }
19
+ opts.on('-d', '--domain DOMAIN', 'sets the domain') { |d| domains << d }
20
+ opts.on_tail('-h', '--help', 'Show this message') {
21
+ puts opts
22
+ exit
23
+ }
24
+ opts.parse!(ARGV)
25
+ end
26
+
27
+ cl = Client::new(jid, false)
28
+ cl.connect
29
+ cl.auth(password)
30
+ exit = false
31
+ nb = 0
32
+ cl.add_iq_callback { |i|
33
+ fjid = JID::new(i.from)
34
+ if i.type == :result and fjid.resource == "admin"
35
+ domain = fjid.domain
36
+ items = nil
37
+ i.each_element('item') { |e| items = e }
38
+ raise "items nil" if items.nil?
39
+ puts "--- Online users for #{domain} (seconds, sent, received)"
40
+ c = 0
41
+ items.each_element('user') do |e|
42
+ puts "#{e.attribute('jid')} #{e.attribute('name')}"
43
+ c += 1
44
+ end
45
+ puts "--- #{domain} : #{c} online users"
46
+ nb -= 1
47
+ end
48
+ }
49
+ for d in domains do
50
+ cl.send(Iq::new_browseget.set_to("#{d}/admin"))
51
+ nb += 1
52
+ end
53
+ while nb > 0
54
+ cl.process(1)
55
+ end
56
+ cl.close
@@ -0,0 +1,315 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'gtk2'
4
+
5
+ $:.unshift '../../../../../lib'
6
+ require 'xmpp4r'
7
+ require 'xmpp4r/muc/helper/simplemucclient'
8
+ require 'xmpp4r/version/helper/simpleresponder'
9
+
10
+ #Jabber::debug = true
11
+
12
+ class Gtk::Label
13
+ def self.new_show(str = nil, mnemonic = false)
14
+ label = new(str, mnemonic)
15
+ label.show
16
+ label
17
+ end
18
+ end
19
+
20
+ class SetupWindow < Gtk::Window
21
+ def initialize
22
+ super(Gtk::Window::TOPLEVEL)
23
+ self.title = "GtkMUCClient setup"
24
+ signal_connect("delete_event") { cancel }
25
+
26
+ vbox = Gtk::VBox.new
27
+ vbox.set_border_width(4)
28
+ add(vbox)
29
+ vbox.show
30
+
31
+ frame1 = Gtk::Frame.new('Jabber Account Settings')
32
+ frame1.set_border_width(4)
33
+ frame1.show
34
+ vbox.add(frame1)
35
+
36
+ layout1 = Gtk::Table.new(4, 2)
37
+ layout1.row_spacings = 4
38
+ layout1.column_spacings = 8
39
+ layout1.show
40
+ frame1.add(layout1)
41
+
42
+ layout1.attach(Gtk::Label.new_show('Jabber ID:'), 0, 1, 1, 2)
43
+ @entry_jid = Gtk::Entry.new
44
+ @entry_jid.text = 'collector@jabber.ccc.de'
45
+ @entry_jid.show
46
+ layout1.attach(@entry_jid, 1, 2, 1, 2)
47
+
48
+ layout1.attach(Gtk::Label.new_show('Password:'), 0, 1, 2, 3)
49
+ @entry_password = Gtk::Entry.new
50
+ @entry_password.visibility = false
51
+ @entry_password.show
52
+ layout1.attach(@entry_password, 1, 2, 2, 3)
53
+
54
+ layout1.attach(Gtk::Label.new_show('Resource:'), 0, 1, 3, 4)
55
+ @entry_resource = Gtk::Entry.new
56
+ @entry_resource.text = 'gtkmucclient'
57
+ @entry_resource.show
58
+ layout1.attach(@entry_resource, 1, 2, 3, 4)
59
+
60
+
61
+ frame2 = Gtk::Frame.new('Multi-User Chat Settings')
62
+ frame2.set_border_width(4)
63
+ frame2.show
64
+ vbox.add(frame2)
65
+
66
+ layout2 = Gtk::Table.new(3, 2)
67
+ layout2.row_spacings = 4
68
+ layout2.column_spacings = 8
69
+ layout2.show
70
+ frame2.add(layout2)
71
+
72
+ layout2.attach(Gtk::Label.new_show('Room:'), 0, 1, 1, 2)
73
+ @entry_room = Gtk::Entry.new
74
+ @entry_room.text = 'test@conference.jabber.org'
75
+ @entry_room.show
76
+ layout2.attach(@entry_room, 1, 2, 1, 2)
77
+
78
+ layout2.attach(Gtk::Label.new_show('Nick:'), 0, 1, 2, 3)
79
+ @entry_nick = Gtk::Entry.new
80
+ @entry_nick.text = 'XMPP4R-Fan'
81
+ @entry_nick.show
82
+ layout2.attach(@entry_nick, 1, 2, 2, 3)
83
+
84
+
85
+ hbox = Gtk::HBox.new
86
+ hbox.show
87
+ vbox.add(hbox)
88
+
89
+ button_ok = Gtk::Button.new("Ok")
90
+ button_ok.set_border_width(4)
91
+ hbox.add(button_ok)
92
+ button_ok.signal_connect("clicked") { ok }
93
+ button_ok.can_default = true
94
+ button_ok.grab_default
95
+ button_ok.show
96
+ button_cancel = Gtk::Button.new("Cancel")
97
+ button_cancel.set_border_width(4)
98
+ hbox.add(button_cancel)
99
+ button_cancel.signal_connect("clicked") { cancel }
100
+ button_cancel.show
101
+ end
102
+
103
+ def error_dialog(msg)
104
+ dialog = Gtk::MessageDialog.new(self, Gtk::Dialog::MODAL, Gtk::MessageDialog::ERROR, Gtk::MessageDialog::BUTTONS_OK, msg)
105
+ dialog.signal_connect("response") { dialog.destroy }
106
+ dialog.run
107
+ end
108
+
109
+ def ok
110
+ jid = Jabber::JID.new(@entry_jid.text)
111
+ mucjid = Jabber::JID.new(@entry_room.text)
112
+
113
+ if jid.node.nil?
114
+ error_dialog("Your Jabber ID must contain a user name and therefore contain one @ character.")
115
+ elsif jid.resource
116
+ error_dialog("If you intend to set a custom resource, put that in the right text field. Remove the slash!")
117
+ elsif @entry_resource.text.empty?
118
+ error_dialog("Please set a resource. This is a somewhat unimportant setting...")
119
+ elsif mucjid.node.nil?
120
+ error_dialog("Please set a room name, e.g. myroom@conference.jabber.org")
121
+ elsif mucjid.resource
122
+ error_dialog("The MUC room must not contain a resource. Remove the slash!")
123
+ elsif @entry_nick.text.empty?
124
+ error_dialog("Please set a nick for MUC.")
125
+ else
126
+ jid.resource = @entry_resource.text
127
+ mucjid.resource = @entry_nick.text
128
+ password = @entry_password.text
129
+
130
+ destroy
131
+
132
+ ChatWindow.new(jid, password, mucjid).show
133
+ end
134
+ end
135
+
136
+ def cancel
137
+ destroy
138
+ Gtk.main_quit
139
+ end
140
+ end
141
+
142
+ class ChatWindow < Gtk::Window
143
+ def initialize(jid, password, mucjid)
144
+ super(Gtk::Window::TOPLEVEL)
145
+ self.title = "GtkMUCClient: #{mucjid.resource} in #{mucjid.strip}"
146
+ signal_connect("delete_event") { destroy; Gtk.main_quit }
147
+
148
+ layout = Gtk::VBox.new
149
+
150
+ @topic = Gtk::Entry.new
151
+ @topic.editable = false
152
+ @topic.show
153
+ layout.pack_start(@topic, false, false, 2)
154
+
155
+ layout_mid = Gtk::HPaned.new
156
+ layout_mid.position = 500
157
+ layout_mid.show
158
+ layout.pack_start(layout_mid)
159
+
160
+ @buffer_scroll = Gtk::ScrolledWindow.new
161
+ @buffer_scroll.show
162
+ @buffer_scroll.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC)
163
+ layout_mid.pack1(@buffer_scroll, true, true)
164
+
165
+ @buffer_view = Gtk::TextView.new
166
+ @buffer_view.editable = false
167
+ @buffer_view.cursor_visible = false
168
+ @buffer_view.wrap_mode = Gtk::TextTag::WRAP_WORD
169
+ @buffer_view.modify_font(Pango::FontDescription.new('monospace 12'))
170
+ @buffer_view.show
171
+ @buffer_scroll.add_with_viewport(@buffer_view)
172
+
173
+ roster_scroll = Gtk::ScrolledWindow.new
174
+ roster_scroll.show
175
+ roster_scroll.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC)
176
+ layout_mid.pack2(roster_scroll, true, true)
177
+
178
+ @roster = Gtk::ListStore.new(String)
179
+ @roster.set_sort_column_id(0)
180
+ roster_view = Gtk::TreeView.new(@roster)
181
+ roster_view.append_column Gtk::TreeViewColumn.new("Participant", Gtk::CellRendererText.new, {:text => 0})
182
+ roster_view.show
183
+ roster_scroll.add_with_viewport(roster_view)
184
+
185
+ @input = Gtk::Entry.new
186
+ @input.grab_focus
187
+ @input.signal_connect("activate") {
188
+ on_input(@input.text)
189
+ @input.text = ''
190
+ }
191
+ @input.show
192
+ layout.pack_start(@input, false, false, 2)
193
+
194
+ layout.show
195
+ add(layout)
196
+
197
+ print_buffer "Welcome to the XMPP4R sample GTK2 Multi-User Chat client"
198
+ print_buffer "Commands start with a slash, type \"/help\" for a list"
199
+
200
+ @client = Jabber::Client.new(jid)
201
+ Jabber::Version::SimpleResponder.new(@client, "XMPP4R example: GtkMUCClient", Jabber::XMPP4R_VERSION, IO.popen("uname -sr").readlines.to_s.strip)
202
+ Thread.new {
203
+ begin
204
+ print_buffer "Connecting for domain #{jid.domain}..."
205
+ @client.connect
206
+ print_buffer "Authenticating for #{jid.strip}..."
207
+ @client.auth(password)
208
+ print_buffer "Attempting to join #{mucjid.strip} as #{mucjid.resource}..."
209
+ @muc = Jabber::MUC::SimpleMUCClient.new(@client)
210
+ register_handlers
211
+ @muc.join(mucjid)
212
+ rescue Exception => e
213
+ puts "#{e.class}: #{e}\n#{e.backtrace.join("\n")}"
214
+ print_buffer("Error: #{e}")
215
+ end
216
+ }
217
+
218
+ set_size_request(600, 400)
219
+ end
220
+
221
+ def print_buffer(s, time=nil)
222
+ @buffer_view.buffer.insert(@buffer_view.buffer.end_iter, "[#{(time || Time.new).getlocal.strftime('%I:%M')}] #{s}\n")
223
+ va = @buffer_scroll.vadjustment
224
+ va.value = va.upper
225
+ end
226
+
227
+ def register_handlers
228
+ @muc.on_room_message { |time,text|
229
+ print_buffer("*** #{text}", time)
230
+ }
231
+ @muc.on_message { |time,nick,text|
232
+ if text =~ /^\/me (.+)$/
233
+ print_buffer("*#{nick} #{$1}", time)
234
+ else
235
+ print_buffer("<#{nick}> #{text}", time)
236
+ end
237
+ }
238
+ @muc.on_private_message { |time,nick,text|
239
+ print_buffer("<-(#{nick}) #{text}", time)
240
+ }
241
+ @muc.on_join { |time,nick|
242
+ @roster.append[0] = nick
243
+ }
244
+ @muc.on_self_leave { |time|
245
+ print_buffer("You have exited the room", time)
246
+ }
247
+ @muc.on_leave { |time,nick|
248
+ del_iter = nil
249
+ @roster.each { |m,p,iter|
250
+ del_iter = iter if iter[0] == nick
251
+ }
252
+ @roster.remove(del_iter) if del_iter
253
+ }
254
+ @muc.on_subject { |time,nick,subject|
255
+ @topic.text = subject
256
+ }
257
+ end
258
+
259
+ def on_input(line)
260
+ commands = {
261
+ 'help' => [
262
+ 'Display this help',
263
+ lambda {
264
+ commands.each { |cmd,a|
265
+ print_buffer "/#{cmd.ljust(10)} - #{a[0]}"
266
+ }
267
+ }
268
+ ],
269
+ 'msg' => [
270
+ 'Send a private message to a user',
271
+ lambda { |args|
272
+ # Limitation: it is not possible to send private messages
273
+ # to a user with a space in his nickname
274
+ to = args.shift
275
+ text = args.join(' ')
276
+ @muc.say(text, to)
277
+ print_buffer "->(#{to}) #{text}"
278
+ }
279
+ ],
280
+ 'subject' => [
281
+ 'Change the room\'s subject',
282
+ lambda { |args|
283
+ @muc.subject = args.join(' ')
284
+ }
285
+ ],
286
+ 'quit' => [
287
+ 'Leave room with optional message, then disconnect client and shut down',
288
+ lambda { |args|
289
+ @muc.exit(args.join(' ')) if @muc.active?
290
+ @client.close
291
+ Gtk.main_quit
292
+ }
293
+ ]
294
+ }
295
+
296
+ if line =~ /^\//
297
+ args = line.split(/ /)
298
+ cmd = args.shift[1..-1].downcase
299
+
300
+ command = commands[cmd]
301
+ if command
302
+ help, func = command
303
+ func.call(args)
304
+ else
305
+ print_buffer "Unknown command: #{cmd}, use \"/help\""
306
+ end
307
+ else
308
+ @muc.say(line)
309
+ end
310
+ end
311
+ end
312
+
313
+ Gtk.init
314
+ SetupWindow.new.show
315
+ Gtk.main