xmpp4r 0.3

Sign up to get free protection for your applications and to get access to all the features.
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,244 @@
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/query'
6
+
7
+ module Jabber
8
+ module Roster
9
+ ##
10
+ # Class for handling roster updates
11
+ #
12
+ # You must do 'client.send(Iq.new_rosterget)' or else you will
13
+ # have nothing to put in receive_iq()
14
+ #
15
+ # You must require 'xmpp4r/rosterquery' to use this class
16
+ # as its functionality is not needed for a working XMPP implementation.
17
+ # This will make [IqQuery] convert all Queries with namespace 'jabber:iq:roster'
18
+ # to [IqQueryRoster]
19
+ #
20
+ # This <query/> contains multiple <item/> children. See RosterItem.
21
+ class IqQueryRoster < IqQuery
22
+ ##
23
+ # Create a new <query xmlns='jabber:iq:roster'/>
24
+ # stream:: [Stream] Stream to handle
25
+ def initialize
26
+ super
27
+ add_namespace('jabber:iq:roster')
28
+ end
29
+
30
+ ##
31
+ # Add an element to the roster
32
+ #
33
+ # Converts <item/> elements to RosterItem
34
+ #
35
+ # Previous RosterItems with the same JID will
36
+ # *not* be deleted!
37
+ def typed_add(element)
38
+ if element.kind_of?(REXML::Element) && (element.name == 'item')
39
+ item = RosterItem::new.import(element)
40
+ super(item)
41
+ else
42
+ super(element)
43
+ end
44
+ end
45
+
46
+ ##
47
+ # Iterate through all items
48
+ # &block:: Yield for every [RosterItem]
49
+ def each(&block)
50
+ each_element { |item|
51
+ # XPath won't work here as it's missing a prefix...
52
+ yield(item) if item.kind_of?(RosterItem)
53
+ }
54
+ end
55
+
56
+ ##
57
+ # Get roster item by JID
58
+ # jid:: [JID] or [Nil]
59
+ # result:: [RosterItem]
60
+ def [](jid)
61
+ each { |item|
62
+ return(item) if item.jid == jid
63
+ }
64
+ nil
65
+ end
66
+
67
+ ##
68
+ # Get all items
69
+ # result:: [Array] of [RosterItem]
70
+ def to_a
71
+ a = []
72
+ each { |item|
73
+ a.push(item)
74
+ }
75
+ a
76
+ end
77
+
78
+ ##
79
+ # Update roster by <iq/> stanza
80
+ # (to be fed by an iq_callback)
81
+ # iq:: [Iq] Containing new roster
82
+ # filter:: [Boolean] If false import non-roster-like results too
83
+ def receive_iq(iq, filter=true)
84
+ if filter && (((iq.type != :set) && (iq.type != :result)) || (iq.queryns != 'jabber:iq:roster'))
85
+ return
86
+ end
87
+
88
+ import(iq.query)
89
+ end
90
+
91
+ ##
92
+ # Output for "p"
93
+ #
94
+ # JIDs of all contained [RosterItem] elements are joined with a comma
95
+ # result:: [String]
96
+ def inspect
97
+ jids = to_a.collect { |item| item.jid.inspect }
98
+ jids.join(', ')
99
+ end
100
+ end
101
+
102
+ ##
103
+ # Class containing the <item/> elements of the roster
104
+ #
105
+ # The 'name' attribute has been renamed to 'iname' here
106
+ # as 'name' is already used by REXML::Element for the
107
+ # element's name. It's still name='...' in XML.
108
+ class RosterItem < REXML::Element
109
+ ##
110
+ # Construct a new roster item
111
+ # jid:: [JID] Jabber ID
112
+ # iname:: [String] Name in the roster
113
+ # subscription:: [Symbol] Type of subscription (see RosterItem#subscription=)
114
+ # ask:: [Symbol] or [Nil] Can be :subscribe
115
+ def initialize(jid=nil, iname=nil, subscription=nil, ask=nil)
116
+ super('item')
117
+ self.jid = jid
118
+ self.iname = iname
119
+ self.subscription = subscription
120
+ self.ask = ask
121
+ end
122
+
123
+ ##
124
+ # Create new RosterItem from REXML::Element
125
+ # item:: [REXML::Element] source element to copy attributes and children from
126
+ def RosterItem.import(item)
127
+ RosterItem::new.import(item)
128
+ end
129
+
130
+ ##
131
+ # Get name of roster item
132
+ #
133
+ # names can be set by the roster's owner himself
134
+ # return:: [String]
135
+ def iname
136
+ attributes['name']
137
+ end
138
+
139
+ ##
140
+ # Set name of roster item
141
+ # val:: [String] Name for this item
142
+ def iname=(val)
143
+ attributes['name'] = val
144
+ end
145
+
146
+ ##
147
+ # Get JID of roster item
148
+ # Resource of the JID will _not_ be stripped
149
+ # return:: [JID]
150
+ def jid
151
+ (a = attributes['jid']) ? JID::new(a) : nil
152
+ end
153
+
154
+ ##
155
+ # Set JID of roster item
156
+ # val:: [JID] or nil
157
+ def jid=(val)
158
+ attributes['jid'] = val.nil? ? nil : val.to_s
159
+ end
160
+
161
+ ##
162
+ # Get subscription type of roster item
163
+ # result:: [Symbol] or [Nil] The following values are valid according to RFC3921:
164
+ # * :both
165
+ # * :from
166
+ # * :none
167
+ # * :remove
168
+ # * :to
169
+ def subscription
170
+ case attributes['subscription']
171
+ when 'both' then :both
172
+ when 'from' then :from
173
+ when 'none' then :none
174
+ when 'remove' then :remove
175
+ when 'to' then :to
176
+ else nil
177
+ end
178
+ end
179
+
180
+ ##
181
+ # Set subscription type of roster item
182
+ # val:: [Symbol] or [Nil] See subscription for possible Symbols
183
+ def subscription=(val)
184
+ case val
185
+ when :both then attributes['subscription'] = 'both'
186
+ when :from then attributes['subscription'] = 'from'
187
+ when :none then attributes['subscription'] = 'none'
188
+ when :remove then attributes['subscription'] = 'remove'
189
+ when :to then attributes['subscription'] = 'to'
190
+ else attributes['subscription'] = nil
191
+ end
192
+ end
193
+
194
+ ##
195
+ # Get if asking for subscription
196
+ # result:: [Symbol] nil or :subscribe
197
+ def ask
198
+ case attributes['ask']
199
+ when 'subscribe' then :subscribe
200
+ else nil
201
+ end
202
+ end
203
+
204
+ ##
205
+ # Set if asking for subscription
206
+ # val:: [Symbol] nil or :subscribe
207
+ def ask=(val)
208
+ case val
209
+ when :subscribe then attributes['ask'] = 'subscribe'
210
+ else attributes['ask'] = nil
211
+ end
212
+ end
213
+
214
+ ##
215
+ # Get groups the item belongs to
216
+ # result:: [Array] of [String] The groups
217
+ def groups
218
+ result = []
219
+ each_element('group') { |group|
220
+ result.push(group.text)
221
+ }
222
+ result
223
+ end
224
+
225
+ ##
226
+ # Set groups the item belongs to,
227
+ # deletes old groups first.
228
+ #
229
+ # See JEP 0083 for nested groups
230
+ # ary:: [Array] New groups, duplicate values will be removed
231
+ def groups=(ary)
232
+ # Delete old group elements
233
+ delete_elements('group')
234
+
235
+ # Add new group elements
236
+ ary.uniq.each { |group|
237
+ add_element('group').text = group
238
+ }
239
+ end
240
+ end
241
+
242
+ IqQuery.add_namespaceclass('jabber:iq:roster', IqQueryRoster)
243
+ end #Module Roster
244
+ end #Module Jabber
@@ -0,0 +1,155 @@
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/x'
6
+ require 'xmpp4r/jid'
7
+
8
+ module Jabber
9
+ module Roster
10
+ ##
11
+ # Implementation of JEP-0144
12
+ # for <tt><x xmlns='http://jabber.org/protocol/rosterx'/></tt>
13
+ # attached to <tt><message/></tt> stanzas
14
+ #
15
+ # Should be backwards compatible to JEP-0093,
16
+ # as only action attribute of roster items are missing there.
17
+ # Pay attention to the namespace which is <tt>jabber:x:roster</tt>
18
+ # for JEP-0093!
19
+ class XRoster < X
20
+ ##
21
+ # Initialize a new XRoster element
22
+ def initialize
23
+ super()
24
+ add_namespace('http://jabber.org/protocol/rosterx')
25
+ end
26
+
27
+ ##
28
+ # Add an element to the roster attachment
29
+ #
30
+ # Converts <item/> elements to XRosterItem
31
+ def typed_add(element)
32
+ if element.kind_of?(REXML::Element) && (element.name == 'item')
33
+ super(XRosterItem::new.import(element))
34
+ else
35
+ super(element)
36
+ end
37
+ end
38
+ end #Class XRoster
39
+
40
+ X.add_namespaceclass('jabber:x:roster', XRoster)
41
+ X.add_namespaceclass('http://jabber.org/protocol/rosterx', XRoster)
42
+
43
+ ##
44
+ # Class containing an <item/> element
45
+ #
46
+ # The 'name' attribute has been renamed to 'iname' here
47
+ # as 'name' is already used by REXML::Element for the
48
+ # element's name. It's still name='...' in XML.
49
+ #
50
+ # This is all a bit analoguous to Jabber::RosterItem, used by
51
+ # Jabber::IqQueryRoster. But this class lacks the subscription and
52
+ # ask attributes.
53
+ class XRosterItem < REXML::Element
54
+ ##
55
+ # Construct a new roster item
56
+ # jid:: [JID] Jabber ID
57
+ # iname:: [String] Name in the roster
58
+ def initialize(jid=nil, iname=nil)
59
+ super('item')
60
+ self.jid = jid
61
+ self.iname = iname
62
+ end
63
+
64
+ ##
65
+ # Create new XRosterItem from REXML::Element
66
+ # item:: [REXML::Element] source element to copy attributes and children from
67
+ def XRosterItem.import(item)
68
+ XRosterItem::new.import(item)
69
+ end
70
+
71
+ ##
72
+ # Get name of roster item
73
+ #
74
+ # names can be set by the roster's owner himself
75
+ # return:: [String]
76
+ def iname
77
+ attributes['name']
78
+ end
79
+
80
+ ##
81
+ # Set name of roster item
82
+ # val:: [String] Name for this item
83
+ def iname=(val)
84
+ attributes['name'] = val
85
+ end
86
+
87
+ ##
88
+ # Get JID of roster item
89
+ # Resource of the JID will _not_ be stripped
90
+ # return:: [JID]
91
+ def jid
92
+ JID::new(attributes['jid'])
93
+ end
94
+
95
+ ##
96
+ # Set JID of roster item
97
+ # val:: [JID] or nil
98
+ def jid=(val)
99
+ attributes['jid'] = val.nil? ? nil : val.to_s
100
+ end
101
+
102
+ ##
103
+ # Get action for this roster item
104
+ # * :add
105
+ # * :modify
106
+ # * :delete
107
+ # result:: [Symbol] (defaults to :add according to JEP-0144)
108
+ def action
109
+ case attributes['action']
110
+ when 'modify' then :modify
111
+ when 'delete' then :delete
112
+ else :add
113
+ end
114
+ end
115
+
116
+ ##
117
+ # Set action for this roster item
118
+ # (see action)
119
+ def action=(a)
120
+ case a
121
+ when :modify then attributes['action'] = 'modify'
122
+ when :delete then attributes['action'] = 'delete'
123
+ else attributes['action'] = 'add'
124
+ end
125
+ end
126
+
127
+ ##
128
+ # Get groups the item belongs to
129
+ # result:: [Array] of [String] The groups
130
+ def groups
131
+ result = []
132
+ each_element('group') { |group|
133
+ result.push(group.text)
134
+ }
135
+ result
136
+ end
137
+
138
+ ##
139
+ # Set groups the item belongs to,
140
+ # deletes old groups first.
141
+ #
142
+ # See JEP 0083 for nested groups
143
+ # ary:: [Array] New groups, duplicate values will be removed
144
+ def groups=(ary)
145
+ # Delete old group elements
146
+ delete_elements('group')
147
+
148
+ # Add new group elements
149
+ ary.uniq.each { |group|
150
+ add_element('group').text = group
151
+ }
152
+ end
153
+ end #Class XRosterItem
154
+ end #Module Roster
155
+ end #Module Jabber
@@ -0,0 +1,4 @@
1
+ require 'xmpp4r/roster/iq/roster.rb'
2
+ require 'xmpp4r/roster/helper/roster.rb'
3
+ require 'xmpp4r/roster/x/roster.rb'
4
+
@@ -0,0 +1,167 @@
1
+ require 'base64'
2
+ require 'digest/md5'
3
+
4
+ module Jabber
5
+ ##
6
+ # Helpers for SASL authentication (RFC2222)
7
+ #
8
+ # You might not need to use them directly, they are
9
+ # invoked by Jabber::Client#auth
10
+ module SASL
11
+ NS_SASL = 'urn:ietf:params:xml:ns:xmpp-sasl'
12
+
13
+ ##
14
+ # Factory function to obtain a SASL helper for the specified mechanism
15
+ def SASL::new(stream, mechanism)
16
+ case mechanism
17
+ when 'DIGEST-MD5'
18
+ DigestMD5.new(stream)
19
+ when 'PLAIN'
20
+ Plain.new(stream)
21
+ else
22
+ raise "Unknown SASL mechanism: #{mechanism}"
23
+ end
24
+ end
25
+
26
+ ##
27
+ # SASL mechanism base class (stub)
28
+ class Base
29
+ def initialize(stream)
30
+ @stream = stream
31
+ end
32
+
33
+ private
34
+
35
+ def generate_auth(mechanism, text=nil)
36
+ auth = REXML::Element.new 'auth'
37
+ auth.add_namespace NS_SASL
38
+ auth.attributes['mechanism'] = mechanism
39
+ auth.text = text
40
+ auth
41
+ end
42
+
43
+ def generate_nonce
44
+ Digest::MD5.new(Time.new.to_f.to_s).hexdigest
45
+ end
46
+ end
47
+
48
+ ##
49
+ # SASL PLAIN authentication helper (RFC2595)
50
+ class Plain < Base
51
+ ##
52
+ # Authenticate via sending password in clear-text
53
+ def auth(password)
54
+ auth_text = "#{@stream.jid.strip}\x00#{@stream.jid.node}\x00#{password}"
55
+ error = nil
56
+ @stream.send(generate_auth('PLAIN', Base64::encode64(auth_text).strip)) { |reply|
57
+ if reply.name != 'success'
58
+ error = reply.first_element(nil).name
59
+ end
60
+ true
61
+ }
62
+
63
+ raise error if error
64
+ end
65
+ end
66
+
67
+ ##
68
+ # SASL DIGEST-MD5 authentication helper (RFC2831)
69
+ class DigestMD5 < Base
70
+ ##
71
+ # Sends the wished auth mechanism and wait for a challenge
72
+ #
73
+ # (proceed with DigestMD5#auth)
74
+ def initialize(stream)
75
+ super
76
+
77
+ challenge = {}
78
+ error = nil
79
+ @stream.send(generate_auth('DIGEST-MD5')) { |reply|
80
+ if reply.name == 'challenge' and reply.namespace == NS_SASL
81
+ challenge_text = Base64::decode64(reply.text)
82
+ challenge_text.split(/,/).each { |s|
83
+ key, value = s.split(/=/, 2)
84
+ value.sub!(/^"/, '')
85
+ value.sub!(/"$/, '')
86
+ challenge[key] = value
87
+ }
88
+ else
89
+ error = reply.first_element(nil).name
90
+ end
91
+ true
92
+ }
93
+ raise error if error
94
+
95
+ @nonce = challenge['nonce']
96
+ @realm = challenge['realm']
97
+ end
98
+
99
+ ##
100
+ # * Send a response
101
+ # * Wait for the server's challenge (which aren't checked)
102
+ # * Send a blind response to the server's challenge
103
+ def auth(password)
104
+ response = {}
105
+ response['nonce'] = @nonce
106
+ response['charset'] = 'utf-8'
107
+ response['username'] = @stream.jid.node
108
+ response['realm'] = @realm || @stream.jid.domain
109
+ response['cnonce'] = generate_nonce
110
+ response['nc'] = '00000001'
111
+ response['qop'] = 'auth'
112
+ response['digest-uri'] = "xmpp/#{@stream.jid.domain}"
113
+ response['response'] = response_value(@stream.jid.node, @stream.jid.domain, response['digest-uri'], password, @nonce, response['cnonce'], response['qop'])
114
+ response.each { |key,value|
115
+ unless %w(nc qop response charset).include? key
116
+ response[key] = "\"#{value}\""
117
+ end
118
+ }
119
+
120
+ r = REXML::Element.new('response')
121
+ r.add_namespace NS_SASL
122
+ r.text = Base64::encode64(response.collect { |k,v| "#{k}=#{v}" }.join(',')).gsub(/\s/, '')
123
+ error = nil
124
+ @stream.send(r) { |reply|
125
+ if reply.name != 'challenge'
126
+ error = reply.first_element(nil).name
127
+ end
128
+ true
129
+ }
130
+
131
+ raise error if error
132
+
133
+ # TODO: check the challenge from the server
134
+
135
+ r.text = nil
136
+ @stream.send(r) { |reply|
137
+ if reply.name != 'success'
138
+ error = reply.first_element(nil).name
139
+ end
140
+ true
141
+ }
142
+
143
+ raise error if error
144
+ end
145
+
146
+ private
147
+
148
+ ##
149
+ # Function from RFC2831
150
+ def h(s); Digest::MD5.digest(s); end
151
+ ##
152
+ # Function from RFC2831
153
+ def hh(s); Digest::MD5.hexdigest(s); end
154
+
155
+ ##
156
+ # Calculate the value for the response field
157
+ def response_value(username, realm, digest_uri, passwd, nonce, cnonce, qop)
158
+ a1_h = h("#{username}:#{realm}:#{passwd}")
159
+ a1 = "#{a1_h}:#{nonce}:#{cnonce}"
160
+ #a2 = "AUTHENTICATE:#{digest_uri}#{(qop == 'auth') ? '' : ':00000000000000000000000000000000'}"
161
+ a2 = "AUTHENTICATE:#{digest_uri}"
162
+
163
+ hh("#{hh(a1)}:#{nonce}:00000001:#{cnonce}:#{qop}:#{hh(a2)}")
164
+ end
165
+ end
166
+ end
167
+ end