tp-blather 0.8.2

Sign up to get free protection for your applications and to get access to all the features.
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