vines 0.1.1 → 0.2.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/README +2 -2
- data/Rakefile +63 -8
- data/bin/vines +0 -1
- data/conf/config.rb +16 -7
- data/lib/vines.rb +21 -16
- data/lib/vines/command/init.rb +5 -3
- data/lib/vines/config.rb +34 -0
- data/lib/vines/contact.rb +14 -0
- data/lib/vines/stanza.rb +26 -0
- data/lib/vines/stanza/iq.rb +1 -1
- data/lib/vines/stanza/iq/disco_info.rb +3 -0
- data/lib/vines/stanza/iq/private_storage.rb +83 -0
- data/lib/vines/stanza/iq/roster.rb +26 -30
- data/lib/vines/stanza/presence.rb +0 -12
- data/lib/vines/stanza/presence/subscribe.rb +3 -20
- data/lib/vines/stanza/presence/subscribed.rb +9 -10
- data/lib/vines/stanza/presence/unsubscribe.rb +8 -15
- data/lib/vines/stanza/presence/unsubscribed.rb +8 -8
- data/lib/vines/storage.rb +28 -0
- data/lib/vines/storage/couchdb.rb +29 -0
- data/lib/vines/storage/local.rb +22 -0
- data/lib/vines/storage/redis.rb +26 -0
- data/lib/vines/storage/sql.rb +48 -5
- data/lib/vines/stream/client.rb +6 -8
- data/lib/vines/stream/http.rb +23 -21
- data/lib/vines/stream/http/auth.rb +1 -1
- data/lib/vines/stream/http/bind.rb +1 -1
- data/lib/vines/stream/http/bind_restart.rb +4 -3
- data/lib/vines/stream/http/ready.rb +1 -1
- data/lib/vines/stream/http/request.rb +94 -5
- data/lib/vines/stream/http/session.rb +8 -6
- data/lib/vines/version.rb +1 -1
- data/test/config_test.rb +12 -0
- data/test/contact_test.rb +40 -0
- data/test/rake_test_loader.rb +11 -3
- data/test/stanza/iq/private_storage_test.rb +177 -0
- data/test/stanza/iq/roster_test.rb +1 -1
- data/test/stanza/iq_test.rb +63 -0
- data/test/storage/couchdb_test.rb +7 -1
- data/test/storage/local_test.rb +8 -2
- data/test/storage/redis_test.rb +16 -7
- data/test/storage/sql_test.rb +8 -1
- data/test/storage/storage_tests.rb +50 -0
- data/test/stream/http/auth_test.rb +3 -0
- data/test/stream/http/ready_test.rb +3 -0
- data/test/stream/http/request_test.rb +86 -0
- data/test/stream/parser_test.rb +2 -0
- data/web/404.html +43 -0
- data/web/apple-touch-icon.png +0 -0
- data/web/chat/coffeescripts/chat.coffee +385 -0
- data/web/chat/coffeescripts/init.coffee +15 -0
- data/web/chat/coffeescripts/logout.coffee +5 -0
- data/web/chat/index.html +17 -0
- data/web/chat/javascripts/app.js +1 -0
- data/web/chat/javascripts/chat.js +436 -0
- data/web/chat/javascripts/init.js +21 -0
- data/web/chat/javascripts/logout.js +11 -0
- data/web/chat/stylesheets/chat.css +290 -0
- data/web/favicon.png +0 -0
- data/web/lib/coffeescripts/contact.coffee +32 -0
- data/web/lib/coffeescripts/layout.coffee +30 -0
- data/web/lib/coffeescripts/login.coffee +52 -0
- data/web/lib/coffeescripts/navbar.coffee +84 -0
- data/web/lib/coffeescripts/router.coffee +40 -0
- data/web/lib/coffeescripts/session.coffee +211 -0
- data/web/lib/images/default-user.png +0 -0
- data/web/lib/images/logo-large.png +0 -0
- data/web/lib/images/logo-small.png +0 -0
- data/web/lib/javascripts/base.js +9 -0
- data/web/lib/javascripts/contact.js +94 -0
- data/web/lib/javascripts/icons.js +101 -0
- data/web/lib/javascripts/jquery.cookie.js +91 -0
- data/web/lib/javascripts/jquery.js +18 -0
- data/web/lib/javascripts/layout.js +48 -0
- data/web/lib/javascripts/login.js +61 -0
- data/web/lib/javascripts/navbar.js +69 -0
- data/web/lib/javascripts/raphael.js +8 -0
- data/web/lib/javascripts/router.js +105 -0
- data/web/lib/javascripts/session.js +322 -0
- data/web/lib/javascripts/strophe.js +1 -0
- data/web/lib/stylesheets/base.css +223 -0
- data/web/lib/stylesheets/login.css +63 -0
- metadata +51 -9
data/README
CHANGED
@@ -17,8 +17,8 @@ database. SSL encryption is mandatory on all client and server connections.
|
|
17
17
|
== Dependencies
|
18
18
|
|
19
19
|
* bcrypt-ruby >= 2.1.4
|
20
|
-
* eventmachine >=
|
21
|
-
* nokogiri >= 1.
|
20
|
+
* eventmachine >= 0.12.10
|
21
|
+
* nokogiri >= 1.5.0
|
22
22
|
* ruby >= 1.9.2
|
23
23
|
|
24
24
|
== Ubuntu setup
|
data/Rakefile
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
require 'rake'
|
2
2
|
require 'rake/clean'
|
3
|
-
require 'rake/gempackagetask'
|
4
3
|
require 'rake/testtask'
|
4
|
+
require 'rubygems/package_task'
|
5
|
+
require 'nokogiri'
|
5
6
|
require_relative 'lib/vines/version'
|
6
7
|
|
7
8
|
spec = Gem::Specification.new do |s|
|
@@ -21,28 +22,28 @@ all client and server connections."
|
|
21
22
|
s.email = %w[david@negativecode.com chris@negativecode.com]
|
22
23
|
s.homepage = "http://www.getvines.com"
|
23
24
|
|
24
|
-
s.files = FileList['[A-Z]*', '{bin,lib,conf}/**/*']
|
25
|
+
s.files = FileList['[A-Z]*', '{bin,lib,conf,web}/**/*']
|
25
26
|
s.test_files = FileList["test/**/*"]
|
26
27
|
s.executables = %w[vines]
|
27
28
|
s.require_path = "lib"
|
28
29
|
|
29
30
|
s.add_dependency "activerecord", "~> 3.0"
|
30
31
|
s.add_dependency "bcrypt-ruby", "~> 2.1"
|
31
|
-
s.add_dependency
|
32
|
+
s.add_dependency "em-http-request", "~> 0.3"
|
32
33
|
s.add_dependency "em-redis", "~> 0.3"
|
33
|
-
s.add_dependency "eventmachine", "
|
34
|
+
s.add_dependency "eventmachine", "~> 0.12"
|
34
35
|
s.add_dependency "http_parser.rb", "~> 0.5"
|
35
36
|
s.add_dependency "net-ldap", "~> 0.2"
|
36
|
-
s.add_dependency "nokogiri", "~> 1.
|
37
|
+
s.add_dependency "nokogiri", "~> 1.5"
|
37
38
|
|
38
|
-
s.add_development_dependency "minitest"
|
39
|
+
s.add_development_dependency "minitest", "= 2.2.2"
|
39
40
|
s.add_development_dependency "rake"
|
40
41
|
s.add_development_dependency "sqlite3"
|
41
42
|
|
42
43
|
s.required_ruby_version = '>= 1.9.2'
|
43
44
|
end
|
44
45
|
|
45
|
-
|
46
|
+
Gem::PackageTask.new(spec) do |pkg|
|
46
47
|
pkg.need_tar = true
|
47
48
|
end
|
48
49
|
|
@@ -62,4 +63,58 @@ Rake::TestTask.new(:test) do |test|
|
|
62
63
|
test.warning = false
|
63
64
|
end
|
64
65
|
|
65
|
-
|
66
|
+
# Find lib and chat js includes and return them as two arrays.
|
67
|
+
def scripts(doc)
|
68
|
+
lib, chat = [], []
|
69
|
+
doc.css('script').each do |node|
|
70
|
+
file = node['src'].split('/').last()
|
71
|
+
if node['src'].start_with?('/lib')
|
72
|
+
lib << file
|
73
|
+
elsif node['src'].start_with?('/chat')
|
74
|
+
chat << file
|
75
|
+
end
|
76
|
+
end
|
77
|
+
[lib, chat]
|
78
|
+
end
|
79
|
+
|
80
|
+
# Replace script tags with combined and minimized files.
|
81
|
+
def rewrite_js(doc)
|
82
|
+
doc.css('script').each {|node| node.remove }
|
83
|
+
doc.css('head').each do |node|
|
84
|
+
%w[/lib/javascripts/base.js /chat/javascripts/app.js].each do |src|
|
85
|
+
script = doc.create_element('script',
|
86
|
+
'type' => 'text/javascript',
|
87
|
+
'src' => src)
|
88
|
+
node.add_child(script)
|
89
|
+
node.add_child(doc.create_text_node("\n"))
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
task :compile do
|
95
|
+
index = 'web/chat/index.html'
|
96
|
+
doc = Nokogiri::HTML(File.read(index))
|
97
|
+
lib, chat = scripts(doc)
|
98
|
+
|
99
|
+
rewrite_js(doc)
|
100
|
+
# save index.html before rewriting
|
101
|
+
FileUtils.cp(index, '/tmp/index.html')
|
102
|
+
File.open(index, 'w') {|f| f.write(doc.to_xml(:indent => 2)) }
|
103
|
+
|
104
|
+
lib = lib.map {|f| "web/lib/javascripts/#{f}"}.join(' ')
|
105
|
+
chat = chat.map {|f| "web/chat/javascripts/#{f}"}.join(' ')
|
106
|
+
|
107
|
+
sh %{coffee -c -b -o web/chat/javascripts web/chat/coffeescripts/*.coffee}
|
108
|
+
sh %{cat #{chat} | uglifyjs -nc > web/chat/javascripts/app.js}
|
109
|
+
|
110
|
+
sh %{coffee -c -b -o web/lib/javascripts web/lib/coffeescripts/*.coffee}
|
111
|
+
sh %{cat #{lib} | uglifyjs -nc > web/lib/javascripts/base.js}
|
112
|
+
end
|
113
|
+
|
114
|
+
task :cleanup do
|
115
|
+
# move index.html back into place after gem packaging
|
116
|
+
FileUtils.cp('/tmp/index.html', 'web/chat/index.html')
|
117
|
+
File.delete('/tmp/index.html')
|
118
|
+
end
|
119
|
+
|
120
|
+
task :default => [:clobber, :test, :compile, :gem, :cleanup]
|
data/bin/vines
CHANGED
data/conf/config.rb
CHANGED
@@ -19,13 +19,13 @@ Vines::Config.configure do
|
|
19
19
|
# Shared storage example:
|
20
20
|
# host 'verona.lit', 'wonderland.lit' do
|
21
21
|
# storage 'fs' do
|
22
|
-
# dir '
|
22
|
+
# dir 'data/users'
|
23
23
|
# end
|
24
24
|
# end
|
25
25
|
|
26
26
|
host 'wonderland.lit' do
|
27
27
|
storage 'fs' do
|
28
|
-
dir '
|
28
|
+
dir 'data/users'
|
29
29
|
end
|
30
30
|
end
|
31
31
|
|
@@ -36,7 +36,7 @@ Vines::Config.configure do
|
|
36
36
|
#
|
37
37
|
# host 'wonderland.lit' do
|
38
38
|
# storage 'fs' do
|
39
|
-
# dir '
|
39
|
+
# dir 'data/users'
|
40
40
|
# end
|
41
41
|
# ldap 'ldap.wonderland.lit', 636 do
|
42
42
|
# dn 'cn=Directory Manager'
|
@@ -51,7 +51,10 @@ Vines::Config.configure do
|
|
51
51
|
|
52
52
|
# Configure the client-to-server port. The max_resources_per_account attribute
|
53
53
|
# limits how many concurrent connections one user can have to the server.
|
54
|
+
# The private_storage attribute allows clients to store XML fragments
|
55
|
+
# on the server, using the XEP-0049 Private XML Storage feature.
|
54
56
|
client '0.0.0.0', 5222 do
|
57
|
+
private_storage true
|
55
58
|
max_stanza_size 65536
|
56
59
|
max_resources_per_account 5
|
57
60
|
end
|
@@ -65,11 +68,17 @@ Vines::Config.configure do
|
|
65
68
|
hosts []
|
66
69
|
end
|
67
70
|
|
68
|
-
# Configure the
|
69
|
-
#
|
71
|
+
# Configure the built-in HTTP server that serves static files and responds to
|
72
|
+
# XEP-0124 BOSH requests. This allows HTTP clients to connect to
|
73
|
+
# the XMPP server. The root attribute defines the web server's document root.
|
74
|
+
# It will only serve files out of this directory. The bind attribute defines
|
75
|
+
# the URL to which BOSH clients must POST their XMPP stanza requests.
|
70
76
|
http '0.0.0.0', 5280 do
|
71
|
-
|
77
|
+
bind '/xmpp'
|
78
|
+
private_storage true
|
79
|
+
max_stanza_size 65536
|
72
80
|
max_resources_per_account 5
|
81
|
+
root 'web'
|
73
82
|
end
|
74
83
|
|
75
84
|
# Configure the XEP-0114 external component port. Add entries for each
|
@@ -85,7 +94,7 @@ end
|
|
85
94
|
# Available storage implementations:
|
86
95
|
|
87
96
|
#storage 'fs' do
|
88
|
-
# dir '
|
97
|
+
# dir 'data/users'
|
89
98
|
#end
|
90
99
|
|
91
100
|
#storage 'couchdb' do
|
data/lib/vines.rb
CHANGED
@@ -2,22 +2,25 @@
|
|
2
2
|
|
3
3
|
module Vines
|
4
4
|
NAMESPACES = {
|
5
|
-
:stream
|
6
|
-
:client
|
7
|
-
:server
|
8
|
-
:component
|
9
|
-
:roster
|
10
|
-
:non_sasl
|
11
|
-
:
|
12
|
-
:
|
13
|
-
:
|
14
|
-
:
|
15
|
-
:
|
16
|
-
:
|
17
|
-
:
|
18
|
-
:
|
19
|
-
:
|
20
|
-
:
|
5
|
+
:stream => 'http://etherx.jabber.org/streams'.freeze,
|
6
|
+
:client => 'jabber:client'.freeze,
|
7
|
+
:server => 'jabber:server'.freeze,
|
8
|
+
:component => 'jabber:component:accept'.freeze,
|
9
|
+
:roster => 'jabber:iq:roster'.freeze,
|
10
|
+
:non_sasl => 'jabber:iq:auth'.freeze,
|
11
|
+
:storage => 'jabber:iq:private'.freeze,
|
12
|
+
:sasl => 'urn:ietf:params:xml:ns:xmpp-sasl'.freeze,
|
13
|
+
:tls => 'urn:ietf:params:xml:ns:xmpp-tls'.freeze,
|
14
|
+
:bind => 'urn:ietf:params:xml:ns:xmpp-bind'.freeze,
|
15
|
+
:session => 'urn:ietf:params:xml:ns:xmpp-session'.freeze,
|
16
|
+
:ping => 'urn:xmpp:ping'.freeze,
|
17
|
+
:disco_items => 'http://jabber.org/protocol/disco#items'.freeze,
|
18
|
+
:disco_info => 'http://jabber.org/protocol/disco#info'.freeze,
|
19
|
+
:http_bind => 'http://jabber.org/protocol/httpbind'.freeze,
|
20
|
+
:bosh => 'urn:xmpp:xbosh'.freeze,
|
21
|
+
:vcard => 'vcard-temp'.freeze,
|
22
|
+
:si => 'http://jabber.org/protocol/si'.freeze,
|
23
|
+
:byte_streams => 'http://jabber.org/protocol/bytestreams'.freeze
|
21
24
|
}.freeze
|
22
25
|
|
23
26
|
module Log
|
@@ -52,6 +55,7 @@ end
|
|
52
55
|
em-redis
|
53
56
|
eventmachine
|
54
57
|
fiber
|
58
|
+
fileutils
|
55
59
|
http/parser
|
56
60
|
logger
|
57
61
|
net/ldap
|
@@ -70,6 +74,7 @@ end
|
|
70
74
|
vines/stanza/iq/disco_items
|
71
75
|
vines/stanza/iq/error
|
72
76
|
vines/stanza/iq/ping
|
77
|
+
vines/stanza/iq/private_storage
|
73
78
|
vines/stanza/iq/result
|
74
79
|
vines/stanza/iq/roster
|
75
80
|
vines/stanza/iq/session
|
data/lib/vines/command/init.rb
CHANGED
@@ -10,9 +10,11 @@ module Vines
|
|
10
10
|
raise "Directory already initialized: #{domain}" if File.exists?(dir)
|
11
11
|
Dir.mkdir(dir)
|
12
12
|
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
%w[conf web].each do |sub|
|
14
|
+
FileUtils.cp_r(File.expand_path("../../../../#{sub}", __FILE__), dir)
|
15
|
+
end
|
16
|
+
users, log, pid = %w[data/users log pid].map do |sub|
|
17
|
+
File.join(dir, sub).tap {|subdir| FileUtils.makedirs(subdir) }
|
16
18
|
end
|
17
19
|
|
18
20
|
create_users(domain, users)
|
data/lib/vines/config.rb
CHANGED
@@ -144,6 +144,14 @@ module Vines
|
|
144
144
|
@settings[:max_resources_per_account]
|
145
145
|
end
|
146
146
|
end
|
147
|
+
|
148
|
+
def private_storage(enabled)
|
149
|
+
@settings[:private_storage] = !!enabled
|
150
|
+
end
|
151
|
+
|
152
|
+
def private_storage?
|
153
|
+
@settings[:private_storage]
|
154
|
+
end
|
147
155
|
end
|
148
156
|
|
149
157
|
class ServerPort < Port
|
@@ -166,6 +174,8 @@ module Vines
|
|
166
174
|
def initialize(config, host='0.0.0.0', port=5280, &block)
|
167
175
|
@stream = Vines::Stream::Http
|
168
176
|
super(config, host, port, &block)
|
177
|
+
defaults = {:root => File.expand_path('web'), :bind => '/xmpp'}
|
178
|
+
@settings = defaults.merge(@settings)
|
169
179
|
end
|
170
180
|
|
171
181
|
def max_resources_per_account(max=nil)
|
@@ -175,6 +185,30 @@ module Vines
|
|
175
185
|
@settings[:max_resources_per_account]
|
176
186
|
end
|
177
187
|
end
|
188
|
+
|
189
|
+
def private_storage(enabled)
|
190
|
+
@settings[:private_storage] = !!enabled
|
191
|
+
end
|
192
|
+
|
193
|
+
def private_storage?
|
194
|
+
@settings[:private_storage]
|
195
|
+
end
|
196
|
+
|
197
|
+
def root(dir=nil)
|
198
|
+
if dir
|
199
|
+
@settings[:root] = File.expand_path(dir)
|
200
|
+
else
|
201
|
+
@settings[:root]
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
def bind(url=nil)
|
206
|
+
if url
|
207
|
+
@settings[:bind] = url
|
208
|
+
else
|
209
|
+
@settings[:bind]
|
210
|
+
end
|
211
|
+
end
|
178
212
|
end
|
179
213
|
|
180
214
|
class ComponentPort < Port
|
data/lib/vines/contact.rb
CHANGED
@@ -80,6 +80,20 @@ module Vines
|
|
80
80
|
}
|
81
81
|
end
|
82
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
|
+
|
83
97
|
# Returns this contact as an xmpp <item> element.
|
84
98
|
def to_roster_xml
|
85
99
|
doc = Nokogiri::XML::Document.new
|
data/lib/vines/stanza.rb
CHANGED
@@ -55,6 +55,32 @@ module Vines
|
|
55
55
|
raise 'subclass must implement'
|
56
56
|
end
|
57
57
|
|
58
|
+
# Broadcast unavailable presence from the user's available resources to the
|
59
|
+
# recipient's available resources. Route the stanza to a remote server if
|
60
|
+
# the recipient isn't hosted locally.
|
61
|
+
def send_unavailable(from, to)
|
62
|
+
router.available_resources(from).each do |stream|
|
63
|
+
el = unavailable(stream.user.jid, to)
|
64
|
+
if router.local_jid?(to)
|
65
|
+
router.available_resources(to).each do |recipient|
|
66
|
+
recipient.write(el)
|
67
|
+
end
|
68
|
+
else
|
69
|
+
router.route(el)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# Return an unavailable presence stanza addressed to the given JID.
|
75
|
+
def unavailable(from, to)
|
76
|
+
doc = Document.new
|
77
|
+
doc.create_element('presence',
|
78
|
+
'from' => from.to_s,
|
79
|
+
'id' => Kit.uuid,
|
80
|
+
'to' => to.to_s,
|
81
|
+
'type' => 'unavailable')
|
82
|
+
end
|
83
|
+
|
58
84
|
def method_missing(method, *args, &block)
|
59
85
|
@node.send(method, *args, &block)
|
60
86
|
end
|
data/lib/vines/stanza/iq.rb
CHANGED
@@ -15,7 +15,7 @@ module Vines
|
|
15
15
|
|
16
16
|
def process
|
17
17
|
if self['id'] && VALID_TYPES.include?(self['type'])
|
18
|
-
raise StanzaErrors::FeatureNotImplemented.new(@node, 'cancel')
|
18
|
+
route_iq or raise StanzaErrors::FeatureNotImplemented.new(@node, 'cancel')
|
19
19
|
else
|
20
20
|
raise StanzaErrors::BadRequest.new(@node, 'modify')
|
21
21
|
end
|
@@ -15,6 +15,9 @@ module Vines
|
|
15
15
|
query.default_namespace = NS
|
16
16
|
query << el.document.create_element('feature', 'var' => NAMESPACES[:ping])
|
17
17
|
query << el.document.create_element('feature', 'var' => NAMESPACES[:vcard])
|
18
|
+
if stream.private_storage?
|
19
|
+
query << el.document.create_element('feature', 'var' => NAMESPACES[:storage])
|
20
|
+
end
|
18
21
|
end
|
19
22
|
end
|
20
23
|
stream.write(result)
|
@@ -0,0 +1,83 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module Vines
|
4
|
+
class Stanza
|
5
|
+
class Iq
|
6
|
+
# Implements the Private Storage feature defined in XEP-0049. Clients are
|
7
|
+
# allowed to save arbitrary XML documents on the server, identified by
|
8
|
+
# element name and namespace.
|
9
|
+
class PrivateStorage < Query
|
10
|
+
NS = NAMESPACES[:storage]
|
11
|
+
|
12
|
+
register "/iq[@id and (@type='get' or @type='set')]/ns:query", 'ns' => NS
|
13
|
+
|
14
|
+
def process
|
15
|
+
unless stream.private_storage?
|
16
|
+
raise StanzaErrors::ServiceUnavailable.new(self, 'cancel')
|
17
|
+
end
|
18
|
+
validate_to_address
|
19
|
+
validate_children_size
|
20
|
+
validate_namespaces
|
21
|
+
get? ? retrieve_fragment : update_fragment
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def retrieve_fragment
|
27
|
+
found = storage.find_fragment(stream.user.jid, elements.first.elements.first)
|
28
|
+
raise StanzaErrors::ItemNotFound.new(self, 'cancel') unless found
|
29
|
+
|
30
|
+
result = to_result do |node|
|
31
|
+
node << node.document.create_element('query') do |query|
|
32
|
+
query.default_namespace = NS
|
33
|
+
query << found
|
34
|
+
end
|
35
|
+
end
|
36
|
+
stream.write(result)
|
37
|
+
end
|
38
|
+
|
39
|
+
def update_fragment
|
40
|
+
elements.first.elements.each do |node|
|
41
|
+
storage.save_fragment(stream.user.jid, node)
|
42
|
+
end
|
43
|
+
stream.write(to_result)
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def to_result
|
49
|
+
doc = Document.new
|
50
|
+
node = doc.create_element('iq',
|
51
|
+
'from' => stream.user.jid.to_s,
|
52
|
+
'id' => self['id'],
|
53
|
+
'to' => stream.user.jid.to_s,
|
54
|
+
'type' => 'result')
|
55
|
+
yield node if block_given?
|
56
|
+
node
|
57
|
+
end
|
58
|
+
|
59
|
+
def validate_children_size
|
60
|
+
size = elements.first.elements.size
|
61
|
+
if (get? && size != 1) || (set? && size == 0)
|
62
|
+
raise StanzaErrors::NotAcceptable.new(self, 'modify')
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def validate_to_address
|
67
|
+
to = (self['to'] || '').strip
|
68
|
+
unless to.empty? || to == stream.user.jid.bare.to_s
|
69
|
+
raise StanzaErrors::Forbidden.new(self, 'cancel')
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def validate_namespaces
|
74
|
+
elements.first.elements.each do |node|
|
75
|
+
if node.namespace.nil? || NAMESPACES.values.include?(node.namespace.href)
|
76
|
+
raise StanzaErrors::NotAcceptable.new(self, 'modify')
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|