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.
- data/COPYING +340 -0
- data/ChangeLog +28 -0
- data/LICENSE +59 -0
- data/README +20 -0
- data/Rakefile +103 -0
- data/UPDATING +40 -0
- data/data/doc/xmpp4r/examples/advanced/adventure/README +57 -0
- data/data/doc/xmpp4r/examples/advanced/adventure/adventure.rb +23 -0
- data/data/doc/xmpp4r/examples/advanced/adventure/adventuremuc.rb +136 -0
- data/data/doc/xmpp4r/examples/advanced/adventure/cube.xml +15 -0
- data/data/doc/xmpp4r/examples/advanced/adventure/tower.xml +69 -0
- data/data/doc/xmpp4r/examples/advanced/adventure/world.rb +425 -0
- data/data/doc/xmpp4r/examples/advanced/fileserve.conf +11 -0
- data/data/doc/xmpp4r/examples/advanced/fileserve.rb +344 -0
- data/data/doc/xmpp4r/examples/advanced/getonline.rb +56 -0
- data/data/doc/xmpp4r/examples/advanced/gtkmucclient.rb +315 -0
- data/data/doc/xmpp4r/examples/advanced/migrate.rb +89 -0
- data/data/doc/xmpp4r/examples/advanced/minimuc.rb +266 -0
- data/data/doc/xmpp4r/examples/advanced/recvfile.rb +83 -0
- data/data/doc/xmpp4r/examples/advanced/rosterdiscovery.rb +130 -0
- data/data/doc/xmpp4r/examples/advanced/sendfile.conf +10 -0
- data/data/doc/xmpp4r/examples/advanced/sendfile.rb +72 -0
- data/data/doc/xmpp4r/examples/advanced/shellmgr/shellmgr.rb +51 -0
- data/data/doc/xmpp4r/examples/advanced/shellmgr/shellmgr_jabber.rb +43 -0
- data/data/doc/xmpp4r/examples/advanced/shellmgr/shellmgr_test.rb +10 -0
- data/data/doc/xmpp4r/examples/advanced/versionpoll.rb +90 -0
- data/data/doc/xmpp4r/examples/advanced/xmpping.rb +134 -0
- data/data/doc/xmpp4r/examples/advanced/xmppingrc.sample +9 -0
- data/data/doc/xmpp4r/examples/basic/change_password.rb +41 -0
- data/data/doc/xmpp4r/examples/basic/client.rb +68 -0
- data/data/doc/xmpp4r/examples/basic/component.rb +11 -0
- data/data/doc/xmpp4r/examples/basic/echo_nonthreaded.rb +32 -0
- data/data/doc/xmpp4r/examples/basic/echo_threaded.rb +32 -0
- data/data/doc/xmpp4r/examples/basic/jabbersend.rb +41 -0
- data/data/doc/xmpp4r/examples/basic/mass_sender.rb +67 -0
- data/data/doc/xmpp4r/examples/basic/mucinfo.rb +39 -0
- data/data/doc/xmpp4r/examples/basic/mucsimplebot.rb +83 -0
- data/data/doc/xmpp4r/examples/basic/register.rb +25 -0
- data/data/doc/xmpp4r/examples/basic/remove_registration.rb +18 -0
- data/data/doc/xmpp4r/examples/basic/roster.rb +42 -0
- data/data/doc/xmpp4r/examples/basic/rosterprint.rb +50 -0
- data/data/doc/xmpp4r/examples/basic/rosterrename.rb +34 -0
- data/data/doc/xmpp4r/examples/basic/rosterwatch.rb +172 -0
- data/data/doc/xmpp4r/examples/basic/send_vcard.rb +68 -0
- data/data/doc/xmpp4r/examples/basic/versionbot.rb +75 -0
- data/data/doc/xmpp4r/examples/buggy/jabber2jabber/jabber2jabber.rb +18 -0
- data/data/doc/xmpp4r/examples/buggy/miniedgarr_cgi.rb +192 -0
- data/data/doc/xmpp4r/examples/buggy/miniedgarr_watch.rb +82 -0
- data/lib/callbacks.rb +122 -0
- data/lib/xmpp4r/authenticationfailure.rb +13 -0
- data/lib/xmpp4r/bytestreams/helper/filetransfer.rb +315 -0
- data/lib/xmpp4r/bytestreams/helper/ibb/base.rb +256 -0
- data/lib/xmpp4r/bytestreams/helper/ibb/initiator.rb +30 -0
- data/lib/xmpp4r/bytestreams/helper/ibb/target.rb +46 -0
- data/lib/xmpp4r/bytestreams/helper/socks5bytestreams/base.rb +151 -0
- data/lib/xmpp4r/bytestreams/helper/socks5bytestreams/initiator.rb +85 -0
- data/lib/xmpp4r/bytestreams/helper/socks5bytestreams/server.rb +178 -0
- data/lib/xmpp4r/bytestreams/helper/socks5bytestreams/socks5.rb +56 -0
- data/lib/xmpp4r/bytestreams/helper/socks5bytestreams/target.rb +61 -0
- data/lib/xmpp4r/bytestreams/iq/bytestreams.rb +177 -0
- data/lib/xmpp4r/bytestreams/iq/si.rb +221 -0
- data/lib/xmpp4r/bytestreams.rb +11 -0
- data/lib/xmpp4r/client.rb +249 -0
- data/lib/xmpp4r/component.rb +103 -0
- data/lib/xmpp4r/connection.rb +166 -0
- data/lib/xmpp4r/dataforms/x/data.rb +248 -0
- data/lib/xmpp4r/dataforms.rb +1 -0
- data/lib/xmpp4r/debuglog.rb +34 -0
- data/lib/xmpp4r/delay/x/delay.rb +100 -0
- data/lib/xmpp4r/delay.rb +1 -0
- data/lib/xmpp4r/discovery/iq/discoinfo.rb +225 -0
- data/lib/xmpp4r/discovery/iq/discoitems.rb +162 -0
- data/lib/xmpp4r/discovery.rb +2 -0
- data/lib/xmpp4r/error.rb +230 -0
- data/lib/xmpp4r/errorexception.rb +32 -0
- data/lib/xmpp4r/feature_negotiation/iq/feature.rb +42 -0
- data/lib/xmpp4r/feature_negotiation.rb +1 -0
- data/lib/xmpp4r/idgenerator.rb +37 -0
- data/lib/xmpp4r/iq.rb +229 -0
- data/lib/xmpp4r/jid.rb +167 -0
- data/lib/xmpp4r/message.rb +171 -0
- data/lib/xmpp4r/muc/helper/mucbrowser.rb +107 -0
- data/lib/xmpp4r/muc/helper/mucclient.rb +382 -0
- data/lib/xmpp4r/muc/helper/simplemucclient.rb +222 -0
- data/lib/xmpp4r/muc/x/muc.rb +98 -0
- data/lib/xmpp4r/muc/x/mucuserinvite.rb +58 -0
- data/lib/xmpp4r/muc/x/mucuseritem.rb +148 -0
- data/lib/xmpp4r/muc.rb +6 -0
- data/lib/xmpp4r/presence.rb +255 -0
- data/lib/xmpp4r/query.rb +43 -0
- data/lib/xmpp4r/rexmladdons.rb +826 -0
- data/lib/xmpp4r/roster/helper/roster.rb +514 -0
- data/lib/xmpp4r/roster/iq/roster.rb +244 -0
- data/lib/xmpp4r/roster/x/roster.rb +155 -0
- data/lib/xmpp4r/roster.rb +4 -0
- data/lib/xmpp4r/sasl.rb +167 -0
- data/lib/xmpp4r/stream.rb +543 -0
- data/lib/xmpp4r/streamparser.rb +77 -0
- data/lib/xmpp4r/vcard/helper/vcard.rb +86 -0
- data/lib/xmpp4r/vcard/iq/vcard.rb +102 -0
- data/lib/xmpp4r/vcard.rb +3 -0
- data/lib/xmpp4r/version/helper/responder.rb +71 -0
- data/lib/xmpp4r/version/helper/simpleresponder.rb +44 -0
- data/lib/xmpp4r/version/iq/version.rb +118 -0
- data/lib/xmpp4r/version.rb +3 -0
- data/lib/xmpp4r/x.rb +43 -0
- data/lib/xmpp4r/xmlstanza.rb +174 -0
- data/lib/xmpp4r/xmpp4r.rb +16 -0
- data/lib/xmpp4r.rb +122 -0
- data/setup.rb +1360 -0
- data/test/bytestreams/tc_ibb.rb +186 -0
- data/test/bytestreams/tc_socks5bytestreams.rb +57 -0
- data/test/delay/tc_xdelay.rb +51 -0
- data/test/lib/clienttester.rb +110 -0
- data/test/muc/tc_muc_mucclient.rb +569 -0
- data/test/muc/tc_muc_simplemucclient.rb +72 -0
- data/test/roster/.tc_helper.rb.swp +0 -0
- data/test/roster/tc_helper.rb +389 -0
- data/test/roster/tc_iqqueryroster.rb +140 -0
- data/test/roster/tc_xroster.rb +70 -0
- data/test/tc_callbacks.rb +128 -0
- data/test/tc_class_names.rb +129 -0
- data/test/tc_client.rb +30 -0
- data/test/tc_error.rb +103 -0
- data/test/tc_idgenerator.rb +30 -0
- data/test/tc_iq.rb +109 -0
- data/test/tc_iqquery.rb +31 -0
- data/test/tc_jid.rb +202 -0
- data/test/tc_message.rb +114 -0
- data/test/tc_presence.rb +148 -0
- data/test/tc_stream.rb +182 -0
- data/test/tc_streamError.rb +87 -0
- data/test/tc_streamSend.rb +59 -0
- data/test/tc_streamThreaded.rb +168 -0
- data/test/tc_xmlstanza.rb +76 -0
- data/test/ts_xmpp4r.rb +34 -0
- data/test/vcard/tc_iqvcard.rb +52 -0
- data/test/version/tc_helper.rb +46 -0
- data/test/version/tc_iqqueryversion.rb +96 -0
- data/tools/doctoweb.bash +30 -0
- data/tools/gen_requires.bash +10 -0
- metadata +232 -0
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
require 'time' # For Time#xmlschema
|
|
2
|
+
|
|
3
|
+
require 'xmpp4r/feature_negotiation/iq/feature'
|
|
4
|
+
|
|
5
|
+
module Jabber
|
|
6
|
+
module Bytestreams
|
|
7
|
+
##
|
|
8
|
+
# Iq child 'si' for Stream-Initiation
|
|
9
|
+
class IqSi < REXML::Element
|
|
10
|
+
PROFILE_FILETRANSFER = 'http://jabber.org/protocol/si/profile/file-transfer'
|
|
11
|
+
|
|
12
|
+
def initialize(id=nil, profile=nil, mime_type=nil)
|
|
13
|
+
super('si')
|
|
14
|
+
|
|
15
|
+
add_namespace 'http://jabber.org/protocol/si'
|
|
16
|
+
self.id = id
|
|
17
|
+
self.profile = profile
|
|
18
|
+
self.mime_type = mime_type
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def IqSi.import(element)
|
|
22
|
+
IqSi::new.import(element)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def typed_add(element)
|
|
26
|
+
if element.kind_of?(REXML::Element) and element.name == 'file'
|
|
27
|
+
super IqSiFile.new.import(element)
|
|
28
|
+
elsif element.kind_of?(REXML::Element) and element.name == 'feature'
|
|
29
|
+
super FeatureNegotiation::IqFeature.new.import(element)
|
|
30
|
+
else
|
|
31
|
+
super element
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
##
|
|
36
|
+
# Session ID of this stream
|
|
37
|
+
def id
|
|
38
|
+
attributes['id']
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
##
|
|
42
|
+
# Set Session ID of this stream
|
|
43
|
+
def id=(s)
|
|
44
|
+
attributes['id'] = s
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
##
|
|
48
|
+
# MIME type of this stream
|
|
49
|
+
def mime_type
|
|
50
|
+
attributes['mime-type']
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
##
|
|
54
|
+
# Set MIME type of this stream
|
|
55
|
+
def mime_type=(s)
|
|
56
|
+
attributes['mime-type'] = s
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
##
|
|
60
|
+
# Stream profile, can indicate file-transfer
|
|
61
|
+
def profile
|
|
62
|
+
attributes['profile']
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
##
|
|
66
|
+
# Set stream profile
|
|
67
|
+
def profile=(s)
|
|
68
|
+
attributes['profile'] = s
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
##
|
|
72
|
+
# <file/> child
|
|
73
|
+
# result:: [IqSiFile]
|
|
74
|
+
def file
|
|
75
|
+
first_element('file')
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
##
|
|
79
|
+
# <feature/> child
|
|
80
|
+
# result:: [IqFeature]
|
|
81
|
+
def feature
|
|
82
|
+
first_element('feature')
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
Iq.add_elementclass('si', IqSi)
|
|
87
|
+
|
|
88
|
+
##
|
|
89
|
+
# File-transfer meta-information,
|
|
90
|
+
# may appear as <file/> in IqSi
|
|
91
|
+
class IqSiFile < REXML::Element
|
|
92
|
+
def initialize(fname=nil, size=nil)
|
|
93
|
+
super 'file'
|
|
94
|
+
add_namespace IqSi::PROFILE_FILETRANSFER
|
|
95
|
+
self.fname = fname
|
|
96
|
+
self.size = size
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def typed_add(element)
|
|
100
|
+
if element.kind_of?(REXML::Element) and element.name == 'range'
|
|
101
|
+
super IqSiFileRange.new.import(element)
|
|
102
|
+
else
|
|
103
|
+
super element
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
##
|
|
108
|
+
# Get filename (attribute 'name')
|
|
109
|
+
def fname
|
|
110
|
+
attributes['name']
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
##
|
|
114
|
+
# Set filename (attribute 'name')
|
|
115
|
+
def fname=(s)
|
|
116
|
+
attributes['name'] = s
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
##
|
|
120
|
+
# Get MD5 hash
|
|
121
|
+
def hash
|
|
122
|
+
attributes['hash']
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
##
|
|
126
|
+
# Set MD5 hash
|
|
127
|
+
def hash=(s)
|
|
128
|
+
attributes['hash'] = s
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
##
|
|
132
|
+
# Get file date
|
|
133
|
+
# result:: [Time] or nil
|
|
134
|
+
def date
|
|
135
|
+
begin
|
|
136
|
+
Time.xmlschema(attributes['date'])
|
|
137
|
+
rescue ArgumentError
|
|
138
|
+
nil
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
##
|
|
143
|
+
# Set file date
|
|
144
|
+
# d:: [Time] or nil
|
|
145
|
+
def date=(d)
|
|
146
|
+
attributes['date'] = (d ? d.xmlschema : nil)
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
##
|
|
150
|
+
# File size in bytes
|
|
151
|
+
# result:: [Fixnum]
|
|
152
|
+
def size
|
|
153
|
+
(attributes['size'] =~ /^\d+$/) ? attributes['size'].to_i : nil
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
##
|
|
157
|
+
# Set file size
|
|
158
|
+
def size=(s)
|
|
159
|
+
attributes['size'] = s ? s.to_s : nil
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
##
|
|
163
|
+
# File description
|
|
164
|
+
def description
|
|
165
|
+
first_element_text('desc')
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
##
|
|
169
|
+
# Set file description
|
|
170
|
+
def description=(s)
|
|
171
|
+
replace_element_text('desc', s)
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
##
|
|
175
|
+
# <range/> child
|
|
176
|
+
#
|
|
177
|
+
# A file-transfer offer may contain this with
|
|
178
|
+
# no attributes set, indicating the ability to
|
|
179
|
+
# do ranged transfers.
|
|
180
|
+
# result:: [IqSiFileRange]
|
|
181
|
+
def range
|
|
182
|
+
first_element('range')
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
##
|
|
187
|
+
# Information for ranged transfers
|
|
188
|
+
class IqSiFileRange < REXML::Element
|
|
189
|
+
def initialize(offset=nil, length=nil)
|
|
190
|
+
super('range')
|
|
191
|
+
|
|
192
|
+
self.offset = offset
|
|
193
|
+
self.length = length
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
##
|
|
197
|
+
# File offset (for continuing an interrupted transfer)
|
|
198
|
+
def offset
|
|
199
|
+
(attributes['offset'] =~ /^\d+$/) ? attributes['offset'].to_i : nil
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
##
|
|
203
|
+
# Set file offset
|
|
204
|
+
def offset=(o)
|
|
205
|
+
attributes['offset'] = (o ? o.to_s : nil)
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
##
|
|
209
|
+
# File length (if not to transfer whole file)
|
|
210
|
+
def length
|
|
211
|
+
(attributes['length'] =~ /^\d+$/) ? attributes['length'].to_i : nil
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
##
|
|
215
|
+
# Set file length
|
|
216
|
+
def length=(o)
|
|
217
|
+
attributes['length'] = (o ? o.to_s : nil)
|
|
218
|
+
end
|
|
219
|
+
end
|
|
220
|
+
end
|
|
221
|
+
end
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
require 'xmpp4r/bytestreams/iq/si.rb'
|
|
2
|
+
require 'xmpp4r/bytestreams/iq/bytestreams.rb'
|
|
3
|
+
require 'xmpp4r/bytestreams/helper/ibb/base.rb'
|
|
4
|
+
require 'xmpp4r/bytestreams/helper/ibb/initiator.rb'
|
|
5
|
+
require 'xmpp4r/bytestreams/helper/ibb/target.rb'
|
|
6
|
+
require 'xmpp4r/bytestreams/helper/filetransfer.rb'
|
|
7
|
+
require 'xmpp4r/bytestreams/helper/socks5bytestreams/base.rb'
|
|
8
|
+
require 'xmpp4r/bytestreams/helper/socks5bytestreams/initiator.rb'
|
|
9
|
+
require 'xmpp4r/bytestreams/helper/socks5bytestreams/server.rb'
|
|
10
|
+
require 'xmpp4r/bytestreams/helper/socks5bytestreams/target.rb'
|
|
11
|
+
require 'xmpp4r/bytestreams/helper/socks5bytestreams/socks5.rb'
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
# =XMPP4R - XMPP Library for Ruby
|
|
2
|
+
# License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option.
|
|
3
|
+
# Website::http://home.gna.org/xmpp4r/
|
|
4
|
+
|
|
5
|
+
require 'resolv'
|
|
6
|
+
|
|
7
|
+
require 'xmpp4r/connection'
|
|
8
|
+
require 'xmpp4r/authenticationfailure'
|
|
9
|
+
require 'xmpp4r/sasl'
|
|
10
|
+
|
|
11
|
+
module Jabber
|
|
12
|
+
|
|
13
|
+
# The client class provides everything needed to build a basic XMPP
|
|
14
|
+
# Client.
|
|
15
|
+
#
|
|
16
|
+
# If you want your connection to survive disconnects and timeouts,
|
|
17
|
+
# catch exception in Stream#on_exception and re-call Client#connect
|
|
18
|
+
# and Client#auth. Don't forget to re-send initial Presence and
|
|
19
|
+
# everything else you need to setup your session.
|
|
20
|
+
class Client < Connection
|
|
21
|
+
|
|
22
|
+
# The client's JID
|
|
23
|
+
attr_reader :jid
|
|
24
|
+
|
|
25
|
+
##
|
|
26
|
+
# Create a new Client. If threaded mode is activated, callbacks are called
|
|
27
|
+
# as soon as messages are received; If it isn't, you have to call
|
|
28
|
+
# Stream#process from time to time.
|
|
29
|
+
#
|
|
30
|
+
# Remember to *always* put a resource in your JID unless the server can do SASL.
|
|
31
|
+
def initialize(jid, threaded = true)
|
|
32
|
+
super(threaded)
|
|
33
|
+
@jid = (jid.kind_of?(JID) ? jid : JID.new(jid.to_s))
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
##
|
|
37
|
+
# connect to the server
|
|
38
|
+
# (chaining-friendly)
|
|
39
|
+
#
|
|
40
|
+
# If you omit the optional host argument SRV records for your jid will
|
|
41
|
+
# be resolved. If none works, fallback is connecting to the domain part
|
|
42
|
+
# of the jid.
|
|
43
|
+
# host:: [String] Optional c2s host, will be extracted from jid if nil
|
|
44
|
+
# return:: self
|
|
45
|
+
def connect(host = nil, port = 5222)
|
|
46
|
+
if host.nil?
|
|
47
|
+
begin
|
|
48
|
+
srv = []
|
|
49
|
+
Resolv::DNS.open { |dns|
|
|
50
|
+
# If ruby version is too old and SRV is unknown, this will raise a NameError
|
|
51
|
+
# which is catched below
|
|
52
|
+
Jabber::debuglog("RESOLVING:\n_xmpp-client._tcp.#{@jid.domain} (SRV)")
|
|
53
|
+
srv = dns.getresources("_xmpp-client._tcp.#{@jid.domain}", Resolv::DNS::Resource::IN::SRV)
|
|
54
|
+
}
|
|
55
|
+
# Sort SRV records: lowest priority first, highest weight first
|
|
56
|
+
srv.sort! { |a,b| (a.priority != b.priority) ? (a.priority <=> b.priority) : (b.weight <=> a.weight) }
|
|
57
|
+
|
|
58
|
+
srv.each { |record|
|
|
59
|
+
begin
|
|
60
|
+
connect(record.target.to_s, record.port)
|
|
61
|
+
# Success
|
|
62
|
+
return self
|
|
63
|
+
rescue SocketError
|
|
64
|
+
# Try next SRV record
|
|
65
|
+
end
|
|
66
|
+
}
|
|
67
|
+
rescue NameError
|
|
68
|
+
$stderr.puts "Resolv::DNS does not support SRV records. Please upgrade to ruby-1.8.3 or later!"
|
|
69
|
+
end
|
|
70
|
+
# Fallback to normal connect method
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
super(host.nil? ? jid.domain : host, port)
|
|
74
|
+
self
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
##
|
|
78
|
+
# Close the connection,
|
|
79
|
+
# sends <tt></stream:stream></tt> tag first
|
|
80
|
+
def close
|
|
81
|
+
send("</stream:stream>")
|
|
82
|
+
super
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
##
|
|
86
|
+
# Start the stream-parser and send the client-specific stream opening element
|
|
87
|
+
def start
|
|
88
|
+
super
|
|
89
|
+
send(generate_stream_start(@jid.domain)) { |e|
|
|
90
|
+
if e.name == 'stream'
|
|
91
|
+
true
|
|
92
|
+
else
|
|
93
|
+
false
|
|
94
|
+
end
|
|
95
|
+
}
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
##
|
|
99
|
+
# Authenticate with the server
|
|
100
|
+
#
|
|
101
|
+
# Throws AuthenticationFailure
|
|
102
|
+
#
|
|
103
|
+
# Authentication mechanisms are used in the following preference:
|
|
104
|
+
# * SASL DIGEST-MD5
|
|
105
|
+
# * SASL PLAIN
|
|
106
|
+
# * Non-SASL digest
|
|
107
|
+
# password:: [String]
|
|
108
|
+
def auth(password)
|
|
109
|
+
begin
|
|
110
|
+
if @stream_mechanisms.include? 'DIGEST-MD5'
|
|
111
|
+
auth_sasl SASL.new(self, 'DIGEST-MD5'), password
|
|
112
|
+
elsif @stream_mechanisms.include? 'PLAIN'
|
|
113
|
+
auth_sasl SASL.new(self, 'PLAIN'), password
|
|
114
|
+
else
|
|
115
|
+
auth_nonsasl(password)
|
|
116
|
+
end
|
|
117
|
+
rescue
|
|
118
|
+
Jabber::debuglog("#{$!.class}: #{$!}\n#{$!.backtrace.join("\n")}")
|
|
119
|
+
raise AuthenticationFailure.new, $!.to_s
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
##
|
|
124
|
+
# Use a SASL authentication mechanism and bind to a resource
|
|
125
|
+
#
|
|
126
|
+
# If there was no resource given in the jid, the jid/resource
|
|
127
|
+
# generated by the server will be accepted.
|
|
128
|
+
#
|
|
129
|
+
# This method should not be used directly. Instead, Client#auth
|
|
130
|
+
# may look for the best mechanism suitable.
|
|
131
|
+
# sasl:: Descendant of [Jabber::SASL::Base]
|
|
132
|
+
# password:: [String]
|
|
133
|
+
def auth_sasl(sasl, password)
|
|
134
|
+
sasl.auth(password)
|
|
135
|
+
|
|
136
|
+
# Restart stream after SASL auth
|
|
137
|
+
stop
|
|
138
|
+
start
|
|
139
|
+
# And wait for features - again
|
|
140
|
+
@features_lock.lock
|
|
141
|
+
@features_lock.unlock
|
|
142
|
+
|
|
143
|
+
# Resource binding (RFC3920 - 7)
|
|
144
|
+
if @stream_features.has_key? 'bind'
|
|
145
|
+
iq = Iq.new(:set)
|
|
146
|
+
bind = iq.add REXML::Element.new('bind')
|
|
147
|
+
bind.add_namespace @stream_features['bind']
|
|
148
|
+
if jid.resource
|
|
149
|
+
resource = bind.add REXML::Element.new('resource')
|
|
150
|
+
resource.text = jid.resource
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
send_with_id(iq) { |reply|
|
|
154
|
+
reported_jid = reply.first_element('jid')
|
|
155
|
+
if reply.type == :result and reported_jid and reported_jid.text
|
|
156
|
+
@jid = JID.new(reported_jid.text)
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
true
|
|
160
|
+
}
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
# Session starting
|
|
164
|
+
if @stream_features.has_key? 'session'
|
|
165
|
+
iq = Iq.new(:set)
|
|
166
|
+
session = iq.add REXML::Element.new('session')
|
|
167
|
+
session.add_namespace @stream_features['session']
|
|
168
|
+
|
|
169
|
+
send_with_id(iq) { true }
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
##
|
|
174
|
+
# Send auth with given password and wait for result
|
|
175
|
+
# (non-SASL)
|
|
176
|
+
#
|
|
177
|
+
# Throws ErrorException
|
|
178
|
+
# password:: [String] the password
|
|
179
|
+
# digest:: [Boolean] use Digest authentication
|
|
180
|
+
def auth_nonsasl(password, digest=true)
|
|
181
|
+
authset = nil
|
|
182
|
+
if digest
|
|
183
|
+
authset = Iq::new_authset_digest(@jid, @streamid.to_s, password)
|
|
184
|
+
else
|
|
185
|
+
authset = Iq::new_authset(@jid, password)
|
|
186
|
+
end
|
|
187
|
+
send_with_id(authset) do |r|
|
|
188
|
+
true
|
|
189
|
+
end
|
|
190
|
+
$defout.flush
|
|
191
|
+
|
|
192
|
+
true
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
##
|
|
196
|
+
# Register a new user account
|
|
197
|
+
# (may be used instead of Client#auth)
|
|
198
|
+
#
|
|
199
|
+
# This method may raise ErrorException if the registration was
|
|
200
|
+
# not successful.
|
|
201
|
+
def register(password)
|
|
202
|
+
reg = Iq.new_register(jid.node, password)
|
|
203
|
+
reg.to = jid.domain
|
|
204
|
+
send_with_id(reg) { |answer|
|
|
205
|
+
true
|
|
206
|
+
}
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
##
|
|
210
|
+
# Remove the registration of a user account
|
|
211
|
+
#
|
|
212
|
+
# *WARNING:* this deletes your roster and everything else
|
|
213
|
+
# stored on the server!
|
|
214
|
+
def remove_registration
|
|
215
|
+
reg = Iq.new_register
|
|
216
|
+
reg.to = jid.domain
|
|
217
|
+
reg.query.add(REXML::Element.new('remove'))
|
|
218
|
+
send_with_id(reg) { |answer|
|
|
219
|
+
p answer.to_s
|
|
220
|
+
true
|
|
221
|
+
}
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
##
|
|
225
|
+
# Change the client's password
|
|
226
|
+
#
|
|
227
|
+
# Threading is suggested, as this code waits
|
|
228
|
+
# for an answer.
|
|
229
|
+
#
|
|
230
|
+
# Raises an exception upon error response (ErrorException from
|
|
231
|
+
# Stream#send_with_id).
|
|
232
|
+
# new_password:: [String] New password
|
|
233
|
+
def password=(new_password)
|
|
234
|
+
iq = Iq::new_query(:set, @jid.domain)
|
|
235
|
+
iq.query.add_namespace('jabber:iq:register')
|
|
236
|
+
iq.query.add(REXML::Element.new('username')).text = @jid.node
|
|
237
|
+
iq.query.add(REXML::Element.new('password')).text = new_password
|
|
238
|
+
|
|
239
|
+
err = nil
|
|
240
|
+
send_with_id(iq) { |answer|
|
|
241
|
+
if answer.type == :result
|
|
242
|
+
true
|
|
243
|
+
else
|
|
244
|
+
false
|
|
245
|
+
end
|
|
246
|
+
}
|
|
247
|
+
end
|
|
248
|
+
end
|
|
249
|
+
end
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# =XMPP4R - XMPP Library for Ruby
|
|
2
|
+
# License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option.
|
|
3
|
+
# Website::http://home.gna.org/xmpp4r/
|
|
4
|
+
|
|
5
|
+
require 'xmpp4r/connection'
|
|
6
|
+
|
|
7
|
+
module Jabber
|
|
8
|
+
##
|
|
9
|
+
# The component class provides everything needed to build a XMPP Component.
|
|
10
|
+
#
|
|
11
|
+
# Components are more flexible as they are only restricted in the use of a
|
|
12
|
+
# fixed domain. node and resource of JIDs are freely choosable for all stanzas.
|
|
13
|
+
class Component < Connection
|
|
14
|
+
|
|
15
|
+
# The component's JID
|
|
16
|
+
attr_reader :jid
|
|
17
|
+
|
|
18
|
+
# The server's address
|
|
19
|
+
attr_reader :server_address
|
|
20
|
+
|
|
21
|
+
# The server's port
|
|
22
|
+
attr_reader :server_port
|
|
23
|
+
|
|
24
|
+
# Create a new Component
|
|
25
|
+
# jid:: [JID]
|
|
26
|
+
def initialize(jid, server_address=nil, server_port=5347, threaded = true)
|
|
27
|
+
super(threaded)
|
|
28
|
+
@jid = (jid.kind_of?(JID) ? jid : JID.new(jid.to_s))
|
|
29
|
+
|
|
30
|
+
if server_address
|
|
31
|
+
$stderr.puts "Passing server and port to Jabber::Component::new is " +
|
|
32
|
+
"obsolete and will vanish in some later XMPP4R release. " +
|
|
33
|
+
"Please use these arguments when calling " +
|
|
34
|
+
"Jabber::Client#connect"
|
|
35
|
+
@server_address = server_address
|
|
36
|
+
@server_port = server_port
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Connect to the server
|
|
41
|
+
# (chaining-friendly)
|
|
42
|
+
# server:: [String] Hostname
|
|
43
|
+
# port:: [Integer] TCP port (5347)
|
|
44
|
+
# return:: self
|
|
45
|
+
def connect(server=nil, port=5347)
|
|
46
|
+
if server
|
|
47
|
+
super(server, port)
|
|
48
|
+
else
|
|
49
|
+
super(@server_address, @server_port)
|
|
50
|
+
end
|
|
51
|
+
self
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
##
|
|
55
|
+
# Close the connection,
|
|
56
|
+
# sends <tt></stream:stream></tt> tag first
|
|
57
|
+
def close
|
|
58
|
+
send("</stream:stream>")
|
|
59
|
+
super
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def generate_stream_start(to=nil, from=nil, id=nil, xml_lang="en", xmlns="jabber:component:accept", version="1.0")
|
|
63
|
+
super
|
|
64
|
+
end
|
|
65
|
+
private :generate_stream_start
|
|
66
|
+
|
|
67
|
+
##
|
|
68
|
+
# Start the stream-parser and send the component-specific stream opening element
|
|
69
|
+
def start
|
|
70
|
+
super
|
|
71
|
+
send(generate_stream_start(@jid)) { |e|
|
|
72
|
+
if e.name == 'stream'
|
|
73
|
+
true
|
|
74
|
+
else
|
|
75
|
+
false
|
|
76
|
+
end
|
|
77
|
+
}
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
##
|
|
81
|
+
# Send auth with given secret and wait for result
|
|
82
|
+
#
|
|
83
|
+
# Throws AuthenticationFailure
|
|
84
|
+
# secret:: [String] the shared secret
|
|
85
|
+
def auth(secret)
|
|
86
|
+
hash = Digest::SHA1::new(@streamid.to_s + secret).to_s
|
|
87
|
+
authenticated = false
|
|
88
|
+
send("<handshake>#{hash}</handshake>") { |r|
|
|
89
|
+
if r.prefix == 'stream' and r.name == 'error'
|
|
90
|
+
true
|
|
91
|
+
elsif r.name == 'handshake'
|
|
92
|
+
authenticated = true
|
|
93
|
+
true
|
|
94
|
+
else
|
|
95
|
+
false
|
|
96
|
+
end
|
|
97
|
+
}
|
|
98
|
+
unless authenticated
|
|
99
|
+
raise AuthenticationFailure.new, "Component authentication failed"
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|