tp-blather 0.8.2

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 (150) hide show
  1. data/.autotest +13 -0
  2. data/.gemtest +0 -0
  3. data/.gitignore +19 -0
  4. data/.rspec +3 -0
  5. data/.travis.yml +8 -0
  6. data/CHANGELOG.md +249 -0
  7. data/Gemfile +4 -0
  8. data/Guardfile +5 -0
  9. data/LICENSE +22 -0
  10. data/README.md +413 -0
  11. data/Rakefile +20 -0
  12. data/TODO.md +2 -0
  13. data/blather.gemspec +51 -0
  14. data/examples/certs/README +20 -0
  15. data/examples/certs/ca-bundle.crt +3987 -0
  16. data/examples/echo.rb +19 -0
  17. data/examples/execute.rb +17 -0
  18. data/examples/ping_pong.rb +38 -0
  19. data/examples/print_hierarchy.rb +77 -0
  20. data/examples/rosterprint.rb +15 -0
  21. data/examples/stream_only.rb +28 -0
  22. data/examples/trusted_echo.rb +21 -0
  23. data/examples/xmpp4r/echo.rb +36 -0
  24. data/lib/blather.rb +112 -0
  25. data/lib/blather/cert_store.rb +53 -0
  26. data/lib/blather/client.rb +95 -0
  27. data/lib/blather/client/client.rb +345 -0
  28. data/lib/blather/client/dsl.rb +320 -0
  29. data/lib/blather/client/dsl/pubsub.rb +174 -0
  30. data/lib/blather/core_ext/eventmachine.rb +125 -0
  31. data/lib/blather/core_ext/ipaddr.rb +20 -0
  32. data/lib/blather/errors.rb +69 -0
  33. data/lib/blather/errors/sasl_error.rb +44 -0
  34. data/lib/blather/errors/stanza_error.rb +110 -0
  35. data/lib/blather/errors/stream_error.rb +84 -0
  36. data/lib/blather/file_transfer.rb +107 -0
  37. data/lib/blather/file_transfer/ibb.rb +68 -0
  38. data/lib/blather/file_transfer/s5b.rb +114 -0
  39. data/lib/blather/jid.rb +141 -0
  40. data/lib/blather/roster.rb +118 -0
  41. data/lib/blather/roster_item.rb +146 -0
  42. data/lib/blather/stanza.rb +167 -0
  43. data/lib/blather/stanza/disco.rb +32 -0
  44. data/lib/blather/stanza/disco/capabilities.rb +161 -0
  45. data/lib/blather/stanza/disco/disco_info.rb +205 -0
  46. data/lib/blather/stanza/disco/disco_items.rb +134 -0
  47. data/lib/blather/stanza/iq.rb +144 -0
  48. data/lib/blather/stanza/iq/command.rb +339 -0
  49. data/lib/blather/stanza/iq/ibb.rb +86 -0
  50. data/lib/blather/stanza/iq/ping.rb +50 -0
  51. data/lib/blather/stanza/iq/query.rb +53 -0
  52. data/lib/blather/stanza/iq/roster.rb +185 -0
  53. data/lib/blather/stanza/iq/s5b.rb +208 -0
  54. data/lib/blather/stanza/iq/si.rb +415 -0
  55. data/lib/blather/stanza/iq/vcard.rb +149 -0
  56. data/lib/blather/stanza/message.rb +428 -0
  57. data/lib/blather/stanza/message/muc_user.rb +119 -0
  58. data/lib/blather/stanza/muc/muc_user_base.rb +54 -0
  59. data/lib/blather/stanza/presence.rb +172 -0
  60. data/lib/blather/stanza/presence/c.rb +100 -0
  61. data/lib/blather/stanza/presence/muc.rb +35 -0
  62. data/lib/blather/stanza/presence/muc_user.rb +147 -0
  63. data/lib/blather/stanza/presence/status.rb +218 -0
  64. data/lib/blather/stanza/presence/subscription.rb +100 -0
  65. data/lib/blather/stanza/pubsub.rb +119 -0
  66. data/lib/blather/stanza/pubsub/affiliations.rb +79 -0
  67. data/lib/blather/stanza/pubsub/create.rb +65 -0
  68. data/lib/blather/stanza/pubsub/errors.rb +18 -0
  69. data/lib/blather/stanza/pubsub/event.rb +139 -0
  70. data/lib/blather/stanza/pubsub/items.rb +103 -0
  71. data/lib/blather/stanza/pubsub/publish.rb +103 -0
  72. data/lib/blather/stanza/pubsub/retract.rb +92 -0
  73. data/lib/blather/stanza/pubsub/subscribe.rb +68 -0
  74. data/lib/blather/stanza/pubsub/subscription.rb +135 -0
  75. data/lib/blather/stanza/pubsub/subscriptions.rb +83 -0
  76. data/lib/blather/stanza/pubsub/unsubscribe.rb +84 -0
  77. data/lib/blather/stanza/pubsub_owner.rb +51 -0
  78. data/lib/blather/stanza/pubsub_owner/delete.rb +52 -0
  79. data/lib/blather/stanza/pubsub_owner/purge.rb +52 -0
  80. data/lib/blather/stanza/x.rb +416 -0
  81. data/lib/blather/stream.rb +266 -0
  82. data/lib/blather/stream/client.rb +32 -0
  83. data/lib/blather/stream/component.rb +39 -0
  84. data/lib/blather/stream/features.rb +70 -0
  85. data/lib/blather/stream/features/register.rb +38 -0
  86. data/lib/blather/stream/features/resource.rb +63 -0
  87. data/lib/blather/stream/features/sasl.rb +190 -0
  88. data/lib/blather/stream/features/session.rb +45 -0
  89. data/lib/blather/stream/features/tls.rb +29 -0
  90. data/lib/blather/stream/parser.rb +102 -0
  91. data/lib/blather/version.rb +3 -0
  92. data/lib/blather/xmpp_node.rb +94 -0
  93. data/spec/blather/client/client_spec.rb +687 -0
  94. data/spec/blather/client/dsl/pubsub_spec.rb +492 -0
  95. data/spec/blather/client/dsl_spec.rb +266 -0
  96. data/spec/blather/errors/sasl_error_spec.rb +33 -0
  97. data/spec/blather/errors/stanza_error_spec.rb +129 -0
  98. data/spec/blather/errors/stream_error_spec.rb +108 -0
  99. data/spec/blather/errors_spec.rb +33 -0
  100. data/spec/blather/file_transfer_spec.rb +135 -0
  101. data/spec/blather/jid_spec.rb +87 -0
  102. data/spec/blather/roster_item_spec.rb +134 -0
  103. data/spec/blather/roster_spec.rb +107 -0
  104. data/spec/blather/stanza/discos/disco_info_spec.rb +247 -0
  105. data/spec/blather/stanza/discos/disco_items_spec.rb +154 -0
  106. data/spec/blather/stanza/iq/command_spec.rb +206 -0
  107. data/spec/blather/stanza/iq/ibb_spec.rb +124 -0
  108. data/spec/blather/stanza/iq/ping_spec.rb +45 -0
  109. data/spec/blather/stanza/iq/query_spec.rb +64 -0
  110. data/spec/blather/stanza/iq/roster_spec.rb +139 -0
  111. data/spec/blather/stanza/iq/s5b_spec.rb +57 -0
  112. data/spec/blather/stanza/iq/si_spec.rb +98 -0
  113. data/spec/blather/stanza/iq/vcard_spec.rb +93 -0
  114. data/spec/blather/stanza/iq_spec.rb +61 -0
  115. data/spec/blather/stanza/message/muc_user_spec.rb +152 -0
  116. data/spec/blather/stanza/message_spec.rb +282 -0
  117. data/spec/blather/stanza/presence/c_spec.rb +56 -0
  118. data/spec/blather/stanza/presence/muc_spec.rb +37 -0
  119. data/spec/blather/stanza/presence/muc_user_spec.rb +83 -0
  120. data/spec/blather/stanza/presence/status_spec.rb +144 -0
  121. data/spec/blather/stanza/presence/subscription_spec.rb +102 -0
  122. data/spec/blather/stanza/presence_spec.rb +125 -0
  123. data/spec/blather/stanza/pubsub/affiliations_spec.rb +57 -0
  124. data/spec/blather/stanza/pubsub/create_spec.rb +56 -0
  125. data/spec/blather/stanza/pubsub/event_spec.rb +98 -0
  126. data/spec/blather/stanza/pubsub/items_spec.rb +79 -0
  127. data/spec/blather/stanza/pubsub/publish_spec.rb +83 -0
  128. data/spec/blather/stanza/pubsub/retract_spec.rb +75 -0
  129. data/spec/blather/stanza/pubsub/subscribe_spec.rb +61 -0
  130. data/spec/blather/stanza/pubsub/subscription_spec.rb +97 -0
  131. data/spec/blather/stanza/pubsub/subscriptions_spec.rb +59 -0
  132. data/spec/blather/stanza/pubsub/unsubscribe_spec.rb +74 -0
  133. data/spec/blather/stanza/pubsub_owner/delete_spec.rb +50 -0
  134. data/spec/blather/stanza/pubsub_owner/purge_spec.rb +50 -0
  135. data/spec/blather/stanza/pubsub_owner_spec.rb +27 -0
  136. data/spec/blather/stanza/pubsub_spec.rb +68 -0
  137. data/spec/blather/stanza/x_spec.rb +231 -0
  138. data/spec/blather/stanza_spec.rb +134 -0
  139. data/spec/blather/stream/client_spec.rb +1090 -0
  140. data/spec/blather/stream/component_spec.rb +108 -0
  141. data/spec/blather/stream/parser_spec.rb +152 -0
  142. data/spec/blather/stream/ssl_spec.rb +32 -0
  143. data/spec/blather/xmpp_node_spec.rb +47 -0
  144. data/spec/blather_spec.rb +34 -0
  145. data/spec/fixtures/pubsub.rb +311 -0
  146. data/spec/spec_helper.rb +17 -0
  147. data/yard/templates/default/class/html/handlers.erb +18 -0
  148. data/yard/templates/default/class/setup.rb +10 -0
  149. data/yard/templates/default/class/text/handlers.erb +1 -0
  150. metadata +459 -0
@@ -0,0 +1,174 @@
1
+ module Blather
2
+ module DSL
3
+
4
+ # A helper class for providing a simplified PubSub interface to the
5
+ # DSL
6
+ class PubSub
7
+ attr_accessor :host
8
+
9
+ # Create a new pubsub DSL
10
+ #
11
+ # @param [Blather::Client] client the client who's connection will be used
12
+ # @param [#to_s] host the PubSub host
13
+ def initialize(client, host)
14
+ @client = client
15
+ @host = host
16
+ end
17
+
18
+ # Retrieve Affiliations
19
+ #
20
+ # @param [#to_s] host the PubSub host (defaults to the initialized host)
21
+ # @yield [Hash] affiliations See {Blather::Stanza::PubSub::Affiliations#list}
22
+ def affiliations(host = nil, &callback)
23
+ request Stanza::PubSub::Affiliations.new(:get, send_to(host)), :list, callback
24
+ end
25
+
26
+ # Retrieve Subscriptions
27
+ #
28
+ # @param [#to_s] host the PubSub host (defaults to the initialized host)
29
+ # @yield [Hash] affiliations See {Blather::Stanza::PubSub::Subscriptions#list}
30
+ def subscriptions(host = nil, &callback)
31
+ request Stanza::PubSub::Subscriptions.new(:get, send_to(host)), :list, callback
32
+ end
33
+
34
+ # Discover Nodes
35
+ #
36
+ # @param [#to_s] path the node path
37
+ # @param [#to_s] host the PubSub host (defaults to the initialized host)
38
+ # @yield [Array<Blather::Stanza::DiscoItems::Item>] items
39
+ def nodes(path = nil, host = nil, &callback)
40
+ path ||= '/'
41
+ stanza = Stanza::DiscoItems.new(:get, path)
42
+ stanza.to = send_to(host)
43
+ request stanza, :items, callback
44
+ end
45
+
46
+ # Discover node information
47
+ #
48
+ # @param [#to_s] path the node path
49
+ # @param [#to_s] host the PubSub host (defaults to the initialized host)
50
+ # @yield [Blather::Stanza::DiscoInfo>] info
51
+ def node(path, host = nil, &callback)
52
+ stanza = Stanza::DiscoInfo.new(:get, path)
53
+ stanza.to = send_to(host)
54
+ request stanza, nil, callback
55
+ end
56
+
57
+ # Retrieve items for a node
58
+ #
59
+ # @param [#to_s] path the node path
60
+ # @param [Array<#to_s>] list a list of IDs to retrieve
61
+ # @param [Fixnum, #to_s] max the maximum number of items to return
62
+ # @param [#to_s] host the PubSub host (defaults to the initialized host)
63
+ # @yield [Array<Blather::Stanza::PubSub::PubSubItem>] items see {Blather::Stanza::PubSub::Items#items}
64
+ def items(path, list = [], max = nil, host = nil, &callback)
65
+ request(
66
+ Stanza::PubSub::Items.request(send_to(host), path, list, max),
67
+ :items,
68
+ callback
69
+ )
70
+ end
71
+
72
+ # Subscribe to a node
73
+ #
74
+ # @param [#to_s] node the node to subscribe to
75
+ # @param [Blather::JID, #to_s] jid is the jid that should be used.
76
+ # Defaults to the stripped current JID
77
+ # @param [#to_s] host the PubSub host (defaults to the initialized host)
78
+ # @yield [Blather::Stanza] stanza the reply stanza
79
+ def subscribe(node, jid = nil, host = nil)
80
+ jid ||= client.jid.stripped
81
+ stanza = Stanza::PubSub::Subscribe.new(:set, send_to(host), node, jid)
82
+ request(stanza) { |n| yield n if block_given? }
83
+ end
84
+
85
+ # Unsubscribe from a node
86
+ #
87
+ # @param [#to_s] node the node to unsubscribe from
88
+ # @param [Blather::JID, #to_s] jid is the jid that should be used.
89
+ # Defaults to the stripped current JID
90
+ # @param [#to_s] host the PubSub host (defaults to the initialized host)
91
+ # @yield [Blather::Stanza] stanza the reply stanza
92
+ def unsubscribe(node, jid = nil, subid = nil, host = nil)
93
+ jid ||= client.jid.stripped
94
+ stanza = Stanza::PubSub::Unsubscribe.new(:set, send_to(host), node, jid, subid)
95
+ request(stanza) { |n| yield n if block_given? }
96
+ end
97
+
98
+ # Publish an item to a node
99
+ #
100
+ # @param [#to_s] node the node to publish to
101
+ # @param [#to_s] payload the payload to send see {Blather::Stanza::PubSub::Publish}
102
+ # @param [#to_s] host the PubSub host (defaults to the initialized host)
103
+ # @yield [Blather::Stanza] stanza the reply stanza
104
+ def publish(node, payload, host = nil)
105
+ stanza = Stanza::PubSub::Publish.new(send_to(host), node, :set, payload)
106
+ request(stanza) { |n| yield n if block_given? }
107
+ end
108
+
109
+ # Delete items from a node
110
+ #
111
+ # @param [#to_s] node the node to delete from
112
+ # @param [Array<#to_s>] ids a list of ids to delete
113
+ # @param [#to_s] host the PubSub host (defaults to the initialized host)
114
+ # @yield [Blather::Stanza] stanza the reply stanza
115
+ def retract(node, ids = [], host = nil)
116
+ stanza = Stanza::PubSub::Retract.new(send_to(host), node, :set, ids)
117
+ request(stanza) { |n| yield n if block_given? }
118
+ end
119
+
120
+ # Create a node
121
+ #
122
+ # @param [#to_s] node the node to create
123
+ # @param [#to_s] host the PubSub host (defaults to the initialized host)
124
+ # @param [optional, Blather::Stanza::X] configuration the additional configuration to be set to created node
125
+ # @yield [Blather::Stanza] stanza the reply stanza
126
+ def create(node, host = nil, configuration = nil)
127
+ stanza = Stanza::PubSub::Create.new(:set, send_to(host), node)
128
+ stanza.configure_node << configuration if configuration
129
+ request(stanza) { |n| yield n if block_given? }
130
+ end
131
+
132
+ # Purge all node items
133
+ #
134
+ # @param [#to_s] node the node to purge
135
+ # @param [#to_s] host the PubSub host (defaults to the initialized host)
136
+ # @yield [Blather::Stanza] stanza the reply stanza
137
+ def purge(node, host = nil)
138
+ stanza = Stanza::PubSubOwner::Purge.new(:set, send_to(host), node)
139
+ request(stanza) { |n| yield n if block_given? }
140
+ end
141
+
142
+ # Delete a node
143
+ #
144
+ # @param [#to_s] node the node to delete
145
+ # @param [#to_s] host the PubSub host (defaults to the initialized host)
146
+ # @yield [Blather::Stanza] stanza the reply stanza
147
+ def delete(node, host = nil)
148
+ stanza = Stanza::PubSubOwner::Delete.new(:set, send_to(host), node)
149
+ request(stanza) { |n| yield n if block_given? }
150
+ end
151
+
152
+ private
153
+ def request(node, method = nil, callback = nil, &block)
154
+ unless block_given?
155
+ block = lambda do |node|
156
+ callback.call(method ? node.__send__(method) : node)
157
+ end
158
+ end
159
+
160
+ client.write_with_handler(node, &block)
161
+ end
162
+
163
+ def send_to(host = nil)
164
+ raise 'You must provide a host' unless (host ||= @host)
165
+ host
166
+ end
167
+
168
+ def client
169
+ @client
170
+ end
171
+ end # PubSub
172
+
173
+ end # DSL
174
+ end # Blather
@@ -0,0 +1,125 @@
1
+ # @private
2
+ module EventMachine
3
+ # @private
4
+ module Protocols
5
+ # Basic SOCKS v5 client implementation
6
+ #
7
+ # Use as you would any regular connection:
8
+ #
9
+ # class MyConn < EM::P::Socks5
10
+ # def post_init
11
+ # send_data("sup")
12
+ # end
13
+ #
14
+ # def receive_data(data)
15
+ # send_data("you said: #{data}")
16
+ # end
17
+ # end
18
+ #
19
+ # EM.connect socks_host, socks_port, MyConn, host, port
20
+ #
21
+ # @private
22
+ class Socks5 < Connection
23
+ def initialize(host, port)
24
+ @host = host
25
+ @port = port
26
+ @socks_error_code = nil
27
+ @buffer = ''
28
+ @socks_state = :method_negotiation
29
+ @socks_methods = [0] # TODO: other authentication methods
30
+ setup_methods
31
+ end
32
+
33
+ def setup_methods
34
+ class << self
35
+ def post_init; socks_post_init; end
36
+ def receive_data(*a); socks_receive_data(*a); end
37
+ end
38
+ end
39
+
40
+ def restore_methods
41
+ class << self
42
+ remove_method :post_init
43
+ remove_method :receive_data
44
+ end
45
+ end
46
+
47
+ def socks_post_init
48
+ packet = [5, @socks_methods.size].pack('CC') + @socks_methods.pack('C*')
49
+ send_data(packet)
50
+ end
51
+
52
+ def socks_receive_data(data)
53
+ @buffer << data
54
+
55
+ if @socks_state == :method_negotiation
56
+ return if @buffer.size < 2
57
+
58
+ header_resp = @buffer.slice! 0, 2
59
+ _, method_code = header_resp.unpack("cc")
60
+
61
+ if @socks_methods.include?(method_code)
62
+ @socks_state = :connecting
63
+ packet = [5, 1, 0].pack("C*")
64
+
65
+ if @host =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/ # IPv4
66
+ packet << [1, $1.to_i, $2.to_i, $3.to_i, $4.to_i].pack("C*")
67
+ elsif @host.include?(":") # IPv6
68
+ l, r = if @host =~ /^(.*)::(.*)$/
69
+ [$1,$2].map {|i| i.split ":"}
70
+ else
71
+ [@host.split(":"),[]]
72
+ end
73
+ dec_groups = (l + Array.new(8-l.size-r.size, '0') + r).map {|i| i.hex}
74
+ packet << ([4] + dec_groups).pack("Cn8")
75
+ else # Domain
76
+ packet << [3, @host.length, @host].pack("CCA*")
77
+ end
78
+ packet << [@port].pack("n")
79
+
80
+ send_data packet
81
+ else
82
+ @socks_state = :invalid
83
+ @socks_error_code = method_code
84
+ close_connection
85
+ return
86
+ end
87
+ elsif @socks_state == :connecting
88
+ return if @buffer.size < 4
89
+
90
+ header_resp = @buffer.slice! 0, 4
91
+ _, response_code, _, address_type = header_resp.unpack("C*")
92
+
93
+ if response_code == 0
94
+ case address_type
95
+ when 1
96
+ @buffer.slice! 0, 4
97
+ when 3
98
+ len = @buffer.slice! 0, 1
99
+ @buffer.slice! 0, len.unpack("C").first
100
+ when 4
101
+ @buffer.slice! 0, 16
102
+ else
103
+ @socks_state = :invalid
104
+ @socks_error_code = address_type
105
+ close_connection
106
+ return
107
+ end
108
+ @buffer.slice! 0, 2
109
+
110
+ @socks_state = :connected
111
+ restore_methods
112
+
113
+ post_init
114
+ receive_data(@buffer) unless @buffer.empty?
115
+ else
116
+ @socks_state = :invalid
117
+ @socks_error_code = response_code
118
+ close_connection
119
+ return
120
+ end
121
+ end
122
+ end
123
+ end
124
+ end
125
+ end
@@ -0,0 +1,20 @@
1
+ # @private
2
+ class IPAddr
3
+ PrivateRanges = [
4
+ IPAddr.new("10.0.0.0/8"),
5
+ IPAddr.new("172.16.0.0/12"),
6
+ IPAddr.new("192.168.0.0/16")
7
+ ]
8
+
9
+ def private?
10
+ return false unless self.ipv4?
11
+ PrivateRanges.each do |ipr|
12
+ return true if ipr.include?(self)
13
+ end
14
+ return false
15
+ end
16
+
17
+ def public?
18
+ !private?
19
+ end
20
+ end
@@ -0,0 +1,69 @@
1
+ module Blather
2
+ # Main error class
3
+ # This starts the error hierarchy
4
+ #
5
+ # @handler :error
6
+ class BlatherError < StandardError
7
+ class_attribute :handler_hierarchy
8
+ self.handler_hierarchy ||= []
9
+
10
+ # @private
11
+ @@handler_list = []
12
+
13
+ # Register the class's handler
14
+ #
15
+ # @param [Symbol] handler the handler name
16
+ def self.register(handler)
17
+ @@handler_list << handler
18
+ self.handler_hierarchy = [handler] + self.handler_hierarchy
19
+ end
20
+
21
+ # The list of registered handlers
22
+ #
23
+ # @return [Array<Symbol>] a list of currently registered handlers
24
+ def self.handler_list
25
+ @@handler_list
26
+ end
27
+
28
+ register :error
29
+
30
+ # @private
31
+ # HACK!! until I can refactor the entire Error object model
32
+ def id
33
+ nil
34
+ end
35
+ end # BlatherError
36
+
37
+ # Used in cases where a stanza only allows specific values for its attributes
38
+ # and an invalid value is attempted.
39
+ #
40
+ # @handler :argument_error
41
+ class ArgumentError < BlatherError
42
+ register :argument_error
43
+ end # ArgumentError
44
+
45
+ # The stream handler received a response it didn't know how to handle
46
+ #
47
+ # @handler :unknown_response_error
48
+ class UnknownResponse < BlatherError
49
+ register :unknown_response_error
50
+ attr_reader :node
51
+
52
+ def initialize(node)
53
+ @node = node
54
+ end
55
+ end # UnknownResponse
56
+
57
+ # Something bad happened while parsing the incoming stream
58
+ #
59
+ # @handler :parse_error
60
+ class ParseError < BlatherError
61
+ register :parse_error
62
+ attr_reader :message
63
+
64
+ def initialize(msg)
65
+ @message = msg.to_s
66
+ end
67
+ end # ParseError
68
+
69
+ end # Blather
@@ -0,0 +1,44 @@
1
+ module Blather
2
+
3
+ # General SASL Errors
4
+ # Check #name for the error name
5
+ #
6
+ # @handler :sasl_error
7
+ class SASLError < BlatherError
8
+ # @private
9
+ SASL_ERR_NS = 'urn:ietf:params:xml:ns:xmpp-sasl'
10
+
11
+ class_attribute :err_name
12
+ # @private
13
+ @@registrations = {}
14
+
15
+ register :sasl_error
16
+
17
+ # Import the stanza
18
+ #
19
+ # @param [Blather::XMPPNode] node the error node
20
+ # @return [Blather::SASLError]
21
+ def self.import(node)
22
+ self.new node
23
+ end
24
+
25
+ # Create a new SASLError
26
+ #
27
+ # @param [Blather::XMPPNode] node the error node
28
+ def initialize(node)
29
+ super()
30
+ @node = node
31
+ end
32
+
33
+ # The actual error name
34
+ #
35
+ # @return [Symbol] a symbol representing the error name
36
+ def name
37
+ if @node
38
+ name = @node.find_first('ns:*', :ns => SASL_ERR_NS).element_name
39
+ name.gsub('-', '_').to_sym
40
+ end
41
+ end
42
+ end # SASLError
43
+
44
+ end # Blather
@@ -0,0 +1,110 @@
1
+ module Blather
2
+
3
+ # Stanza errors
4
+ # RFC3920 Section 9.3 (http://xmpp.org/rfcs/rfc3920.html#stanzas-error)
5
+ #
6
+ # @handler :stanza_error
7
+ class StanzaError < BlatherError
8
+ # @private
9
+ STANZA_ERR_NS = 'urn:ietf:params:xml:ns:xmpp-stanzas'
10
+ # @private
11
+ VALID_TYPES = [:cancel, :continue, :modify, :auth, :wait].freeze
12
+
13
+ register :stanza_error
14
+
15
+ attr_reader :original, :name, :type, :text, :extras
16
+
17
+ # Factory method for instantiating the proper class for the error
18
+ #
19
+ # @param [Blather::XMPPNode] node the error node to import
20
+ # @return [Blather::StanzaError]
21
+ def self.import(node)
22
+ original = node.copy
23
+ original.remove_child 'error'
24
+
25
+ error_node = node.find_first '//*[local-name()="error"]'
26
+
27
+ name = error_node.find_first('child::*[name()!="text"]', STANZA_ERR_NS).element_name
28
+ type = error_node['type']
29
+ text = node.find_first 'descendant::*[name()="text"]', STANZA_ERR_NS
30
+ text = text.content if text
31
+
32
+ extras = error_node.find("descendant::*[name()!='text' and name()!='#{name}']").map { |n| n }
33
+
34
+ self.new original, name, type, text, extras
35
+ end
36
+
37
+ # Create a new StanzaError
38
+ #
39
+ # @param [Blather::XMPPNode] original the original stanza
40
+ # @param [String] name the error name
41
+ # @param [#to_s] type the error type as specified in
42
+ # [RFC3920](http://xmpp.org/rfcs/rfc3920.html#rfc.section.9.3.2)
43
+ # @param [String, nil] text additional text for the error
44
+ # @param [Array<Blather::XMPPNode>] extras an array of extra nodes to add
45
+ def initialize(original, name, type, text = nil, extras = [])
46
+ @original = original
47
+ @name = name
48
+ self.type = type
49
+ @text = text
50
+ @extras = extras
51
+ end
52
+
53
+ # Set the error type
54
+ #
55
+ # @param [#to_sym] type the new error type. Must be on of
56
+ # Blather::StanzaError::VALID_TYPES
57
+ # @see [RFC3920 Section 9.3.2](http://xmpp.org/rfcs/rfc3920.html#rfc.section.9.3.2)
58
+ def type=(type)
59
+ type = type.to_sym
60
+ if !VALID_TYPES.include?(type)
61
+ raise ArgumentError, "Invalid Type (#{type}), use: #{VALID_TYPES*' '}"
62
+ end
63
+ @type = type
64
+ end
65
+
66
+ # The error name
67
+ #
68
+ # @return [Symbol]
69
+ def name
70
+ @name.gsub('-','_').to_sym
71
+ end
72
+
73
+ # Creates an XML node from the error
74
+ #
75
+ # @return [Blather::XMPPNode]
76
+ def to_node
77
+ node = self.original.reply
78
+ node.type = 'error'
79
+ node << (error_node = XMPPNode.new('error'))
80
+
81
+ error_node << (err = XMPPNode.new(@name, error_node.document))
82
+ error_node['type'] = self.type
83
+ err.namespace = 'urn:ietf:params:xml:ns:xmpp-stanzas'
84
+
85
+ if self.text
86
+ error_node << (text = XMPPNode.new('text', error_node.document))
87
+ text.namespace = 'urn:ietf:params:xml:ns:xmpp-stanzas'
88
+ text.content = self.text
89
+ end
90
+
91
+ self.extras.each { |extra| error_node << extra.dup }
92
+ node
93
+ end
94
+
95
+ # Convert the object to a proper node then convert it to a string
96
+ #
97
+ # @return [String]
98
+ def to_xml
99
+ to_node.to_s
100
+ end
101
+
102
+ # @private
103
+ def inspect
104
+ "Stanza Error (#{@name}): #{self.text} [#{self.extras}]"
105
+ end
106
+ # @private
107
+ alias_method :to_s, :inspect
108
+ end # StanzaError
109
+
110
+ end # Blather