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,37 @@
|
|
|
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 'singleton'
|
|
6
|
+
|
|
7
|
+
module Jabber
|
|
8
|
+
##
|
|
9
|
+
# The Jabber::IdGenerator class generates unique IDs for use
|
|
10
|
+
# in XMMP stanzas. Jabber::IdGenerator includes the Singleton
|
|
11
|
+
# Mixin, usage as following:
|
|
12
|
+
# Jabber::IdGenerator.generate_id
|
|
13
|
+
# => "23"
|
|
14
|
+
class IdGenerator
|
|
15
|
+
include Singleton
|
|
16
|
+
|
|
17
|
+
def initialize
|
|
18
|
+
@last_id = 0
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
##
|
|
22
|
+
# Generate an unique ID.
|
|
23
|
+
#
|
|
24
|
+
# This is kind of boring this way, as it just counts up
|
|
25
|
+
# a number. Maybe something more random somewhen...
|
|
26
|
+
def IdGenerator.generate_id
|
|
27
|
+
IdGenerator.instance.generate_id
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def generate_id
|
|
31
|
+
@last_id += 1
|
|
32
|
+
timefrac = Time.new.to_f.to_s.split(/\./, 2).last[-3..-1]
|
|
33
|
+
|
|
34
|
+
"#{@last_id}#{timefrac}"
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
data/lib/xmpp4r/iq.rb
ADDED
|
@@ -0,0 +1,229 @@
|
|
|
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/xmlstanza'
|
|
6
|
+
require 'xmpp4r/jid'
|
|
7
|
+
require 'digest/sha1'
|
|
8
|
+
|
|
9
|
+
module Jabber
|
|
10
|
+
##
|
|
11
|
+
# IQ: Information/Query
|
|
12
|
+
# (see RFC3920 - 9.2.3
|
|
13
|
+
#
|
|
14
|
+
# A class used to build/parse IQ requests/responses
|
|
15
|
+
class Iq < XMLStanza
|
|
16
|
+
@@element_classes = {}
|
|
17
|
+
|
|
18
|
+
##
|
|
19
|
+
# Build a new <iq/> stanza
|
|
20
|
+
# type:: [Symbol] or nil, see Iq#type
|
|
21
|
+
# to:: [JID] Recipient
|
|
22
|
+
def initialize(type = nil, to = nil)
|
|
23
|
+
super("iq")
|
|
24
|
+
if not to.nil?
|
|
25
|
+
set_to(to)
|
|
26
|
+
end
|
|
27
|
+
if not type.nil?
|
|
28
|
+
set_type(type)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
##
|
|
33
|
+
# Get the type of the Iq stanza
|
|
34
|
+
#
|
|
35
|
+
# The following values are allowed:
|
|
36
|
+
# * :get
|
|
37
|
+
# * :set
|
|
38
|
+
# * :result
|
|
39
|
+
# * :error
|
|
40
|
+
# result:: [Symbol] or nil
|
|
41
|
+
def type
|
|
42
|
+
case super
|
|
43
|
+
when 'get' then :get
|
|
44
|
+
when 'set' then :set
|
|
45
|
+
when 'result' then :result
|
|
46
|
+
when 'error' then :error
|
|
47
|
+
else nil
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
##
|
|
52
|
+
# Set the type of the Iq stanza (see Iq#type)
|
|
53
|
+
# v:: [Symbol] or nil
|
|
54
|
+
def type=(v)
|
|
55
|
+
case v
|
|
56
|
+
when :get then super('get')
|
|
57
|
+
when :set then super('set')
|
|
58
|
+
when :result then super('result')
|
|
59
|
+
when :error then super('error')
|
|
60
|
+
else super(nil)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
##
|
|
65
|
+
# Set the type of the Iq stanza (chaining-friendly)
|
|
66
|
+
# v:: [Symbol] or nil
|
|
67
|
+
def set_type(v)
|
|
68
|
+
self.type = v
|
|
69
|
+
self
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
##
|
|
73
|
+
# Returns the iq's query child, or nil
|
|
74
|
+
# result:: [IqQuery]
|
|
75
|
+
def query
|
|
76
|
+
first_element('query')
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
##
|
|
80
|
+
# Delete old elements named newquery.name
|
|
81
|
+
#
|
|
82
|
+
# newquery:: [REXML::Element] will be added
|
|
83
|
+
def query=(newquery)
|
|
84
|
+
delete_elements(newquery.name)
|
|
85
|
+
add(newquery)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
##
|
|
89
|
+
# Returns the iq's query's namespace, or nil
|
|
90
|
+
# result:: [String]
|
|
91
|
+
def queryns
|
|
92
|
+
e = first_element('query')
|
|
93
|
+
if e
|
|
94
|
+
return e.namespace
|
|
95
|
+
else
|
|
96
|
+
return nil
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
##
|
|
101
|
+
# Returns the iq's <vCard/> child, or nil
|
|
102
|
+
# result:: [IqVcard]
|
|
103
|
+
def vcard
|
|
104
|
+
first_element('vCard')
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
##
|
|
108
|
+
# Create a new iq from a stanza,
|
|
109
|
+
# copies all attributes and children from xmlstanza
|
|
110
|
+
# xmlstanza:: [REXML::Element] Source stanza
|
|
111
|
+
# return:: [Iq] New stanza
|
|
112
|
+
def Iq.import(xmlstanza)
|
|
113
|
+
Iq::new.import(xmlstanza)
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
##
|
|
117
|
+
# Add an element to the Iq stanza
|
|
118
|
+
# element:: [REXML::Element] Element to add.
|
|
119
|
+
# Will be automatically converted (imported) to
|
|
120
|
+
# a class registered with add_elementclass
|
|
121
|
+
def typed_add(element)
|
|
122
|
+
if element.kind_of?(REXML::Element) && @@element_classes.has_key?(element.name)
|
|
123
|
+
super(@@element_classes[element.name]::import(element))
|
|
124
|
+
else
|
|
125
|
+
super(element)
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
##
|
|
130
|
+
# Create a new Iq stanza with an unspecified query child
|
|
131
|
+
# (<query/> has no namespace)
|
|
132
|
+
def Iq.new_query(type = nil, to = nil)
|
|
133
|
+
iq = Iq::new(type, to)
|
|
134
|
+
query = IqQuery::new
|
|
135
|
+
iq.add(query)
|
|
136
|
+
iq
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
##
|
|
140
|
+
# Create a new jabber:iq:auth set Stanza.
|
|
141
|
+
def Iq.new_authset(jid, password)
|
|
142
|
+
iq = Iq::new(:set)
|
|
143
|
+
query = IqQuery::new
|
|
144
|
+
query.add_namespace('jabber:iq:auth')
|
|
145
|
+
query.add(REXML::Element::new('username').add_text(jid.node))
|
|
146
|
+
query.add(REXML::Element::new('password').add_text(password))
|
|
147
|
+
query.add(REXML::Element::new('resource').add_text(jid.resource)) if not jid.resource.nil?
|
|
148
|
+
iq.add(query)
|
|
149
|
+
iq
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
##
|
|
153
|
+
# Create a new jabber:iq:auth set Stanza for Digest authentication
|
|
154
|
+
def Iq.new_authset_digest(jid, session_id, password)
|
|
155
|
+
iq = Iq::new(:set)
|
|
156
|
+
query = IqQuery::new
|
|
157
|
+
query.add_namespace('jabber:iq:auth')
|
|
158
|
+
query.add(REXML::Element::new('username').add_text(jid.node))
|
|
159
|
+
query.add(REXML::Element::new('digest').add_text(Digest::SHA1.new(session_id + password).hexdigest))
|
|
160
|
+
query.add(REXML::Element::new('resource').add_text(jid.resource)) if not jid.resource.nil?
|
|
161
|
+
iq.add(query)
|
|
162
|
+
iq
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
##
|
|
166
|
+
# Create a new jabber:iq:register set stanza for service/server registration
|
|
167
|
+
# username:: [String] (Element will be ommited if unset)
|
|
168
|
+
# password:: [String] (Element will be ommited if unset)
|
|
169
|
+
def Iq.new_register(username=nil, password=nil)
|
|
170
|
+
iq = Iq::new(:set)
|
|
171
|
+
query = IqQuery::new
|
|
172
|
+
query.add_namespace('jabber:iq:register')
|
|
173
|
+
query.add(REXML::Element::new('username').add_text(username)) if username
|
|
174
|
+
query.add(REXML::Element::new('password').add_text(password)) if password
|
|
175
|
+
iq.add(query)
|
|
176
|
+
iq
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
##
|
|
180
|
+
# Create a new jabber:iq:roster get Stanza.
|
|
181
|
+
#
|
|
182
|
+
# IqQueryRoster is unused here because possibly not require'd
|
|
183
|
+
def Iq.new_rosterget
|
|
184
|
+
iq = Iq::new(:get)
|
|
185
|
+
query = IqQuery::new
|
|
186
|
+
query.add_namespace('jabber:iq:roster')
|
|
187
|
+
iq.add(query)
|
|
188
|
+
iq
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
##
|
|
192
|
+
# Create a new jabber:iq:roster get Stanza.
|
|
193
|
+
def Iq.new_browseget
|
|
194
|
+
iq = Iq::new(:get)
|
|
195
|
+
query = IqQuery::new
|
|
196
|
+
query.add_namespace('jabber:iq:browse')
|
|
197
|
+
iq.add(query)
|
|
198
|
+
iq
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
##
|
|
202
|
+
# Create a new jabber:iq:roster set Stanza.
|
|
203
|
+
def Iq.new_rosterset
|
|
204
|
+
iq = Iq::new(:set)
|
|
205
|
+
query = IqQuery::new
|
|
206
|
+
query.add_namespace('jabber:iq:roster')
|
|
207
|
+
iq.add(query)
|
|
208
|
+
iq
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
##
|
|
212
|
+
# Add a class by name.
|
|
213
|
+
# Elements with this name will be automatically converted
|
|
214
|
+
# to the specific class.
|
|
215
|
+
# Used for <query/>, <vCard>, <pubsub> etc.
|
|
216
|
+
# name:: [String] Element name
|
|
217
|
+
# elementclass:: [Class] Target class
|
|
218
|
+
def Iq.add_elementclass(name, elementclass)
|
|
219
|
+
@@element_classes[name] = elementclass
|
|
220
|
+
end
|
|
221
|
+
end
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
# Actually these should be included at the top,
|
|
225
|
+
# but then they would be unable to call Iq.add_elementclass
|
|
226
|
+
# because it hasn't just been defined.
|
|
227
|
+
|
|
228
|
+
require 'xmpp4r/query'
|
|
229
|
+
require 'xmpp4r/vcard/iq/vcard'
|
data/lib/xmpp4r/jid.rb
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
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
|
+
module Jabber
|
|
6
|
+
##
|
|
7
|
+
# The JID class represents a Jabber Identifier as described by
|
|
8
|
+
# RFC3920 section 3.1.
|
|
9
|
+
#
|
|
10
|
+
# Note that you can use JIDs also for Sorting, Hash keys, ...
|
|
11
|
+
class JID
|
|
12
|
+
include Comparable
|
|
13
|
+
|
|
14
|
+
PATTERN = /^(?:([^@]*)@)??([^@\/]*)(?:\/(.*?))?$/
|
|
15
|
+
|
|
16
|
+
begin
|
|
17
|
+
require 'idn'
|
|
18
|
+
USE_STRINGPREP = true
|
|
19
|
+
rescue LoadError
|
|
20
|
+
USE_STRINGPREP = false
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
##
|
|
24
|
+
# Create a new JID. If called as new('a@b/c'), parse the string and
|
|
25
|
+
# split (node, domain, resource)
|
|
26
|
+
def initialize(node = "", domain = nil, resource = nil)
|
|
27
|
+
@resource = resource
|
|
28
|
+
@domain = domain
|
|
29
|
+
@node = node
|
|
30
|
+
if @domain.nil? and @resource.nil? and @node
|
|
31
|
+
@node, @domain, @resource = @node.to_s.scan(PATTERN).first
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
if USE_STRINGPREP
|
|
35
|
+
@node = IDN::Stringprep.nodeprep(@node) if @node
|
|
36
|
+
@domain = IDN::Stringprep.nameprep(@domain) if @domain
|
|
37
|
+
@resource = IDN::Stringprep.resourceprep(@resource) if @resource
|
|
38
|
+
else
|
|
39
|
+
@node.downcase! if @node
|
|
40
|
+
@domain.downcase! if @domain
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
raise ArgumentError, 'Node too long' if (@node || '').length > 1023
|
|
44
|
+
raise ArgumentError, 'Domain too long' if (@domain || '').length > 1023
|
|
45
|
+
raise ArgumentError, 'Resource too long' if (@resource || '').length > 1023
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
##
|
|
49
|
+
# Returns a string representation of the JID
|
|
50
|
+
# * ""
|
|
51
|
+
# * "domain"
|
|
52
|
+
# * "node@domain"
|
|
53
|
+
# * "domain/resource"
|
|
54
|
+
# * "node@domain/resource"
|
|
55
|
+
def to_s
|
|
56
|
+
s = @domain
|
|
57
|
+
s = "#{@node}@#{s}" if @node
|
|
58
|
+
s += "/#{@resource}" if @resource
|
|
59
|
+
return s
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
##
|
|
63
|
+
# Returns a new JID with resource removed.
|
|
64
|
+
# return:: [JID]
|
|
65
|
+
def strip
|
|
66
|
+
JID::new(@node, @domain)
|
|
67
|
+
end
|
|
68
|
+
alias_method :bare, :strip
|
|
69
|
+
|
|
70
|
+
##
|
|
71
|
+
# Removes the resource (sets it to nil)
|
|
72
|
+
# return:: [JID] self
|
|
73
|
+
def strip!
|
|
74
|
+
@resource = nil
|
|
75
|
+
self
|
|
76
|
+
end
|
|
77
|
+
alias_method :bare!, :strip!
|
|
78
|
+
|
|
79
|
+
##
|
|
80
|
+
# Returns a hash value of the String representation
|
|
81
|
+
# (see JID#to_s)
|
|
82
|
+
def hash
|
|
83
|
+
return to_s.hash
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
##
|
|
87
|
+
# Ccompare to another JID
|
|
88
|
+
#
|
|
89
|
+
# String representations are compared, see JID#to_s
|
|
90
|
+
def eql?(o)
|
|
91
|
+
to_s.eql?(o.to_s)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
##
|
|
95
|
+
# Ccompare to another JID
|
|
96
|
+
#
|
|
97
|
+
# String representations are compared, see JID#to_s
|
|
98
|
+
def ==(o)
|
|
99
|
+
to_s == o.to_s
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
##
|
|
103
|
+
# Compare two JIDs,
|
|
104
|
+
# helpful for sorting etc.
|
|
105
|
+
#
|
|
106
|
+
# String representations are compared, see JID#to_s
|
|
107
|
+
def <=>(o)
|
|
108
|
+
to_s <=> o.to_s
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# Get the JID's node
|
|
112
|
+
def node
|
|
113
|
+
@node
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# Set the JID's node
|
|
117
|
+
def node=(v)
|
|
118
|
+
@node = v.to_s
|
|
119
|
+
if USE_STRINGPREP
|
|
120
|
+
@node = IDN::Stringprep.nodeprep(@node) if @node
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# Get the JID's domain
|
|
125
|
+
def domain
|
|
126
|
+
return nil if @domain.empty?
|
|
127
|
+
@domain
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
# Set the JID's domain
|
|
131
|
+
def domain=(v)
|
|
132
|
+
@domain = v.to_s
|
|
133
|
+
if USE_STRINGPREP
|
|
134
|
+
@domain = IDN::Stringprep.nodeprep(@domain)
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
# Get the JID's resource
|
|
139
|
+
def resource
|
|
140
|
+
@resource
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# Set the JID's resource
|
|
144
|
+
def resource=(v)
|
|
145
|
+
@resource = v.to_s
|
|
146
|
+
if USE_STRINGPREP
|
|
147
|
+
@resource = IDN::Stringprep.nodeprep(@resource)
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
# Escape JID
|
|
152
|
+
def JID::escape(jid)
|
|
153
|
+
return jid.to_s.gsub('@', '%')
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
# Test if jid is empty
|
|
157
|
+
def empty?
|
|
158
|
+
to_s.empty?
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
# Test id jid is strepped
|
|
162
|
+
def stripped?
|
|
163
|
+
@resource.nil?
|
|
164
|
+
end
|
|
165
|
+
alias_method :bared?, :stripped?
|
|
166
|
+
end
|
|
167
|
+
end
|
|
@@ -0,0 +1,171 @@
|
|
|
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/xmlstanza'
|
|
6
|
+
require 'xmpp4r/x'
|
|
7
|
+
|
|
8
|
+
module Jabber
|
|
9
|
+
##
|
|
10
|
+
# The Message class manages the <message/> stanzas,
|
|
11
|
+
# which is used for all messaging communication.
|
|
12
|
+
class Message < XMLStanza
|
|
13
|
+
|
|
14
|
+
##
|
|
15
|
+
# Create a new message
|
|
16
|
+
# >to:: a JID or a String object to send the message to.
|
|
17
|
+
# >body:: the message's body
|
|
18
|
+
def initialize(to = nil, body = nil)
|
|
19
|
+
super("message")
|
|
20
|
+
if not to.nil?
|
|
21
|
+
set_to(to)
|
|
22
|
+
end
|
|
23
|
+
if !body.nil?
|
|
24
|
+
add_element(REXML::Element::new("body").add_text(body))
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
##
|
|
29
|
+
# Add a sub-element
|
|
30
|
+
#
|
|
31
|
+
# Will be converted to [X] if named "x"
|
|
32
|
+
# element:: [REXML::Element] to add
|
|
33
|
+
def typed_add(element)
|
|
34
|
+
if element.kind_of?(REXML::Element) && (element.name == 'x')
|
|
35
|
+
super(X::import(element))
|
|
36
|
+
else
|
|
37
|
+
super(element)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
##
|
|
42
|
+
# Get the type of the Message stanza
|
|
43
|
+
#
|
|
44
|
+
# The following Symbols are allowed:
|
|
45
|
+
# * :chat
|
|
46
|
+
# * :error
|
|
47
|
+
# * :groupchat
|
|
48
|
+
# * :headline
|
|
49
|
+
# * :normal
|
|
50
|
+
# result:: [Symbol] or nil
|
|
51
|
+
def type
|
|
52
|
+
case super
|
|
53
|
+
when 'chat' then :chat
|
|
54
|
+
when 'error' then :error
|
|
55
|
+
when 'groupchat' then :groupchat
|
|
56
|
+
when 'headline' then :headline
|
|
57
|
+
when 'normal' then :normal
|
|
58
|
+
else nil
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
##
|
|
63
|
+
# Set the type of the Message stanza (see Message#type for details)
|
|
64
|
+
# v:: [Symbol] or nil
|
|
65
|
+
def type=(v)
|
|
66
|
+
case v
|
|
67
|
+
when :chat then super('chat')
|
|
68
|
+
when :error then super('error')
|
|
69
|
+
when :groupchat then super('groupchat')
|
|
70
|
+
when :headline then super('headline')
|
|
71
|
+
when :normal then super('normal')
|
|
72
|
+
else super(nil)
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
##
|
|
77
|
+
# Set the type of the Message stanza (chaining-friendly)
|
|
78
|
+
# v:: [Symbol] or nil
|
|
79
|
+
def set_type(v)
|
|
80
|
+
self.type = v
|
|
81
|
+
self
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
##
|
|
85
|
+
# Get the first <x/> element of this stanza, or nil if none found.
|
|
86
|
+
def x
|
|
87
|
+
first_element('x')
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
##
|
|
91
|
+
# Returns the message's body, or nil.
|
|
92
|
+
# This is the message's plain-text content.
|
|
93
|
+
def body
|
|
94
|
+
first_element_text('body')
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
##
|
|
98
|
+
# Create a new message from a stanza,
|
|
99
|
+
# by copying all attributes and children from it.
|
|
100
|
+
# xmlstanza:: [REXML::Element] Source
|
|
101
|
+
# return:: [Message] Result
|
|
102
|
+
def Message.import(xmlstanza)
|
|
103
|
+
Message::new.import(xmlstanza)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
##
|
|
107
|
+
# Sets the message's body
|
|
108
|
+
#
|
|
109
|
+
# b:: [String] body to set
|
|
110
|
+
def body=(b)
|
|
111
|
+
replace_element_text('body', b)
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
##
|
|
115
|
+
# Sets the message's body
|
|
116
|
+
#
|
|
117
|
+
# b:: [String] body to set
|
|
118
|
+
# return:: [REXML::Element] self for chaining
|
|
119
|
+
def set_body(b)
|
|
120
|
+
self.body = b
|
|
121
|
+
self
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
##
|
|
125
|
+
# sets the message's subject
|
|
126
|
+
#
|
|
127
|
+
# s:: [String] subject to set
|
|
128
|
+
def subject=(s)
|
|
129
|
+
replace_element_text('subject', s)
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
##
|
|
133
|
+
# sets the message's subject
|
|
134
|
+
#
|
|
135
|
+
# s:: [String] subject to set
|
|
136
|
+
# return:: [REXML::Element] self for chaining
|
|
137
|
+
def set_subject(s)
|
|
138
|
+
self.subject = s
|
|
139
|
+
self
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
##
|
|
143
|
+
# Returns the message's subject, or nil
|
|
144
|
+
def subject
|
|
145
|
+
first_element_text('subject')
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
##
|
|
149
|
+
# sets the message's thread
|
|
150
|
+
# s:: [String] thread to set
|
|
151
|
+
def thread=(s)
|
|
152
|
+
delete_elements('thread')
|
|
153
|
+
replace_element_text('thread', s) unless s.nil?
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
##
|
|
157
|
+
# gets the message's thread (chaining-friendly)
|
|
158
|
+
# Please note that this are not [Thread] but a [String]-Identifier to track conversations
|
|
159
|
+
# s:: [String] thread to set
|
|
160
|
+
def set_thread(s)
|
|
161
|
+
self.thread = s
|
|
162
|
+
self
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
##
|
|
166
|
+
# Returns the message's thread, or nil
|
|
167
|
+
def thread
|
|
168
|
+
first_element_text('thread')
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
end
|
|
@@ -0,0 +1,107 @@
|
|
|
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/discovery'
|
|
6
|
+
|
|
7
|
+
module Jabber
|
|
8
|
+
module MUC
|
|
9
|
+
##
|
|
10
|
+
# The MUCBrowser helper can be used to discover
|
|
11
|
+
# Multi-User-Chat components via Service Discovery
|
|
12
|
+
#
|
|
13
|
+
# See JEP 0045 sections 6.1. and 6.2.
|
|
14
|
+
#
|
|
15
|
+
# Usage of its functions should be threaded as
|
|
16
|
+
# responses can take a while
|
|
17
|
+
class MUCBrowser
|
|
18
|
+
##
|
|
19
|
+
# Initialize a new MUCBrowser helper
|
|
20
|
+
def initialize(stream)
|
|
21
|
+
@stream = stream
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
##
|
|
25
|
+
# Retrieve the name of a MUC component,
|
|
26
|
+
# depending upon whether the target entity supports
|
|
27
|
+
# the MUC protocol.
|
|
28
|
+
#
|
|
29
|
+
# A return-value of nil does *not* mean that the entity
|
|
30
|
+
# does not exist or does not support Service Discovery!
|
|
31
|
+
# nil just means that this is not a MUC-compliant service.
|
|
32
|
+
#
|
|
33
|
+
# Throws an ErrorException when receiving
|
|
34
|
+
# <tt><iq type='error'/></tt>
|
|
35
|
+
# jid:: [JID] Target entity (set only domain!)
|
|
36
|
+
# return:: [String] or [nil]
|
|
37
|
+
def muc_name(jid)
|
|
38
|
+
iq = Iq.new(:get, jid)
|
|
39
|
+
iq.from = @stream.jid # Enable components to use this
|
|
40
|
+
iq.add(Discovery::IqQueryDiscoInfo.new)
|
|
41
|
+
|
|
42
|
+
res = nil
|
|
43
|
+
|
|
44
|
+
@stream.send_with_id(iq) do |answer|
|
|
45
|
+
if answer.type == :result
|
|
46
|
+
answer.query.each_element('feature') { |feature|
|
|
47
|
+
# Look if the component has a MUC or Groupchat feature
|
|
48
|
+
if feature.var == 'http://jabber.org/protocol/muc' or feature.var == 'gc-1.0'
|
|
49
|
+
# If so, get the identity
|
|
50
|
+
if answer.query.first_element('identity')
|
|
51
|
+
res = answer.query.first_element('identity').iname
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
}
|
|
55
|
+
true
|
|
56
|
+
else
|
|
57
|
+
false
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
res
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
##
|
|
65
|
+
# Retrieve the existing rooms of a MUC component
|
|
66
|
+
#
|
|
67
|
+
# The resulting Hash contains pairs of room JID and room name
|
|
68
|
+
#
|
|
69
|
+
# Usage:
|
|
70
|
+
# my_mucbrowse_helper.muc_rooms('conference.jabber.org').each { |jid,name| ... }
|
|
71
|
+
#
|
|
72
|
+
# Throws an exception when receiving <tt><iq type='error'/></tt>
|
|
73
|
+
# jid:: [JID] Target entity (set only domain!)
|
|
74
|
+
# return:: [Hash]
|
|
75
|
+
def muc_rooms(jid)
|
|
76
|
+
iq = Iq.new(:get, jid)
|
|
77
|
+
iq.from = @stream.jid # Enable components to use this
|
|
78
|
+
iq.add(Discovery::IqQueryDiscoItems.new)
|
|
79
|
+
|
|
80
|
+
rooms = {}
|
|
81
|
+
err = nil
|
|
82
|
+
|
|
83
|
+
@stream.send_with_id(iq) do |answer|
|
|
84
|
+
|
|
85
|
+
if answer.type == :result
|
|
86
|
+
answer.query.each_element('item') { |item|
|
|
87
|
+
rooms[item.jid] = item.iname
|
|
88
|
+
}
|
|
89
|
+
true
|
|
90
|
+
elsif answer.type == :error
|
|
91
|
+
err = answer.error
|
|
92
|
+
true
|
|
93
|
+
else
|
|
94
|
+
false
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
if err
|
|
99
|
+
raise "Error getting MUC rooms: #{err.error}, #{err.text}"
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
rooms
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|