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,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