vinesmod 0.4.5

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