vines 0.1.0
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.
- data/LICENSE +19 -0
- data/README +34 -0
- data/Rakefile +55 -0
- data/bin/vines +95 -0
- data/conf/certs/README +32 -0
- data/conf/certs/ca-bundle.crt +3987 -0
- data/conf/config.rb +114 -0
- data/lib/vines.rb +155 -0
- data/lib/vines/command/bcrypt.rb +12 -0
- data/lib/vines/command/cert.rb +49 -0
- data/lib/vines/command/init.rb +58 -0
- data/lib/vines/command/ldap.rb +35 -0
- data/lib/vines/command/restart.rb +12 -0
- data/lib/vines/command/schema.rb +24 -0
- data/lib/vines/command/start.rb +28 -0
- data/lib/vines/command/stop.rb +18 -0
- data/lib/vines/config.rb +191 -0
- data/lib/vines/contact.rb +99 -0
- data/lib/vines/daemon.rb +78 -0
- data/lib/vines/error.rb +150 -0
- data/lib/vines/jid.rb +56 -0
- data/lib/vines/kit.rb +23 -0
- data/lib/vines/router.rb +125 -0
- data/lib/vines/stanza.rb +55 -0
- data/lib/vines/stanza/iq.rb +50 -0
- data/lib/vines/stanza/iq/auth.rb +18 -0
- data/lib/vines/stanza/iq/disco_info.rb +25 -0
- data/lib/vines/stanza/iq/disco_items.rb +23 -0
- data/lib/vines/stanza/iq/error.rb +16 -0
- data/lib/vines/stanza/iq/ping.rb +16 -0
- data/lib/vines/stanza/iq/query.rb +10 -0
- data/lib/vines/stanza/iq/result.rb +16 -0
- data/lib/vines/stanza/iq/roster.rb +153 -0
- data/lib/vines/stanza/iq/session.rb +22 -0
- data/lib/vines/stanza/iq/vcard.rb +58 -0
- data/lib/vines/stanza/message.rb +41 -0
- data/lib/vines/stanza/presence.rb +119 -0
- data/lib/vines/stanza/presence/error.rb +23 -0
- data/lib/vines/stanza/presence/probe.rb +38 -0
- data/lib/vines/stanza/presence/subscribe.rb +66 -0
- data/lib/vines/stanza/presence/subscribed.rb +64 -0
- data/lib/vines/stanza/presence/unavailable.rb +15 -0
- data/lib/vines/stanza/presence/unsubscribe.rb +57 -0
- data/lib/vines/stanza/presence/unsubscribed.rb +50 -0
- data/lib/vines/storage.rb +216 -0
- data/lib/vines/storage/couchdb.rb +119 -0
- data/lib/vines/storage/ldap.rb +59 -0
- data/lib/vines/storage/local.rb +66 -0
- data/lib/vines/storage/redis.rb +108 -0
- data/lib/vines/storage/sql.rb +174 -0
- data/lib/vines/store.rb +51 -0
- data/lib/vines/stream.rb +198 -0
- data/lib/vines/stream/client.rb +131 -0
- data/lib/vines/stream/client/auth.rb +94 -0
- data/lib/vines/stream/client/auth_restart.rb +33 -0
- data/lib/vines/stream/client/bind.rb +58 -0
- data/lib/vines/stream/client/bind_restart.rb +25 -0
- data/lib/vines/stream/client/closed.rb +13 -0
- data/lib/vines/stream/client/ready.rb +15 -0
- data/lib/vines/stream/client/start.rb +27 -0
- data/lib/vines/stream/client/tls.rb +37 -0
- data/lib/vines/stream/component.rb +53 -0
- data/lib/vines/stream/component/handshake.rb +25 -0
- data/lib/vines/stream/component/ready.rb +24 -0
- data/lib/vines/stream/component/start.rb +19 -0
- data/lib/vines/stream/http.rb +111 -0
- data/lib/vines/stream/http/http_request.rb +22 -0
- data/lib/vines/stream/http/http_state.rb +139 -0
- data/lib/vines/stream/http/http_states.rb +53 -0
- data/lib/vines/stream/parser.rb +78 -0
- data/lib/vines/stream/server.rb +126 -0
- data/lib/vines/stream/server/auth.rb +13 -0
- data/lib/vines/stream/server/auth_restart.rb +19 -0
- data/lib/vines/stream/server/final_restart.rb +20 -0
- data/lib/vines/stream/server/outbound/auth.rb +31 -0
- data/lib/vines/stream/server/outbound/auth_restart.rb +20 -0
- data/lib/vines/stream/server/outbound/auth_result.rb +28 -0
- data/lib/vines/stream/server/outbound/final_features.rb +27 -0
- data/lib/vines/stream/server/outbound/final_restart.rb +20 -0
- data/lib/vines/stream/server/outbound/start.rb +20 -0
- data/lib/vines/stream/server/outbound/tls.rb +30 -0
- data/lib/vines/stream/server/outbound/tls_result.rb +31 -0
- data/lib/vines/stream/server/ready.rb +20 -0
- data/lib/vines/stream/server/start.rb +13 -0
- data/lib/vines/stream/server/tls.rb +13 -0
- data/lib/vines/stream/state.rb +55 -0
- data/lib/vines/token_bucket.rb +46 -0
- data/lib/vines/user.rb +124 -0
- data/lib/vines/version.rb +5 -0
- data/lib/vines/xmpp_server.rb +25 -0
- data/test/config_test.rb +396 -0
- data/test/error_test.rb +59 -0
- data/test/ext/nokogiri.rb +14 -0
- data/test/jid_test.rb +71 -0
- data/test/kit_test.rb +21 -0
- data/test/router_test.rb +60 -0
- data/test/stanza/iq/roster_test.rb +198 -0
- data/test/stanza/iq/session_test.rb +30 -0
- data/test/stanza/iq/vcard_test.rb +159 -0
- data/test/stanza/message_test.rb +124 -0
- data/test/stanza/presence/subscribe_test.rb +75 -0
- data/test/storage/couchdb_test.rb +102 -0
- data/test/storage/ldap_test.rb +207 -0
- data/test/storage/local_test.rb +54 -0
- data/test/storage/redis_test.rb +75 -0
- data/test/storage/sql_test.rb +55 -0
- data/test/storage/storage_tests.rb +134 -0
- data/test/storage_test.rb +90 -0
- data/test/stream/client/auth_test.rb +127 -0
- data/test/stream/client/ready_test.rb +47 -0
- data/test/stream/component/handshake_test.rb +46 -0
- data/test/stream/component/ready_test.rb +105 -0
- data/test/stream/component/start_test.rb +41 -0
- data/test/stream/parser_test.rb +121 -0
- data/test/stream/server/outbound/auth_test.rb +77 -0
- data/test/stream/server/ready_test.rb +100 -0
- data/test/token_bucket_test.rb +24 -0
- data/test/user_test.rb +64 -0
- metadata +318 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# encoding: UTF-8
|
|
2
|
+
|
|
3
|
+
module Vines
|
|
4
|
+
class Stream
|
|
5
|
+
class Http
|
|
6
|
+
class HttpState
|
|
7
|
+
class HttpRequest
|
|
8
|
+
attr_accessor :rid
|
|
9
|
+
|
|
10
|
+
def initialize(rid)
|
|
11
|
+
@rid = rid
|
|
12
|
+
@received = Time.now
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def timed_out?
|
|
16
|
+
Time.now - @received > 55
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
# encoding: UTF-8
|
|
2
|
+
|
|
3
|
+
module Vines
|
|
4
|
+
class Stream
|
|
5
|
+
class Http
|
|
6
|
+
class HttpState
|
|
7
|
+
include Nokogiri::XML
|
|
8
|
+
|
|
9
|
+
attr_reader :domain
|
|
10
|
+
attr_accessor :last_broadcast_presence, :expiration, :domain
|
|
11
|
+
attr_accessor :last_activity, :queued_stanzas, :queued_requests, :user
|
|
12
|
+
|
|
13
|
+
def initialize(stream, sid, rid, domain=nil)
|
|
14
|
+
@stream, @sid, @domain = stream, sid, domain
|
|
15
|
+
@last_activity = Time.now
|
|
16
|
+
@state = Stream::Client::Start.new(self)
|
|
17
|
+
@queued_stanzas = []
|
|
18
|
+
@queued_requests = []
|
|
19
|
+
@expiration = 65
|
|
20
|
+
@pinged = false
|
|
21
|
+
create_session(rid, sid)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def method_missing(method, *args, &block)
|
|
25
|
+
@stream.send(method, *args, &block)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def send_response(data, sid, rid)
|
|
29
|
+
doc = Document.new
|
|
30
|
+
body = doc.create_element('body',
|
|
31
|
+
'rid' => rid,
|
|
32
|
+
'sid' => sid,
|
|
33
|
+
'xmlns' => NAMESPACES[:http_bind]) do |node|
|
|
34
|
+
node.inner_html = data
|
|
35
|
+
end.to_s
|
|
36
|
+
@stream.log_node(body, :out)
|
|
37
|
+
@stream.send_data([create_header(body.bytesize), body].join("\r\n\r\n"))
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def create_header(content_length)
|
|
41
|
+
["HTTP/1.1 200 OK",
|
|
42
|
+
"Content-Type: text/xml; charset=utf-8",
|
|
43
|
+
"Content-Length: #{content_length}"].join("\r\n")
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def expired?
|
|
47
|
+
cleanup_requests
|
|
48
|
+
(Time.now - @last_activity > @expiration) && @queued_requests.empty?
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def cleanup_requests
|
|
52
|
+
timed_out_requests.each do |request|
|
|
53
|
+
log.debug("Clearing out #{request.rid}")
|
|
54
|
+
send_response("", @sid, request.rid)
|
|
55
|
+
@queued_requests.delete(request)
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def ping
|
|
60
|
+
log.debug("Pinging #{self}. Request queue: #{@queued_requests}")
|
|
61
|
+
@last_activity = Time.now
|
|
62
|
+
write("")
|
|
63
|
+
@pinged = true
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def pinged?
|
|
67
|
+
@pinged
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def create_session(rid, sid)
|
|
71
|
+
doc = Document.new
|
|
72
|
+
node = doc.create_element('body',
|
|
73
|
+
'accept' => 'deflate,gzip',
|
|
74
|
+
'ack' => rid,
|
|
75
|
+
'charsets' => 'UTF-8',
|
|
76
|
+
'from' => domain,
|
|
77
|
+
'hold' => '1',
|
|
78
|
+
'inactivity' => '30',
|
|
79
|
+
'maxpause' => '120',
|
|
80
|
+
'polling' => '5',
|
|
81
|
+
'requests' => '2',
|
|
82
|
+
'sid' => sid,
|
|
83
|
+
'ver' => '1.6',
|
|
84
|
+
'wait' => '60',
|
|
85
|
+
'xmlns' => 'http://jabber.org/protocol/httpbind')
|
|
86
|
+
|
|
87
|
+
node << doc.create_element('features', 'xmlns' => 'jabber:client') do |el|
|
|
88
|
+
el << doc.create_element('mechanisms') do |parent|
|
|
89
|
+
parent.default_namespace = NAMESPACES[:sasl]
|
|
90
|
+
mechanisms.each {|name| parent << doc.create_element('mechanism', name) }
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
@stream.send_data([create_header(node.to_s.bytesize), node.to_s].join("\r\n\r\n"))
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def request(rid)
|
|
98
|
+
@pinged = false
|
|
99
|
+
@last_activity = Time.now
|
|
100
|
+
if @queued_stanzas.size > 0
|
|
101
|
+
send_response(@queued_stanzas.join(" "), @sid, rid)
|
|
102
|
+
@queued_stanzas.clear
|
|
103
|
+
else
|
|
104
|
+
@queued_requests << HttpRequest.new(rid)
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def write(node)
|
|
109
|
+
request = @queued_requests.shift
|
|
110
|
+
unless request.nil?
|
|
111
|
+
send_response(node.to_s, @sid, request.rid)
|
|
112
|
+
else
|
|
113
|
+
@queued_stanzas << node.to_s
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def timed_out_requests
|
|
118
|
+
@queued_requests.select {|request| request.timed_out? }
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def mechanisms
|
|
122
|
+
['EXTERNAL', 'PLAIN']
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def handle_restart
|
|
126
|
+
doc = Document.new
|
|
127
|
+
node = doc.create_element('body',
|
|
128
|
+
'xmlns' => NAMESPACES[:http_bind],
|
|
129
|
+
'xmlns:stream' => NAMESPACES[:stream])
|
|
130
|
+
node << doc.create_element('stream:features') do |features|
|
|
131
|
+
features << doc.create_element('bind', 'xmlns' => NAMESPACES[:bind])
|
|
132
|
+
end
|
|
133
|
+
@available = true
|
|
134
|
+
@stream.send_data([create_header(node.to_s.bytesize), node.to_s].join("\r\n\r\n"))
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# encoding: UTF-8
|
|
2
|
+
|
|
3
|
+
module Vines
|
|
4
|
+
class Stream
|
|
5
|
+
class Http
|
|
6
|
+
class HttpStates
|
|
7
|
+
include Vines::Log
|
|
8
|
+
|
|
9
|
+
def start_timer
|
|
10
|
+
@timer ||= EventMachine::PeriodicTimer.new(5) { cleanup }
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def initialize
|
|
14
|
+
@http_states = {}
|
|
15
|
+
start_timer
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def cleanup
|
|
19
|
+
# An expired HTTP client is one that has no queued requests
|
|
20
|
+
# and has no activity in more than 60 seconds
|
|
21
|
+
expired.each do |sid, http_state|
|
|
22
|
+
@http_states.delete(sid)
|
|
23
|
+
log.debug("Removed expired HTTP client #{sid}")
|
|
24
|
+
end
|
|
25
|
+
rescue Exception => e
|
|
26
|
+
log.error("Failed to cleanup HTTP connections: #{e}")
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def []=(sid, http_state)
|
|
30
|
+
@http_states[sid] = http_state
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def [](sid)
|
|
34
|
+
@http_states[sid]
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def connected_http_clients
|
|
38
|
+
@http_states
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def delete
|
|
42
|
+
@http_states
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
private
|
|
46
|
+
|
|
47
|
+
def expired
|
|
48
|
+
@http_states.select {|sid, http_state| http_state.expired? }
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# encoding: UTF-8
|
|
2
|
+
|
|
3
|
+
module Vines
|
|
4
|
+
class Stream
|
|
5
|
+
class Parser < Nokogiri::XML::SAX::Document
|
|
6
|
+
include Nokogiri::XML
|
|
7
|
+
STREAM_NAME = 'stream'.freeze
|
|
8
|
+
STREAM_URI = 'http://etherx.jabber.org/streams'.freeze
|
|
9
|
+
IGNORE = NAMESPACES.values_at(:client, :component, :server)
|
|
10
|
+
|
|
11
|
+
def initialize(&block)
|
|
12
|
+
@listeners, @node = Hash.new {|h, k| h[k] = []}, nil
|
|
13
|
+
@parser = Nokogiri::XML::SAX::PushParser.new(self)
|
|
14
|
+
instance_eval(&block) if block
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
[:stream_open, :stream_close, :stanza].each do |name|
|
|
18
|
+
define_method(name) do |&block|
|
|
19
|
+
@listeners[name] << block
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def <<(data)
|
|
24
|
+
@parser << data
|
|
25
|
+
self
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def start_element_namespace(name, attrs=[], prefix=nil, uri=nil, ns=[])
|
|
29
|
+
el = node(name, attrs, prefix, uri, ns)
|
|
30
|
+
if stream?(name, uri)
|
|
31
|
+
notify(:stream_open, el)
|
|
32
|
+
else
|
|
33
|
+
@node << el if @node
|
|
34
|
+
@node = el
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def end_element_namespace(name, prefix=nil, uri=nil)
|
|
39
|
+
if stream?(name, uri)
|
|
40
|
+
notify(:stream_close)
|
|
41
|
+
elsif @node.parent != @node.document
|
|
42
|
+
@node = @node.parent
|
|
43
|
+
else
|
|
44
|
+
notify(:stanza, @node)
|
|
45
|
+
@node = nil
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def characters(chars)
|
|
50
|
+
@node << Text.new(chars, @node.document) if @node
|
|
51
|
+
end
|
|
52
|
+
alias :cdata_block :characters
|
|
53
|
+
|
|
54
|
+
private
|
|
55
|
+
|
|
56
|
+
def notify(msg, node=nil)
|
|
57
|
+
@listeners[msg].each do |b|
|
|
58
|
+
(node ? b.call(node) : b.call) rescue nil
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def stream?(name, uri)
|
|
63
|
+
name == STREAM_NAME && uri == STREAM_URI
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def node(name, attrs=[], prefix=nil, uri=nil, ns=[])
|
|
67
|
+
ignore = stream?(name, uri) ? [] : IGNORE
|
|
68
|
+
doc = @node ? @node.document : Document.new
|
|
69
|
+
doc.create_element(name) do |node|
|
|
70
|
+
attrs.each {|attr| node[attr.localname] = attr.value }
|
|
71
|
+
ns.each {|prefix, uri| node.add_namespace(prefix, uri) unless ignore.include?(uri) }
|
|
72
|
+
node.namespace = node.add_namespace(prefix, uri) unless ignore.include?(uri)
|
|
73
|
+
doc << node unless @node
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# encoding: UTF-8
|
|
2
|
+
|
|
3
|
+
module Vines
|
|
4
|
+
class Stream
|
|
5
|
+
|
|
6
|
+
# Implements the XMPP protocol for server-to-server (s2s) streams. This
|
|
7
|
+
# serves connected streams using the jabber:server namespace. This handles
|
|
8
|
+
# both accepting incoming s2s streams and initiating outbound s2s streams
|
|
9
|
+
# to other servers.
|
|
10
|
+
class Server < Stream
|
|
11
|
+
|
|
12
|
+
# Starts the connection to the remote server. When the stream is
|
|
13
|
+
# connected and ready to send stanzas it will yield to the callback
|
|
14
|
+
# block. The callback is run on the EventMachine reactor thread. The
|
|
15
|
+
# yielded stream will be nil if the remote connection failed. We need to
|
|
16
|
+
# use a background thread to avoid blocking the server on DNS SRV
|
|
17
|
+
# lookups.
|
|
18
|
+
def self.start(config, to, from, &callback)
|
|
19
|
+
op = proc do
|
|
20
|
+
Resolv::DNS.open do |dns|
|
|
21
|
+
dns.getresources("_xmpp-server._tcp.#{to}", Resolv::DNS::Resource::IN::SRV)
|
|
22
|
+
end.sort! {|a,b| a.priority == b.priority ? b.weight <=> a.weight : a.priority <=> b.priority }
|
|
23
|
+
end
|
|
24
|
+
cb = proc do |srv|
|
|
25
|
+
if srv.empty?
|
|
26
|
+
srv << {:target => to, :port => 5269}
|
|
27
|
+
class << srv.first
|
|
28
|
+
def method_missing(name); self[name]; end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
Server.connect(config, to, from, srv, callback)
|
|
32
|
+
end
|
|
33
|
+
EM.defer(proc { op.call rescue [] }, cb)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def self.connect(config, to, from, srv, callback)
|
|
37
|
+
if srv.empty?
|
|
38
|
+
callback.call(nil)
|
|
39
|
+
else
|
|
40
|
+
begin
|
|
41
|
+
rr = srv.shift
|
|
42
|
+
opts = {:to => to, :from => from, :srv => srv, :callback => callback}
|
|
43
|
+
EM.connect(rr.target.to_s, rr.port, Server, config, opts)
|
|
44
|
+
rescue Exception => e
|
|
45
|
+
connect(config, to, from, srv, callback)
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
attr_reader :config, :domain
|
|
51
|
+
attr_accessor :remote_domain
|
|
52
|
+
|
|
53
|
+
def initialize(config, options={})
|
|
54
|
+
@config = config
|
|
55
|
+
@remote_domain = options[:to]
|
|
56
|
+
@domain = options[:from]
|
|
57
|
+
@srv = options[:srv]
|
|
58
|
+
@callback = options[:callback]
|
|
59
|
+
@outbound = @remote_domain && @domain
|
|
60
|
+
@state = @outbound ? Outbound::Start.new(self) : Start.new(self)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def post_init
|
|
64
|
+
super
|
|
65
|
+
send_stream_header if @outbound
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def max_stanza_size
|
|
69
|
+
@config[:server].max_stanza_size
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def ssl_handshake_completed
|
|
73
|
+
close_connection unless cert_domain_matches?(@remote_domain)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def unbind
|
|
77
|
+
super
|
|
78
|
+
if @outbound && @state.class != Ready
|
|
79
|
+
Server.connect(@config, @remote_domain, @domain, @srv, @callback)
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def vhost?(domain)
|
|
84
|
+
@config.vhost?(domain)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def notify_connected
|
|
88
|
+
if @callback
|
|
89
|
+
@callback.call(self)
|
|
90
|
+
@callback = nil
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def ready?
|
|
95
|
+
@state.class == Server::Ready
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def start(node)
|
|
99
|
+
if @outbound then send_stream_header; return end
|
|
100
|
+
@domain, @remote_domain = %w[to from].map {|a| node[a] }
|
|
101
|
+
send_stream_header
|
|
102
|
+
raise StreamErrors::UnsupportedVersion unless node['version'] == '1.0'
|
|
103
|
+
raise StreamErrors::ImproperAddressing if [@domain, @remote_domain].any? {|addr| (addr || '').strip.empty? }
|
|
104
|
+
raise StreamErrors::HostUnknown unless @config.vhost?(@domain)
|
|
105
|
+
raise StreamErrors::NotAuthorized unless @config.s2s?(@remote_domain)
|
|
106
|
+
raise StreamErrors::InvalidNamespace unless node.namespaces['xmlns'] == NAMESPACES[:server]
|
|
107
|
+
raise StreamErrors::InvalidNamespace unless node.namespaces['xmlns:stream'] == NAMESPACES[:stream]
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
private
|
|
111
|
+
|
|
112
|
+
def send_stream_header
|
|
113
|
+
attrs = {
|
|
114
|
+
'xmlns' => NAMESPACES[:server],
|
|
115
|
+
'xmlns:stream' => NAMESPACES[:stream],
|
|
116
|
+
'xml:lang' => 'en',
|
|
117
|
+
'id' => Kit.uuid,
|
|
118
|
+
'from' => @domain,
|
|
119
|
+
'to' => @remote_domain,
|
|
120
|
+
'version' => '1.0'
|
|
121
|
+
}
|
|
122
|
+
write "<stream:stream %s>" % attrs.to_a.map{|k,v| "#{k}='#{v}'"}.join(' ')
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# encoding: UTF-8
|
|
2
|
+
|
|
3
|
+
module Vines
|
|
4
|
+
class Stream
|
|
5
|
+
class Server
|
|
6
|
+
class AuthRestart < Client::AuthRestart
|
|
7
|
+
def initialize(stream, success=Auth)
|
|
8
|
+
super
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
private
|
|
12
|
+
|
|
13
|
+
def mechanisms
|
|
14
|
+
['EXTERNAL']
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|