vinesmod 0.4.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (151) hide show
  1. data/Gemfile +3 -0
  2. data/LICENSE +19 -0
  3. data/README.md +43 -0
  4. data/Rakefile +57 -0
  5. data/bin/vines +93 -0
  6. data/conf/certs/README +39 -0
  7. data/conf/certs/ca-bundle.crt +3366 -0
  8. data/conf/config.rb +149 -0
  9. data/lib/vines.rb +197 -0
  10. data/lib/vines/cluster.rb +246 -0
  11. data/lib/vines/cluster/connection.rb +26 -0
  12. data/lib/vines/cluster/publisher.rb +55 -0
  13. data/lib/vines/cluster/pubsub.rb +92 -0
  14. data/lib/vines/cluster/sessions.rb +125 -0
  15. data/lib/vines/cluster/subscriber.rb +108 -0
  16. data/lib/vines/command/bcrypt.rb +12 -0
  17. data/lib/vines/command/cert.rb +50 -0
  18. data/lib/vines/command/init.rb +68 -0
  19. data/lib/vines/command/register.rb +27 -0
  20. data/lib/vines/command/restart.rb +12 -0
  21. data/lib/vines/command/schema.rb +24 -0
  22. data/lib/vines/command/start.rb +28 -0
  23. data/lib/vines/command/stop.rb +18 -0
  24. data/lib/vines/command/unregister.rb +27 -0
  25. data/lib/vines/config.rb +213 -0
  26. data/lib/vines/config/host.rb +119 -0
  27. data/lib/vines/config/port.rb +132 -0
  28. data/lib/vines/config/pubsub.rb +108 -0
  29. data/lib/vines/contact.rb +111 -0
  30. data/lib/vines/daemon.rb +78 -0
  31. data/lib/vines/error.rb +150 -0
  32. data/lib/vines/jid.rb +95 -0
  33. data/lib/vines/kit.rb +35 -0
  34. data/lib/vines/log.rb +24 -0
  35. data/lib/vines/router.rb +179 -0
  36. data/lib/vines/stanza.rb +175 -0
  37. data/lib/vines/stanza/iq.rb +48 -0
  38. data/lib/vines/stanza/iq/auth.rb +18 -0
  39. data/lib/vines/stanza/iq/disco_info.rb +45 -0
  40. data/lib/vines/stanza/iq/disco_items.rb +29 -0
  41. data/lib/vines/stanza/iq/error.rb +16 -0
  42. data/lib/vines/stanza/iq/ping.rb +16 -0
  43. data/lib/vines/stanza/iq/private_storage.rb +83 -0
  44. data/lib/vines/stanza/iq/query.rb +10 -0
  45. data/lib/vines/stanza/iq/register.rb +42 -0
  46. data/lib/vines/stanza/iq/result.rb +16 -0
  47. data/lib/vines/stanza/iq/roster.rb +140 -0
  48. data/lib/vines/stanza/iq/session.rb +17 -0
  49. data/lib/vines/stanza/iq/vcard.rb +56 -0
  50. data/lib/vines/stanza/iq/version.rb +25 -0
  51. data/lib/vines/stanza/message.rb +43 -0
  52. data/lib/vines/stanza/presence.rb +156 -0
  53. data/lib/vines/stanza/presence/error.rb +23 -0
  54. data/lib/vines/stanza/presence/probe.rb +37 -0
  55. data/lib/vines/stanza/presence/subscribe.rb +42 -0
  56. data/lib/vines/stanza/presence/subscribed.rb +51 -0
  57. data/lib/vines/stanza/presence/unavailable.rb +15 -0
  58. data/lib/vines/stanza/presence/unsubscribe.rb +38 -0
  59. data/lib/vines/stanza/presence/unsubscribed.rb +38 -0
  60. data/lib/vines/stanza/pubsub.rb +22 -0
  61. data/lib/vines/stanza/pubsub/create.rb +39 -0
  62. data/lib/vines/stanza/pubsub/delete.rb +41 -0
  63. data/lib/vines/stanza/pubsub/publish.rb +66 -0
  64. data/lib/vines/stanza/pubsub/subscribe.rb +44 -0
  65. data/lib/vines/stanza/pubsub/unsubscribe.rb +30 -0
  66. data/lib/vines/storage.rb +188 -0
  67. data/lib/vines/storage/local.rb +165 -0
  68. data/lib/vines/storage/null.rb +39 -0
  69. data/lib/vines/storage/sql.rb +260 -0
  70. data/lib/vines/store.rb +94 -0
  71. data/lib/vines/stream.rb +247 -0
  72. data/lib/vines/stream/client.rb +84 -0
  73. data/lib/vines/stream/client/auth.rb +74 -0
  74. data/lib/vines/stream/client/auth_restart.rb +29 -0
  75. data/lib/vines/stream/client/bind.rb +72 -0
  76. data/lib/vines/stream/client/bind_restart.rb +24 -0
  77. data/lib/vines/stream/client/closed.rb +13 -0
  78. data/lib/vines/stream/client/ready.rb +17 -0
  79. data/lib/vines/stream/client/session.rb +210 -0
  80. data/lib/vines/stream/client/start.rb +27 -0
  81. data/lib/vines/stream/client/tls.rb +38 -0
  82. data/lib/vines/stream/component.rb +58 -0
  83. data/lib/vines/stream/component/handshake.rb +26 -0
  84. data/lib/vines/stream/component/ready.rb +23 -0
  85. data/lib/vines/stream/component/start.rb +19 -0
  86. data/lib/vines/stream/http.rb +157 -0
  87. data/lib/vines/stream/http/auth.rb +22 -0
  88. data/lib/vines/stream/http/bind.rb +32 -0
  89. data/lib/vines/stream/http/bind_restart.rb +37 -0
  90. data/lib/vines/stream/http/ready.rb +29 -0
  91. data/lib/vines/stream/http/request.rb +172 -0
  92. data/lib/vines/stream/http/session.rb +120 -0
  93. data/lib/vines/stream/http/sessions.rb +65 -0
  94. data/lib/vines/stream/http/start.rb +23 -0
  95. data/lib/vines/stream/parser.rb +78 -0
  96. data/lib/vines/stream/sasl.rb +92 -0
  97. data/lib/vines/stream/server.rb +150 -0
  98. data/lib/vines/stream/server/auth.rb +13 -0
  99. data/lib/vines/stream/server/auth_restart.rb +13 -0
  100. data/lib/vines/stream/server/final_restart.rb +21 -0
  101. data/lib/vines/stream/server/outbound/auth.rb +31 -0
  102. data/lib/vines/stream/server/outbound/auth_restart.rb +20 -0
  103. data/lib/vines/stream/server/outbound/auth_result.rb +32 -0
  104. data/lib/vines/stream/server/outbound/final_features.rb +28 -0
  105. data/lib/vines/stream/server/outbound/final_restart.rb +20 -0
  106. data/lib/vines/stream/server/outbound/start.rb +20 -0
  107. data/lib/vines/stream/server/outbound/tls.rb +30 -0
  108. data/lib/vines/stream/server/outbound/tls_result.rb +34 -0
  109. data/lib/vines/stream/server/ready.rb +24 -0
  110. data/lib/vines/stream/server/start.rb +13 -0
  111. data/lib/vines/stream/server/tls.rb +13 -0
  112. data/lib/vines/stream/state.rb +60 -0
  113. data/lib/vines/token_bucket.rb +55 -0
  114. data/lib/vines/user.rb +123 -0
  115. data/lib/vines/version.rb +5 -0
  116. data/lib/vines/xmpp_server.rb +43 -0
  117. data/vines.gemspec +36 -0
  118. data/web/404.html +51 -0
  119. data/web/apple-touch-icon.png +0 -0
  120. data/web/chat/coffeescripts/chat.coffee +362 -0
  121. data/web/chat/coffeescripts/init.coffee +15 -0
  122. data/web/chat/index.html +16 -0
  123. data/web/chat/javascripts/app.js +1 -0
  124. data/web/chat/stylesheets/chat.css +144 -0
  125. data/web/favicon.png +0 -0
  126. data/web/lib/coffeescripts/button.coffee +25 -0
  127. data/web/lib/coffeescripts/contact.coffee +32 -0
  128. data/web/lib/coffeescripts/filter.coffee +49 -0
  129. data/web/lib/coffeescripts/layout.coffee +30 -0
  130. data/web/lib/coffeescripts/login.coffee +68 -0
  131. data/web/lib/coffeescripts/logout.coffee +5 -0
  132. data/web/lib/coffeescripts/navbar.coffee +84 -0
  133. data/web/lib/coffeescripts/notification.coffee +14 -0
  134. data/web/lib/coffeescripts/router.coffee +40 -0
  135. data/web/lib/coffeescripts/session.coffee +229 -0
  136. data/web/lib/coffeescripts/transfer.coffee +106 -0
  137. data/web/lib/images/dark-gray.png +0 -0
  138. data/web/lib/images/default-user.png +0 -0
  139. data/web/lib/images/light-gray.png +0 -0
  140. data/web/lib/images/logo-large.png +0 -0
  141. data/web/lib/images/logo-small.png +0 -0
  142. data/web/lib/images/white.png +0 -0
  143. data/web/lib/javascripts/base.js +12 -0
  144. data/web/lib/javascripts/icons.js +110 -0
  145. data/web/lib/javascripts/jquery.cookie.js +91 -0
  146. data/web/lib/javascripts/jquery.js +4 -0
  147. data/web/lib/javascripts/raphael.js +6 -0
  148. data/web/lib/javascripts/strophe.js +1 -0
  149. data/web/lib/stylesheets/base.css +385 -0
  150. data/web/lib/stylesheets/login.css +68 -0
  151. metadata +423 -0
@@ -0,0 +1,132 @@
1
+ # encoding: UTF-8
2
+
3
+ module Vines
4
+ class Config
5
+ class Port
6
+ include Vines::Log
7
+
8
+ attr_reader :config, :stream, :settings
9
+
10
+ %w[host port].each do |name|
11
+ define_method(name) do
12
+ @settings[name.to_sym]
13
+ end
14
+ end
15
+
16
+ def initialize(config, host, port, &block)
17
+ @config, @settings = config, {}
18
+ instance_eval(&block) if block
19
+ defaults = {:host => host, :port => port,
20
+ :max_resources_per_account => 5, :max_stanza_size => 128 * 1024}
21
+ @settings = defaults.merge(@settings)
22
+ end
23
+
24
+ def max_stanza_size(max=nil)
25
+ if max
26
+ # rfc 6120 section 13.12
27
+ @settings[:max_stanza_size] = [10000, max].max
28
+ else
29
+ @settings[:max_stanza_size]
30
+ end
31
+ end
32
+
33
+ def start
34
+ type = stream.name.split('::').last.downcase
35
+ log.info("Accepting #{type} connections on #{host}:#{port}")
36
+ EventMachine::start_server(host, port, stream, config)
37
+ end
38
+ end
39
+
40
+ class ClientPort < Port
41
+ def initialize(config, host='0.0.0.0', port=5222, &block)
42
+ @stream = Vines::Stream::Client
43
+ super(config, host, port, &block)
44
+ end
45
+
46
+ def max_resources_per_account(max=nil)
47
+ if max
48
+ @settings[:max_resources_per_account] = max
49
+ else
50
+ @settings[:max_resources_per_account]
51
+ end
52
+ end
53
+
54
+ def start
55
+ super
56
+ config.cluster.start if config.cluster?
57
+ end
58
+ end
59
+
60
+ class ServerPort < Port
61
+ def initialize(config, host='0.0.0.0', port=5269, &block)
62
+ @hosts, @stream = [], Vines::Stream::Server
63
+ super(config, host, port, &block)
64
+ end
65
+
66
+ def hosts(*hosts)
67
+ if hosts.any?
68
+ @hosts << hosts
69
+ @hosts.flatten!
70
+ else
71
+ @hosts
72
+ end
73
+ end
74
+ end
75
+
76
+ class HttpPort < Port
77
+ def initialize(config, host='0.0.0.0', port=5280, &block)
78
+ @stream = Vines::Stream::Http
79
+ super(config, host, port, &block)
80
+ defaults = {:root => File.expand_path('web'), :bind => '/xmpp'}
81
+ @settings = defaults.merge(@settings)
82
+ end
83
+
84
+ def max_resources_per_account(max=nil)
85
+ if max
86
+ @settings[:max_resources_per_account] = max
87
+ else
88
+ @settings[:max_resources_per_account]
89
+ end
90
+ end
91
+
92
+ def root(dir=nil)
93
+ if dir
94
+ @settings[:root] = File.expand_path(dir)
95
+ else
96
+ @settings[:root]
97
+ end
98
+ end
99
+
100
+ def bind(url=nil)
101
+ if url
102
+ @settings[:bind] = url
103
+ else
104
+ @settings[:bind]
105
+ end
106
+ end
107
+
108
+ def vroute(id=nil)
109
+ if id
110
+ id = id.to_s.strip
111
+ @settings[:vroute] = id.empty? ? nil : id
112
+ else
113
+ @settings[:vroute]
114
+ end
115
+ end
116
+
117
+ def start
118
+ super
119
+ if config.cluster? && vroute.nil?
120
+ log.warn("vroute sticky session cookie not set")
121
+ end
122
+ end
123
+ end
124
+
125
+ class ComponentPort < Port
126
+ def initialize(config, host='0.0.0.0', port=5347, &block)
127
+ @stream = Vines::Stream::Component
128
+ super(config, host, port, &block)
129
+ end
130
+ end
131
+ end
132
+ end
@@ -0,0 +1,108 @@
1
+ # encoding: UTF-8
2
+
3
+ module Vines
4
+ class Config
5
+ # Provides the configuration DSL to conf/config.rb for pubsub subdomains and
6
+ # exposes the storage and notification systems that the pubsub stanzas need
7
+ # to process. This class hides the complexity of determining pubsub behavior
8
+ # in a standalone vs. clustered chat server environment from the stanzas.
9
+ class PubSub
10
+ def initialize(config, name)
11
+ @config, @name = config, name
12
+ @nodes = {}
13
+ end
14
+
15
+ def add_node(id)
16
+ if @config.cluster?
17
+ @config.cluster.add_pubsub_node(@name, id)
18
+ else
19
+ @nodes[id] ||= Set.new
20
+ end
21
+ end
22
+
23
+ def delete_node(id)
24
+ if @config.cluster?
25
+ @config.cluster.delete_pubsub_node(@name, id)
26
+ else
27
+ @nodes.delete(id)
28
+ end
29
+ end
30
+
31
+ def subscribe(node, jid)
32
+ return unless node?(node) && @config.allowed?(jid, @name)
33
+ if @config.cluster?
34
+ @config.cluster.subscribe_pubsub(@name, node, jid)
35
+ else
36
+ @nodes[node] << JID.new(jid)
37
+ end
38
+ end
39
+
40
+ def unsubscribe(node, jid)
41
+ return unless node?(node)
42
+ if @config.cluster?
43
+ @config.cluster.unsubscribe_pubsub(@name, node, jid)
44
+ else
45
+ @nodes[node].delete(JID.new(jid))
46
+ delete_node(node) if subscribers(node).empty?
47
+ end
48
+ end
49
+
50
+ def unsubscribe_all(jid)
51
+ if @config.cluster?
52
+ @config.cluster.unsubscribe_all_pubsub(@name, jid)
53
+ else
54
+ @nodes.keys.each do |node|
55
+ unsubscribe(node, jid)
56
+ end
57
+ end
58
+ end
59
+
60
+ def node?(node)
61
+ if @config.cluster?
62
+ @config.cluster.pubsub_node?(@name, node)
63
+ else
64
+ @nodes.key?(node)
65
+ end
66
+ end
67
+
68
+ def subscribed?(node, jid)
69
+ return false unless node?(node)
70
+ if @config.cluster?
71
+ @config.cluster.pubsub_subscribed?(@name, node, jid)
72
+ else
73
+ @nodes[node].include?(JID.new(jid))
74
+ end
75
+ end
76
+
77
+ def publish(node, stanza)
78
+ stanza['id'] = Kit.uuid
79
+ stanza['from'] = @name
80
+
81
+ local, remote = subscribers(node).partition {|jid| @config.local_jid?(jid) }
82
+
83
+ local.flat_map do |jid|
84
+ @config.router.connected_resources(jid, @name)
85
+ end.each do |recipient|
86
+ stanza['to'] = recipient.user.jid.to_s
87
+ recipient.write(stanza)
88
+ end
89
+
90
+ remote.each do |jid|
91
+ el = stanza.clone
92
+ el['to'] = jid.to_s
93
+ @config.router.route(el) rescue nil # ignore RemoteServerNotFound
94
+ end
95
+ end
96
+
97
+ private
98
+
99
+ def subscribers(node)
100
+ if @config.cluster?
101
+ @config.cluster.pubsub_subscribers(@name, node)
102
+ else
103
+ @nodes[node] || []
104
+ end
105
+ end
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,111 @@
1
+ # encoding: UTF-8
2
+
3
+ module Vines
4
+ class Contact
5
+ include Comparable
6
+
7
+ attr_accessor :name, :subscription, :ask, :groups
8
+ attr_reader :jid
9
+
10
+ def initialize(args={})
11
+ @jid = JID.new(args[:jid]).bare
12
+ raise ArgumentError, 'invalid jid' if @jid.empty?
13
+ @name = args[:name]
14
+ @subscription = args[:subscription] || 'none'
15
+ @ask = args[:ask]
16
+ @groups = args[:groups] || []
17
+ end
18
+
19
+ def <=>(contact)
20
+ contact.is_a?(Contact) ? self.jid.to_s <=> contact.jid.to_s : nil
21
+ end
22
+
23
+ alias :eql? :==
24
+
25
+ def hash
26
+ jid.to_s.hash
27
+ end
28
+
29
+ def update_from(contact)
30
+ @name = contact.name
31
+ @subscription = contact.subscription
32
+ @ask = contact.ask
33
+ @groups = contact.groups.clone
34
+ end
35
+
36
+ # Returns true if this contact is in a state that allows the user
37
+ # to subscribe to their presence updates.
38
+ def can_subscribe?
39
+ @ask == 'subscribe' && %w[none from].include?(@subscription)
40
+ end
41
+
42
+ def subscribe_to
43
+ @subscription = (@subscription == 'none') ? 'to' : 'both'
44
+ @ask = nil
45
+ end
46
+
47
+ def unsubscribe_to
48
+ @subscription = (@subscription == 'both') ? 'from' : 'none'
49
+ end
50
+
51
+ def subscribe_from
52
+ @subscription = (@subscription == 'none') ? 'from' : 'both'
53
+ @ask = nil
54
+ end
55
+
56
+ def unsubscribe_from
57
+ @subscription = (@subscription == 'both') ? 'to' : 'none'
58
+ end
59
+
60
+ # Returns true if the user is subscribed to this contact's
61
+ # presence updates.
62
+ def subscribed_to?
63
+ %w[to both].include?(@subscription)
64
+ end
65
+
66
+ # Returns true if the user has a presence subscription from
67
+ # this contact. The contact is subscribed to this user's presence.
68
+ def subscribed_from?
69
+ %w[from both].include?(@subscription)
70
+ end
71
+
72
+ # Returns a hash of this contact's attributes suitable for persisting in
73
+ # a document store.
74
+ def to_h
75
+ {
76
+ 'name' => @name,
77
+ 'subscription' => @subscription,
78
+ 'ask' => @ask,
79
+ 'groups' => @groups.sort!
80
+ }
81
+ end
82
+
83
+ # Write an iq stanza to the recipient stream representing this contact's
84
+ # current roster item state.
85
+ def send_roster_push(recipient)
86
+ doc = Nokogiri::XML::Document.new
87
+ node = doc.create_element('iq',
88
+ 'id' => Kit.uuid,
89
+ 'to' => recipient.user.jid.to_s,
90
+ 'type' => 'set')
91
+ node << doc.create_element('query', 'xmlns' => NAMESPACES[:roster]) do |query|
92
+ query << to_roster_xml
93
+ end
94
+ recipient.write(node)
95
+ end
96
+
97
+ # Returns this contact as an xmpp <item> element.
98
+ def to_roster_xml
99
+ doc = Nokogiri::XML::Document.new
100
+ doc.create_element('item') do |el|
101
+ el['ask'] = @ask unless @ask.nil? || @ask.empty?
102
+ el['jid'] = @jid.bare.to_s
103
+ el['name'] = @name unless @name.nil? || @name.empty?
104
+ el['subscription'] = @subscription
105
+ @groups.sort!.each do |group|
106
+ el << doc.create_element('group', group)
107
+ end
108
+ end
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,78 @@
1
+ # encoding: UTF-8
2
+
3
+ module Vines
4
+
5
+ # Fork the current process into the background and manage pid
6
+ # files so we can kill the process later.
7
+ class Daemon
8
+
9
+ # Configure a new daemon process. Arguments hash can include the following
10
+ # keys: :pid (pid file name, required),
11
+ # :stdin, :stdout, :stderr (default to /dev/null)
12
+ def initialize(args)
13
+ @pid = args[:pid]
14
+ raise ArgumentError.new('pid file is required') unless @pid
15
+ raise ArgumentError.new('pid must be a file name') if File.directory?(@pid)
16
+ raise ArgumentError.new('pid file must be writable') unless File.writable?(File.dirname(@pid))
17
+ @stdin, @stdout, @stderr = [:stdin, :stdout, :stderr].map {|k| args[k] || '/dev/null' }
18
+ end
19
+
20
+ # Fork the current process into the background to start the
21
+ # daemon. Do nothing if the daemon is already running.
22
+ def start
23
+ daemonize unless running?
24
+ end
25
+
26
+ # Use the pid stored in the pid file created from a previous
27
+ # call to start to send a TERM signal to the process. Do nothing
28
+ # if the daemon is not running.
29
+ def stop
30
+ 10.times do
31
+ break unless running?
32
+ Process.kill('TERM', pid)
33
+ sleep(0.1)
34
+ end
35
+ end
36
+
37
+ # Returns true if the process is running as determined by the numeric
38
+ # pid stored in the pid file created by a previous call to start.
39
+ def running?
40
+ begin
41
+ pid && Process.kill(0, pid)
42
+ rescue Errno::ESRCH
43
+ delete_pid
44
+ false
45
+ rescue Errno::EPERM
46
+ true
47
+ end
48
+ end
49
+
50
+ # Returns the numeric process ID from the pid file.
51
+ # If the pid file does not exist, returns nil.
52
+ def pid
53
+ File.read(@pid).to_i if File.exists?(@pid)
54
+ end
55
+
56
+ private
57
+
58
+ def delete_pid
59
+ File.delete(@pid) if File.exists?(@pid)
60
+ end
61
+
62
+ # Fork process into background twice to release it from
63
+ # the controlling tty. Point open file descriptors shared
64
+ # with the parent process to separate destinations (e.g. /dev/null).
65
+ def daemonize
66
+ exit if fork
67
+ Process.setsid
68
+ exit if fork
69
+ Dir.chdir('/')
70
+ $stdin.reopen(@stdin)
71
+ $stdout.reopen(@stdout, 'a').sync = true
72
+ $stderr.reopen(@stderr, 'a').sync = true
73
+ File.open(@pid, 'w') {|f| f.write(Process.pid) }
74
+ at_exit { delete_pid }
75
+ trap('TERM') { exit }
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,150 @@
1
+ # encoding: UTF-8
2
+
3
+ module Vines
4
+ class XmppError < StandardError
5
+ include Nokogiri::XML
6
+
7
+ # Returns the XML element name based on the exception class name.
8
+ # For example, Vines::BadFormat becomes bad-format.
9
+ def element_name
10
+ name = self.class.name.split('::').last
11
+ name.gsub(/([A-Z])/, '-\1').downcase[1..-1]
12
+ end
13
+ end
14
+
15
+ class SaslError < XmppError
16
+ NAMESPACE = 'urn:ietf:params:xml:ns:xmpp-sasl'.freeze
17
+
18
+ def initialize(text=nil)
19
+ @text = text
20
+ end
21
+
22
+ def to_xml
23
+ doc = Document.new
24
+ doc.create_element('failure') do |node|
25
+ node.add_namespace(nil, NAMESPACE)
26
+ node << doc.create_element(element_name)
27
+ if @text
28
+ node << doc.create_element('text') do |text|
29
+ text['xml:lang'] = 'en'
30
+ text.content = @text
31
+ end
32
+ end
33
+ end.to_xml(:indent => 0).gsub(/\n/, '')
34
+ end
35
+ end
36
+
37
+ class StreamError < XmppError
38
+ NAMESPACE = 'urn:ietf:params:xml:ns:xmpp-streams'.freeze
39
+
40
+ def initialize(text=nil)
41
+ @text = text
42
+ end
43
+
44
+ def to_xml
45
+ doc = Document.new
46
+ doc.create_element('stream:error') do |el|
47
+ el << doc.create_element(element_name, 'xmlns' => NAMESPACE)
48
+ if @text
49
+ el << doc.create_element('text', @text, 'xmlns' => NAMESPACE, 'xml:lang' => 'en')
50
+ end
51
+ end.to_xml(:indent => 0).gsub(/\n/, '')
52
+ end
53
+ end
54
+
55
+ class StanzaError < XmppError
56
+ TYPES = %w[auth cancel continue modify wait].freeze
57
+ KINDS = %w[message presence iq].freeze
58
+ NAMESPACE = 'urn:ietf:params:xml:ns:xmpp-stanzas'.freeze
59
+
60
+ def initialize(el, type, text=nil)
61
+ raise "type must be one of: %s" % TYPES.join(', ') unless TYPES.include?(type)
62
+ raise "stanza must be one of: %s" % KINDS.join(', ') unless KINDS.include?(el.name)
63
+ @stanza_kind, @type, @text = el.name, type, text
64
+ @id, @from, @to = %w[id from to].map {|a| el[a] }
65
+ end
66
+
67
+ def to_xml
68
+ doc = Document.new
69
+ doc.create_element(@stanza_kind) do |el|
70
+ el['from'] = @to if @to
71
+ el['id'] = @id if @id
72
+ el['to'] = @from if @from
73
+ el['type'] = 'error'
74
+ el << doc.create_element('error', 'type' => @type) do |error|
75
+ error << doc.create_element(element_name, 'xmlns' => NAMESPACE)
76
+ if @text
77
+ error << doc.create_element('text', @text, 'xmlns' => NAMESPACE, 'xml:lang' => 'en')
78
+ end
79
+ end
80
+ end.to_xml(:indent => 0).gsub(/\n/, '')
81
+ end
82
+ end
83
+
84
+ module SaslErrors
85
+ class Aborted < SaslError; end
86
+ class AccountDisabled < SaslError; end
87
+ class CredentialsExpired < SaslError; end
88
+ class EncryptionRequired < SaslError; end
89
+ class IncorrectEncoding < SaslError; end
90
+ class InvalidAuthzid < SaslError; end
91
+ class InvalidMechanism < SaslError; end
92
+ class MalformedRequest < SaslError; end
93
+ class MechanismTooWeak < SaslError; end
94
+ class NotAuthorized < SaslError; end
95
+ class TemporaryAuthFailure < SaslError; end
96
+ end
97
+
98
+ module StreamErrors
99
+ class BadFormat < StreamError; end
100
+ class BadNamespacePrefix < StreamError; end
101
+ class Conflict < StreamError; end
102
+ class ConnectionTimeout < StreamError; end
103
+ class HostGone < StreamError; end
104
+ class HostUnknown < StreamError; end
105
+ class ImproperAddressing < StreamError; end
106
+ class InternalServerError < StreamError; end
107
+ class InvalidFrom < StreamError; end
108
+ class InvalidNamespace < StreamError; end
109
+ class InvalidXml < StreamError; end
110
+ class NotAuthorized < StreamError; end
111
+ class NotWellFormed < StreamError; end
112
+ class PolicyViolation < StreamError; end
113
+ class RemoteConnectionFailed < StreamError; end
114
+ class Reset < StreamError; end
115
+ class ResourceConstraint < StreamError; end
116
+ class RestrictedXml < StreamError; end
117
+ class SeeOtherHost < StreamError; end
118
+ class SystemShutdown < StreamError; end
119
+ class UndefinedCondition < StreamError; end
120
+ class UnsupportedEncoding < StreamError; end
121
+ class UnsupportedFeature < StreamError; end
122
+ class UnsupportedStanzaType < StreamError; end
123
+ class UnsupportedVersion < StreamError; end
124
+ end
125
+
126
+ module StanzaErrors
127
+ class BadRequest < StanzaError; end
128
+ class Conflict < StanzaError; end
129
+ class FeatureNotImplemented < StanzaError; end
130
+ class Forbidden < StanzaError; end
131
+ class Gone < StanzaError; end
132
+ class InternalServerError < StanzaError; end
133
+ class ItemNotFound < StanzaError; end
134
+ class JidMalformed < StanzaError; end
135
+ class NotAcceptable < StanzaError; end
136
+ class NotAllowed < StanzaError; end
137
+ class NotAuthorized < StanzaError; end
138
+ class PolicyViolation < StanzaError; end
139
+ class RecipientUnavailable < StanzaError; end
140
+ class Redirect < StanzaError; end
141
+ class RegistrationRequired < StanzaError; end
142
+ class RemoteServerNotFound < StanzaError; end
143
+ class RemoteServerTimeout < StanzaError; end
144
+ class ResourceConstraint < StanzaError; end
145
+ class ServiceUnavailable < StanzaError; end
146
+ class SubscriptionRequired < StanzaError; end
147
+ class UndefinedCondition < StanzaError; end
148
+ class UnexpectedRequest < StanzaError; end
149
+ end
150
+ end