shingara-blather 0.4.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (100) hide show
  1. data/LICENSE +22 -0
  2. data/README.md +162 -0
  3. data/examples/echo.rb +18 -0
  4. data/examples/execute.rb +16 -0
  5. data/examples/ping_pong.rb +37 -0
  6. data/examples/print_hierarchy.rb +76 -0
  7. data/examples/rosterprint.rb +14 -0
  8. data/examples/stream_only.rb +27 -0
  9. data/examples/xmpp4r/echo.rb +35 -0
  10. data/lib/blather/client/client.rb +310 -0
  11. data/lib/blather/client/dsl/pubsub.rb +170 -0
  12. data/lib/blather/client/dsl.rb +264 -0
  13. data/lib/blather/client.rb +87 -0
  14. data/lib/blather/core_ext/nokogiri.rb +40 -0
  15. data/lib/blather/errors/sasl_error.rb +43 -0
  16. data/lib/blather/errors/stanza_error.rb +107 -0
  17. data/lib/blather/errors/stream_error.rb +82 -0
  18. data/lib/blather/errors.rb +69 -0
  19. data/lib/blather/jid.rb +142 -0
  20. data/lib/blather/roster.rb +111 -0
  21. data/lib/blather/roster_item.rb +122 -0
  22. data/lib/blather/stanza/disco/disco_info.rb +176 -0
  23. data/lib/blather/stanza/disco/disco_items.rb +132 -0
  24. data/lib/blather/stanza/disco.rb +25 -0
  25. data/lib/blather/stanza/iq/query.rb +53 -0
  26. data/lib/blather/stanza/iq/roster.rb +179 -0
  27. data/lib/blather/stanza/iq.rb +138 -0
  28. data/lib/blather/stanza/message.rb +332 -0
  29. data/lib/blather/stanza/presence/status.rb +212 -0
  30. data/lib/blather/stanza/presence/subscription.rb +101 -0
  31. data/lib/blather/stanza/presence.rb +163 -0
  32. data/lib/blather/stanza/pubsub/affiliations.rb +79 -0
  33. data/lib/blather/stanza/pubsub/create.rb +65 -0
  34. data/lib/blather/stanza/pubsub/errors.rb +18 -0
  35. data/lib/blather/stanza/pubsub/event.rb +123 -0
  36. data/lib/blather/stanza/pubsub/items.rb +103 -0
  37. data/lib/blather/stanza/pubsub/publish.rb +103 -0
  38. data/lib/blather/stanza/pubsub/retract.rb +92 -0
  39. data/lib/blather/stanza/pubsub/subscribe.rb +68 -0
  40. data/lib/blather/stanza/pubsub/subscription.rb +134 -0
  41. data/lib/blather/stanza/pubsub/subscriptions.rb +81 -0
  42. data/lib/blather/stanza/pubsub/unsubscribe.rb +68 -0
  43. data/lib/blather/stanza/pubsub.rb +129 -0
  44. data/lib/blather/stanza/pubsub_owner/delete.rb +52 -0
  45. data/lib/blather/stanza/pubsub_owner/purge.rb +52 -0
  46. data/lib/blather/stanza/pubsub_owner.rb +51 -0
  47. data/lib/blather/stanza.rb +149 -0
  48. data/lib/blather/stream/client.rb +31 -0
  49. data/lib/blather/stream/component.rb +38 -0
  50. data/lib/blather/stream/features/resource.rb +63 -0
  51. data/lib/blather/stream/features/sasl.rb +187 -0
  52. data/lib/blather/stream/features/session.rb +44 -0
  53. data/lib/blather/stream/features/tls.rb +28 -0
  54. data/lib/blather/stream/features.rb +53 -0
  55. data/lib/blather/stream/parser.rb +102 -0
  56. data/lib/blather/stream.rb +231 -0
  57. data/lib/blather/xmpp_node.rb +218 -0
  58. data/lib/blather.rb +78 -0
  59. data/spec/blather/client/client_spec.rb +559 -0
  60. data/spec/blather/client/dsl/pubsub_spec.rb +462 -0
  61. data/spec/blather/client/dsl_spec.rb +143 -0
  62. data/spec/blather/core_ext/nokogiri_spec.rb +83 -0
  63. data/spec/blather/errors/sasl_error_spec.rb +33 -0
  64. data/spec/blather/errors/stanza_error_spec.rb +129 -0
  65. data/spec/blather/errors/stream_error_spec.rb +108 -0
  66. data/spec/blather/errors_spec.rb +33 -0
  67. data/spec/blather/jid_spec.rb +87 -0
  68. data/spec/blather/roster_item_spec.rb +96 -0
  69. data/spec/blather/roster_spec.rb +103 -0
  70. data/spec/blather/stanza/discos/disco_info_spec.rb +226 -0
  71. data/spec/blather/stanza/discos/disco_items_spec.rb +148 -0
  72. data/spec/blather/stanza/iq/query_spec.rb +64 -0
  73. data/spec/blather/stanza/iq/roster_spec.rb +140 -0
  74. data/spec/blather/stanza/iq_spec.rb +45 -0
  75. data/spec/blather/stanza/message_spec.rb +132 -0
  76. data/spec/blather/stanza/presence/status_spec.rb +132 -0
  77. data/spec/blather/stanza/presence/subscription_spec.rb +105 -0
  78. data/spec/blather/stanza/presence_spec.rb +66 -0
  79. data/spec/blather/stanza/pubsub/affiliations_spec.rb +57 -0
  80. data/spec/blather/stanza/pubsub/create_spec.rb +56 -0
  81. data/spec/blather/stanza/pubsub/event_spec.rb +84 -0
  82. data/spec/blather/stanza/pubsub/items_spec.rb +79 -0
  83. data/spec/blather/stanza/pubsub/publish_spec.rb +83 -0
  84. data/spec/blather/stanza/pubsub/retract_spec.rb +75 -0
  85. data/spec/blather/stanza/pubsub/subscribe_spec.rb +61 -0
  86. data/spec/blather/stanza/pubsub/subscription_spec.rb +97 -0
  87. data/spec/blather/stanza/pubsub/subscriptions_spec.rb +59 -0
  88. data/spec/blather/stanza/pubsub/unsubscribe_spec.rb +61 -0
  89. data/spec/blather/stanza/pubsub_owner/delete_spec.rb +50 -0
  90. data/spec/blather/stanza/pubsub_owner/purge_spec.rb +50 -0
  91. data/spec/blather/stanza/pubsub_owner_spec.rb +27 -0
  92. data/spec/blather/stanza/pubsub_spec.rb +67 -0
  93. data/spec/blather/stanza_spec.rb +116 -0
  94. data/spec/blather/stream/client_spec.rb +1011 -0
  95. data/spec/blather/stream/component_spec.rb +95 -0
  96. data/spec/blather/stream/parser_spec.rb +145 -0
  97. data/spec/blather/xmpp_node_spec.rb +231 -0
  98. data/spec/fixtures/pubsub.rb +311 -0
  99. data/spec/spec_helper.rb +43 -0
  100. metadata +249 -0
@@ -0,0 +1,68 @@
1
+ module Blather
2
+ class Stanza
3
+ class PubSub
4
+
5
+ # # PubSub Unsubscribe Stanza
6
+ #
7
+ # [XEP-0060 Section 6.2 - Unsubscribe from a Node](http://xmpp.org/extensions/xep-0060.html#subscriber-unsubscribe)
8
+ #
9
+ # @handler :pubsub_unsubscribe
10
+ class Unsubscribe < PubSub
11
+ register :pubsub_unsubscribe, :unsubscribe, self.registered_ns
12
+
13
+ # Create a new unsubscribe node
14
+ #
15
+ # @param [Blather::Stanza::Iq::VALID_TYPES] type the IQ stanza type
16
+ # @param [String] host the host to send the request to
17
+ # @param [String] node the node to unsubscribe from
18
+ # @param [Blather::JID, #to_s] jid the JID of the unsubscription
19
+ def self.new(type = :set, host = nil, node = nil, jid = nil)
20
+ new_node = super(type, host)
21
+ new_node.node = node
22
+ new_node.jid = jid
23
+ new_node
24
+ end
25
+
26
+ # Get the JID of the unsubscription
27
+ #
28
+ # @return [Blather::JID]
29
+ def jid
30
+ JID.new(unsubscribe[:jid])
31
+ end
32
+
33
+ # Set the JID of the unsubscription
34
+ #
35
+ # @param [Blather::JID, #to_s] jid
36
+ def jid=(jid)
37
+ unsubscribe[:jid] = jid
38
+ end
39
+
40
+ # Get the name of the node to unsubscribe from
41
+ #
42
+ # @return [String]
43
+ def node
44
+ unsubscribe[:node]
45
+ end
46
+
47
+ # Set the name of the node to unsubscribe from
48
+ #
49
+ # @param [String] node
50
+ def node=(node)
51
+ unsubscribe[:node] = node
52
+ end
53
+
54
+ # Get or create the actual unsubscribe node
55
+ #
56
+ # @return [Blather::XMPPNode]
57
+ def unsubscribe
58
+ unless unsubscribe = pubsub.find_first('ns:unsubscribe', :ns => self.class.registered_ns)
59
+ self.pubsub << (unsubscribe = XMPPNode.new('unsubscribe', self.document))
60
+ unsubscribe.namespace = self.pubsub.namespace
61
+ end
62
+ unsubscribe
63
+ end
64
+ end # Unsubscribe
65
+
66
+ end # PubSub
67
+ end # Stanza
68
+ end # Blather
@@ -0,0 +1,129 @@
1
+ module Blather
2
+ class Stanza
3
+
4
+ # # Pubsub Stanza
5
+ #
6
+ # [XEP-0060 - Publish-Subscribe](http://xmpp.org/extensions/xep-0060.html)
7
+ #
8
+ # The base class for all PubSub nodes. This provides helper methods common to
9
+ # all PubSub nodes.
10
+ #
11
+ # @handler :pubsub_node
12
+ class PubSub < Iq
13
+ register :pubsub_node, :pubsub, 'http://jabber.org/protocol/pubsub'
14
+
15
+ # @private
16
+ def self.import(node)
17
+ klass = nil
18
+ if pubsub = node.document.find_first('//ns:pubsub', :ns => self.registered_ns)
19
+ pubsub.children.detect do |e|
20
+ ns = e.namespace ? e.namespace.href : nil
21
+ klass = class_from_registration(e.element_name, ns)
22
+ end
23
+ end
24
+ (klass || self).new(node[:type]).inherit(node)
25
+ end
26
+
27
+ # Overwrites the parent constructor to ensure a pubsub node is present.
28
+ # Also allows the addition of a host attribute
29
+ #
30
+ # @param [<Blather::Stanza::Iq::VALID_TYPES>] type the IQ type
31
+ # @param [String, nil] host the host the node should be sent to
32
+ def self.new(type = nil, host = nil)
33
+ new_node = super type
34
+ new_node.to = host
35
+ new_node.pubsub
36
+ new_node
37
+ end
38
+
39
+ # Overrides the parent to ensure the current pubsub node is destroyed before
40
+ # inheritting the new content
41
+ #
42
+ # @private
43
+ def inherit(node)
44
+ remove_children :pubsub
45
+ super
46
+ end
47
+
48
+ # Get or create the pubsub node on the stanza
49
+ #
50
+ # @return [Blather::XMPPNode]
51
+ def pubsub
52
+ p = find_first('ns:pubsub', :ns => self.class.registered_ns) ||
53
+ find_first('pubsub', :ns => self.class.registered_ns)
54
+
55
+ unless p
56
+ self << (p = XMPPNode.new('pubsub', self.document))
57
+ p.namespace = self.class.registered_ns
58
+ end
59
+ p
60
+ end
61
+ end # PubSub
62
+
63
+ # # PubSubItem Fragment
64
+ #
65
+ # This fragment is found in many places throughout the pubsub spec
66
+ # This is a convenience class to attach methods to the node
67
+ class PubSubItem < XMPPNode
68
+ ATOM_NS = 'http://www.w3.org/2005/Atom'.freeze
69
+
70
+ # Create a new PubSubItem
71
+ #
72
+ # @param [String, nil] id the id of the stanza
73
+ # @param [#to_s, nil] payload the payload to attach to this item.
74
+ # @param [XML::Document, nil] document the document the node should be
75
+ # attached to. This should be the document of the parent PubSub node.
76
+ def self.new(id = nil, payload = nil, document = nil)
77
+ new_node = super 'item', document
78
+ new_node.id = id
79
+ new_node.payload = payload if payload
80
+ new_node
81
+ end
82
+
83
+ # Get the item's ID
84
+ #
85
+ # @return [String, nil]
86
+ def id
87
+ read_attr :id
88
+ end
89
+
90
+ # Set the item's ID
91
+ #
92
+ # @param [#to_s] id the new ID
93
+ def id=(id)
94
+ write_attr :id, id
95
+ end
96
+
97
+ # Get the item's payload
98
+ #
99
+ # To get the XML representation use #entry
100
+ #
101
+ # @return [String, nil]
102
+ def payload
103
+ self.entry.content.empty? ? nil : content
104
+ end
105
+
106
+ # Set the item's payload
107
+ #
108
+ # @param [String, nil] payload the payload
109
+ def payload=(payload)
110
+ self.entry.content = payload
111
+ end
112
+
113
+ # Get or create the entry node
114
+ #
115
+ # @return [Blather::XMPPNode]
116
+ def entry
117
+ e = find_first('ns:entry', :ns => ATOM_NS) ||
118
+ find_first('entry', :ns => ATOM_NS)
119
+
120
+ unless e
121
+ self << (e = XMPPNode.new('entry', self.document))
122
+ e.namespace = ATOM_NS
123
+ end
124
+ e
125
+ end
126
+ end # PubSubItem
127
+
128
+ end # Stanza
129
+ end # Blather
@@ -0,0 +1,52 @@
1
+ module Blather
2
+ class Stanza
3
+ class PubSubOwner
4
+
5
+ # # PubSubOwner Delete Stanza
6
+ #
7
+ # [XEP-0060 Section 8.4 Delete a Node](http://xmpp.org/extensions/xep-0060.html#owner-delete)
8
+ #
9
+ # @handler :pubsub_delete
10
+ class Delete < PubSubOwner
11
+ register :pubsub_delete, :delete, self.registered_ns
12
+
13
+ # Create a new delete stanza
14
+ #
15
+ # @param [Blather::Stanza::Iq::VALID_TYPES] type the IQ stanza type
16
+ # @param [String] host the host to send the request to
17
+ # @param [String] node the name of the node to delete
18
+ def self.new(type = :set, host = nil, node = nil)
19
+ new_node = super(type, host)
20
+ new_node.node = node
21
+ new_node
22
+ end
23
+
24
+ # Get the name of the node to delete
25
+ #
26
+ # @return [String]
27
+ def node
28
+ delete_node[:node]
29
+ end
30
+
31
+ # Set the name of the node to delete
32
+ #
33
+ # @param [String] node
34
+ def node=(node)
35
+ delete_node[:node] = node
36
+ end
37
+
38
+ # Get or create the actual delete node on the stanza
39
+ #
40
+ # @return [Blather::XMPPNode]
41
+ def delete_node
42
+ unless delete_node = pubsub.find_first('ns:delete', :ns => self.class.registered_ns)
43
+ self.pubsub << (delete_node = XMPPNode.new('delete', self.document))
44
+ delete_node.namespace = self.pubsub.namespace
45
+ end
46
+ delete_node
47
+ end
48
+ end # Retract
49
+
50
+ end # PubSub
51
+ end # Stanza
52
+ end # Blather
@@ -0,0 +1,52 @@
1
+ module Blather
2
+ class Stanza
3
+ class PubSubOwner
4
+
5
+ # # PubSubOwner Purge Stanza
6
+ #
7
+ # [XEP-0060 Section 8.5 - Purge All Node Items](http://xmpp.org/extensions/xep-0060.html#owner-purge)
8
+ #
9
+ # @handler :pubsub_purge
10
+ class Purge < PubSubOwner
11
+ register :pubsub_purge, :purge, self.registered_ns
12
+
13
+ # Create a new purge stanza
14
+ #
15
+ # @param [Blather::Stanza::Iq::VALID_TYPES] type the IQ stanza type
16
+ # @param [String] host the host to send the request to
17
+ # @param [String] node the name of the node to purge
18
+ def self.new(type = :set, host = nil, node = nil)
19
+ new_node = super(type, host)
20
+ new_node.node = node
21
+ new_node
22
+ end
23
+
24
+ # Get the name of the node to delete
25
+ #
26
+ # @return [String]
27
+ def node
28
+ purge_node[:node]
29
+ end
30
+
31
+ # Set the name of the node to delete
32
+ #
33
+ # @param [String] node
34
+ def node=(node)
35
+ purge_node[:node] = node
36
+ end
37
+
38
+ # Get or create the actual purge node on the stanza
39
+ #
40
+ # @return [Blather::XMPPNode]
41
+ def purge_node
42
+ unless purge_node = pubsub.find_first('ns:purge', :ns => self.class.registered_ns)
43
+ self.pubsub << (purge_node = XMPPNode.new('purge', self.document))
44
+ purge_node.namespace = self.pubsub.namespace
45
+ end
46
+ purge_node
47
+ end
48
+ end # Retract
49
+
50
+ end # PubSub
51
+ end # Stanza
52
+ end # Blather
@@ -0,0 +1,51 @@
1
+ module Blather
2
+ class Stanza
3
+
4
+ # # PubSubOwner Base Class
5
+ #
6
+ # [XEP-0060 - Publish-Subscribe](http://xmpp.org/extensions/xep-0060.html)
7
+ #
8
+ # @handler :pubsub_owner
9
+ class PubSubOwner < Iq
10
+ register :pubsub_owner, :pubsub, 'http://jabber.org/protocol/pubsub#owner'
11
+
12
+ # Creates the proper class from the stana's child
13
+ # @private
14
+ def self.import(node)
15
+ klass = nil
16
+ if pubsub = node.document.find_first('//ns:pubsub', :ns => self.registered_ns)
17
+ pubsub.children.each { |e| break if klass = class_from_registration(e.element_name, (e.namespace.href if e.namespace)) }
18
+ end
19
+ (klass || self).new(node[:type]).inherit(node)
20
+ end
21
+
22
+ # Overrides the parent to ensure a pubsub node is created
23
+ # @private
24
+ def self.new(type = nil, host = nil)
25
+ new_node = super type
26
+ new_node.to = host
27
+ new_node.pubsub
28
+ new_node
29
+ end
30
+
31
+ # Overrides the parent to ensure the pubsub node is destroyed
32
+ # @private
33
+ def inherit(node)
34
+ remove_children :pubsub
35
+ super
36
+ end
37
+
38
+ # Get or create the pubsub node on the stanza
39
+ #
40
+ # @return [Blather::XMPPNode]
41
+ def pubsub
42
+ unless p = find_first('ns:pubsub', :ns => self.class.registered_ns)
43
+ self << (p = XMPPNode.new('pubsub', self.document))
44
+ p.namespace = self.class.registered_ns
45
+ end
46
+ p
47
+ end
48
+ end # PubSubOwner
49
+
50
+ end # Stanza
51
+ end # Blather
@@ -0,0 +1,149 @@
1
+ module Blather
2
+
3
+ # # Base XMPP Stanza
4
+ #
5
+ # All stanzas inherit this class. It provides a set of methods and helpers
6
+ # common to all XMPP Stanzas
7
+ #
8
+ # @handler :stanza
9
+ class Stanza < XMPPNode
10
+ # @private
11
+ @@last_id = 0
12
+ # @private
13
+ @@handler_list = []
14
+
15
+ class_inheritable_array :handler_hierarchy
16
+
17
+ # Registers a callback onto the callback stack
18
+ #
19
+ # @param [Symbol] handler the name of the handler
20
+ # @param [Symbol, String, nil] name the name of the first element in the
21
+ # stanza. If nil the inherited name will be used. If that's nil the
22
+ # handler name will be used.
23
+ # @param [String, nil] ns the namespace of the stanza
24
+ def self.register(handler, name = nil, ns = nil)
25
+ @@handler_list << handler
26
+ self.handler_hierarchy ||= [:stanza]
27
+ self.handler_hierarchy.unshift handler
28
+
29
+ name = name || self.registered_name || handler
30
+ super name, ns
31
+ end
32
+
33
+ # The handler stack for the current stanza class
34
+ #
35
+ # @return [Array<Symbol>]
36
+ def self.handler_list
37
+ @@handler_list
38
+ end
39
+
40
+ # Helper method that creates a unique ID for stanzas
41
+ #
42
+ # @return [String] a new unique ID
43
+ def self.next_id
44
+ @@last_id += 1
45
+ 'blather%04x' % @@last_id
46
+ end
47
+
48
+ # Check if the stanza is an error stanza
49
+ #
50
+ # @return [true, false]
51
+ def error?
52
+ self.type == :error
53
+ end
54
+
55
+ # Creates a copy with to and from swapped
56
+ #
57
+ # @return [Blather::Stanza]
58
+ def reply
59
+ self.dup.reply!
60
+ end
61
+
62
+ # Swaps from and to
63
+ #
64
+ # @return [self]
65
+ def reply!
66
+ self.to, self.from = self.from, self.to
67
+ self
68
+ end
69
+
70
+ # Get the stanza's ID
71
+ #
72
+ # @return [String, nil]
73
+ def id
74
+ read_attr :id
75
+ end
76
+
77
+ # Set the stanza's ID
78
+ #
79
+ # @param [#to_s] id the new stanza ID
80
+ def id=(id)
81
+ write_attr :id, id
82
+ end
83
+
84
+ # Get the stanza's to
85
+ #
86
+ # @return [Blather::JID, nil]
87
+ def to
88
+ JID.new(self[:to]) if self[:to]
89
+ end
90
+
91
+ # Set the stanza's to field
92
+ #
93
+ # @param [#to_s] to the new JID for the to field
94
+ def to=(to)
95
+ write_attr :to, to
96
+ end
97
+
98
+ # Get the stanza's from
99
+ #
100
+ # @return [Blather::JID, nil]
101
+ def from
102
+ JID.new(self[:from]) if self[:from]
103
+ end
104
+
105
+ # Set the stanza's from field
106
+ #
107
+ # @param [#to_s] from the new JID for the from field
108
+ def from=(from)
109
+ write_attr :from, from
110
+ end
111
+
112
+ # Get the stanza's type
113
+ #
114
+ # @return [Symbol, nil]
115
+ def type
116
+ read_attr :type, :to_sym
117
+ end
118
+
119
+ # Set the stanza's type
120
+ #
121
+ # @param [#to_s] type the new stanza type
122
+ def type=(type)
123
+ write_attr :type, type
124
+ end
125
+
126
+ # Create an error stanza from the current stanza
127
+ #
128
+ # @param [String] name the error name
129
+ # @param [<Blather::StanzaError::VALID_TYPES>] type the error type
130
+ # @param [String, nil] text the error text
131
+ # @param [Array<XML::Node>] extras an array of extra nodes to attach to
132
+ # the error
133
+ #
134
+ # @return [Blather::StanzaError]
135
+ def as_error(name, type, text = nil, extras = [])
136
+ StanzaError.new self, name, type, text, extras
137
+ end
138
+
139
+ protected
140
+ # @private
141
+ def reply_if_needed!
142
+ unless @reversed_endpoints
143
+ reply!
144
+ @reversed_endpoints = true
145
+ end
146
+ self
147
+ end
148
+ end
149
+ end
@@ -0,0 +1,31 @@
1
+ module Blather
2
+ class Stream
3
+
4
+ class Client < Stream
5
+ LANG = 'en'
6
+ VERSION = '1.0'
7
+ NAMESPACE = 'jabber:client'
8
+
9
+ def start
10
+ @parser = Parser.new self
11
+ start_stream = <<-STREAM
12
+ <stream:stream
13
+ to='#{@to}'
14
+ xmlns='#{NAMESPACE}'
15
+ xmlns:stream='#{STREAM_NS}'
16
+ version='#{VERSION}'
17
+ xml:lang='#{LANG}'
18
+ >
19
+ STREAM
20
+ send start_stream.gsub(/\s+/, ' ')
21
+ end
22
+
23
+ def send(stanza)
24
+ stanza.from = self.jid if stanza.is_a?(Stanza) && !stanza.from.nil?
25
+ super stanza
26
+ end
27
+
28
+ end #Client
29
+
30
+ end #Stream
31
+ end #Blather
@@ -0,0 +1,38 @@
1
+ module Blather
2
+ class Stream
3
+
4
+ class Component < Stream
5
+ NAMESPACE = 'jabber:component:accept'
6
+
7
+ def receive(node) # :nodoc:
8
+ if node.element_name == 'handshake'
9
+ ready!
10
+ else
11
+ super
12
+ end
13
+
14
+ if node.document.find_first('/stream:stream[not(stream:error)]', :xmlns => NAMESPACE, :stream => STREAM_NS)
15
+ send("<handshake>#{Digest::SHA1.hexdigest(@node['id']+@password)}</handshake>")
16
+ end
17
+ end
18
+
19
+ def send(stanza)
20
+ stanza.from ||= self.jid if stanza.respond_to?(:from) && stanza.respond_to?(:from=)
21
+ super stanza
22
+ end
23
+
24
+ def start
25
+ @parser = Parser.new self
26
+ start_stream = <<-STREAM
27
+ <stream:stream
28
+ to='#{@jid}'
29
+ xmlns='#{NAMESPACE}'
30
+ xmlns:stream='#{STREAM_NS}'
31
+ >
32
+ STREAM
33
+ send start_stream.gsub(/\s+/, ' ')
34
+ end
35
+ end #Client
36
+
37
+ end #Stream
38
+ end #Blather
@@ -0,0 +1,63 @@
1
+ module Blather # :nodoc:
2
+ class Stream # :nodoc:
3
+
4
+ class Resource < Features # :nodoc:
5
+ BIND_NS = 'urn:ietf:params:xml:ns:xmpp-bind'.freeze
6
+ register BIND_NS
7
+
8
+ def initialize(stream, succeed, fail)
9
+ super
10
+ @jid = stream.jid
11
+ end
12
+
13
+ def receive_data(stanza)
14
+ @node = stanza
15
+ case stanza.element_name
16
+ when 'bind' then bind
17
+ when 'iq' then result
18
+ else fail!(UnknownResponse.new(@node))
19
+ end
20
+ end
21
+
22
+ private
23
+ ##
24
+ # Respond to the bind request
25
+ # If @jid has a resource set already request it from the server
26
+ def bind
27
+ response = Stanza::Iq.new :set
28
+ @id = response.id
29
+
30
+ response << (binder = XMPPNode.new('bind', response.document))
31
+ binder.namespace = BIND_NS
32
+
33
+ if @jid.resource
34
+ binder << (resource = XMPPNode.new('resource', binder.document))
35
+ resource.content = @jid.resource
36
+ end
37
+
38
+ @stream.send response
39
+ end
40
+
41
+ ##
42
+ # Process the result from the server
43
+ # Sets the sends the JID (now bound to a resource)
44
+ # back to the stream
45
+ def result
46
+ if @node[:type] == 'error'
47
+ fail! StanzaError.import(@node)
48
+ return
49
+ end
50
+
51
+ Blather.logger.debug "RESOURCE NODE #{@node}"
52
+ # ensure this is a response to our original request
53
+ if @id == @node['id']
54
+ @stream.jid = JID.new @node.find_first('bind_ns:bind/bind_ns:jid', :bind_ns => BIND_NS).content
55
+ succeed!
56
+ else
57
+ fail!("BIND result ID mismatch. Expected: #{@id}. Received: #{@node['id']}")
58
+ end
59
+ end
60
+ end #Resource
61
+
62
+ end #Stream
63
+ end #Blather