vines 0.4.5 → 0.4.6
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/Gemfile +3 -0
 - data/README.md +48 -0
 - data/Rakefile +6 -58
 - data/bin/vines +12 -2
 - data/conf/certs/ca-bundle.crt +568 -39
 - data/conf/config.rb +9 -42
 - data/lib/vines.rb +1 -8
 - data/lib/vines/command/cert.rb +4 -4
 - data/lib/vines/command/init.rb +3 -3
 - data/lib/vines/config.rb +9 -0
 - data/lib/vines/kit.rb +2 -7
 - data/lib/vines/storage/local.rb +50 -17
 - data/lib/vines/store.rb +6 -4
 - data/lib/vines/stream.rb +1 -1
 - data/lib/vines/stream/http/session.rb +0 -0
 - data/lib/vines/stream/http/sessions.rb +0 -0
 - data/lib/vines/stream/parser.rb +3 -2
 - data/lib/vines/token_bucket.rb +19 -10
 - data/lib/vines/version.rb +1 -1
 - data/test/cluster/publisher_test.rb +45 -33
 - data/test/cluster/sessions_test.rb +32 -39
 - data/test/cluster/subscriber_test.rb +93 -78
 - data/test/config/host_test.rb +2 -4
 - data/test/config/pubsub_test.rb +132 -126
 - data/test/config_test.rb +2 -4
 - data/test/contact_test.rb +80 -66
 - data/test/error_test.rb +54 -55
 - data/test/jid_test.rb +1 -2
 - data/test/kit_test.rb +22 -17
 - data/test/router_test.rb +187 -146
 - data/test/stanza/iq/disco_info_test.rb +59 -59
 - data/test/stanza/iq/disco_items_test.rb +36 -34
 - data/test/stanza/iq/private_storage_test.rb +138 -143
 - data/test/stanza/iq/roster_test.rb +198 -175
 - data/test/stanza/iq/session_test.rb +17 -18
 - data/test/stanza/iq/vcard_test.rb +117 -116
 - data/test/stanza/iq/version_test.rb +47 -46
 - data/test/stanza/iq_test.rb +53 -49
 - data/test/stanza/message_test.rb +92 -89
 - data/test/stanza/presence/probe_test.rb +2 -5
 - data/test/stanza/presence/subscribe_test.rb +67 -54
 - data/test/stanza/pubsub/create_test.rb +86 -108
 - data/test/stanza/pubsub/delete_test.rb +141 -114
 - data/test/stanza/pubsub/publish_test.rb +256 -320
 - data/test/stanza/pubsub/subscribe_test.rb +169 -150
 - data/test/stanza/pubsub/unsubscribe_test.rb +111 -142
 - data/test/stanza_test.rb +61 -54
 - data/test/storage/ldap_test.rb +1 -2
 - data/test/storage/local_test.rb +3 -5
 - data/test/storage/null_test.rb +3 -4
 - data/test/storage/storage_tests.rb +1 -3
 - data/test/storage_test.rb +1 -2
 - data/test/store_test.rb +1 -2
 - data/test/stream/client/auth_test.rb +61 -63
 - data/test/stream/client/ready_test.rb +7 -8
 - data/test/stream/client/session_test.rb +19 -18
 - data/test/stream/component/handshake_test.rb +40 -37
 - data/test/stream/component/ready_test.rb +76 -61
 - data/test/stream/component/start_test.rb +7 -8
 - data/test/stream/http/auth_test.rb +3 -4
 - data/test/stream/http/ready_test.rb +52 -60
 - data/test/stream/http/request_test.rb +1 -3
 - data/test/stream/http/sessions_test.rb +2 -3
 - data/test/stream/http/start_test.rb +3 -4
 - data/test/stream/parser_test.rb +3 -4
 - data/test/stream/sasl_test.rb +105 -86
 - data/test/stream/server/auth_test.rb +40 -36
 - data/test/stream/server/outbound/auth_test.rb +3 -4
 - data/test/stream/server/ready_test.rb +51 -51
 - data/test/test_helper.rb +42 -0
 - data/test/token_bucket_test.rb +38 -18
 - data/test/user_test.rb +79 -49
 - data/vines.gemspec +33 -0
 - data/web/chat/javascripts/app.js +1 -1
 - data/web/lib/coffeescripts/layout.coffee +1 -1
 - data/web/lib/javascripts/base.js +10 -10
 - data/web/lib/javascripts/jquery.js +4 -4
 - metadata +31 -128
 - data/README +0 -35
 - data/lib/vines/storage/couchdb.rb +0 -129
 - data/lib/vines/storage/mongodb.rb +0 -132
 - data/lib/vines/storage/redis.rb +0 -127
 - data/lib/vines/storage/sql.rb +0 -220
 - data/test/rake_test_loader.rb +0 -17
 - data/test/storage/couchdb_test.rb +0 -107
 - data/test/storage/mock_mongo.rb +0 -40
 - data/test/storage/mongodb_test.rb +0 -81
 - data/test/storage/redis_test.rb +0 -51
 - data/test/storage/sql_test.rb +0 -62
 
    
        data/conf/config.rb
    CHANGED
    
    | 
         @@ -8,6 +8,11 @@ Vines::Config.configure do 
     | 
|
| 
       8 
8 
     | 
    
         
             
              # level logs all XML sent and received by the server.
         
     | 
| 
       9 
9 
     | 
    
         
             
              log :info
         
     | 
| 
       10 
10 
     | 
    
         | 
| 
      
 11 
     | 
    
         
            +
              # Set the directory in which to look for virtual hosts' TLS certificates.
         
     | 
| 
      
 12 
     | 
    
         
            +
              # This is optional and defaults to the conf/certs directory created during
         
     | 
| 
      
 13 
     | 
    
         
            +
              # `vines init`.
         
     | 
| 
      
 14 
     | 
    
         
            +
              certs 'conf/certs'
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
       11 
16 
     | 
    
         
             
              # Each host element below is a virtual host domain name that this server will
         
     | 
| 
       12 
17 
     | 
    
         
             
              # service. Hosts can share storage configurations or use separate databases.
         
     | 
| 
       13 
18 
     | 
    
         
             
              # TLS encryption is mandatory so each host must have a <domain>.crt and
         
     | 
| 
         @@ -16,6 +21,10 @@ Vines::Config.configure do 
     | 
|
| 
       16 
21 
     | 
    
         
             
              # command. Change the example, 'wonderland.lit', domain name to your actual
         
     | 
| 
       17 
22 
     | 
    
         
             
              # domain.
         
     | 
| 
       18 
23 
     | 
    
         
             
              #
         
     | 
| 
      
 24 
     | 
    
         
            +
              # The vines gem is distributed with a single 'fs' filesystem storage backend.
         
     | 
| 
      
 25 
     | 
    
         
            +
              # Additional database support is provided by the vines-sql, vines-redis,
         
     | 
| 
      
 26 
     | 
    
         
            +
              # vines-couchdb, and vines-mongodb gems.
         
     | 
| 
      
 27 
     | 
    
         
            +
              #
         
     | 
| 
       19 
28 
     | 
    
         
             
              # The private_storage attribute allows clients to store XML fragments
         
     | 
| 
       20 
29 
     | 
    
         
             
              # on the server, using the XEP-0049 Private XML Storage feature.
         
     | 
| 
       21 
30 
     | 
    
         
             
              #
         
     | 
| 
         @@ -125,45 +134,3 @@ Vines::Config.configure do 
     | 
|
| 
       125 
134 
     | 
    
         
             
              #  password ''
         
     | 
| 
       126 
135 
     | 
    
         
             
              #end
         
     | 
| 
       127 
136 
     | 
    
         
             
            end
         
     | 
| 
       128 
     | 
    
         
            -
             
     | 
| 
       129 
     | 
    
         
            -
            # Available storage implementations:
         
     | 
| 
       130 
     | 
    
         
            -
             
     | 
| 
       131 
     | 
    
         
            -
            #storage 'fs' do
         
     | 
| 
       132 
     | 
    
         
            -
            #  dir 'data'
         
     | 
| 
       133 
     | 
    
         
            -
            #end
         
     | 
| 
       134 
     | 
    
         
            -
             
     | 
| 
       135 
     | 
    
         
            -
            #storage 'couchdb' do
         
     | 
| 
       136 
     | 
    
         
            -
            #  host 'localhost'
         
     | 
| 
       137 
     | 
    
         
            -
            #  port 6984
         
     | 
| 
       138 
     | 
    
         
            -
            #  database 'xmpp'
         
     | 
| 
       139 
     | 
    
         
            -
            #  tls true
         
     | 
| 
       140 
     | 
    
         
            -
            #  username ''
         
     | 
| 
       141 
     | 
    
         
            -
            #  password ''
         
     | 
| 
       142 
     | 
    
         
            -
            #end
         
     | 
| 
       143 
     | 
    
         
            -
             
     | 
| 
       144 
     | 
    
         
            -
            #storage 'mongodb' do
         
     | 
| 
       145 
     | 
    
         
            -
            #  host 'localhost', 27017
         
     | 
| 
       146 
     | 
    
         
            -
            #  host 'localhost', 27018 # optional, connects to replica set
         
     | 
| 
       147 
     | 
    
         
            -
            #  database 'xmpp'
         
     | 
| 
       148 
     | 
    
         
            -
            #  tls true
         
     | 
| 
       149 
     | 
    
         
            -
            #  username ''
         
     | 
| 
       150 
     | 
    
         
            -
            #  password ''
         
     | 
| 
       151 
     | 
    
         
            -
            #  pool 5
         
     | 
| 
       152 
     | 
    
         
            -
            #end
         
     | 
| 
       153 
     | 
    
         
            -
             
     | 
| 
       154 
     | 
    
         
            -
            #storage 'redis' do
         
     | 
| 
       155 
     | 
    
         
            -
            #  host 'localhost'
         
     | 
| 
       156 
     | 
    
         
            -
            #  port 6379
         
     | 
| 
       157 
     | 
    
         
            -
            #  database 0
         
     | 
| 
       158 
     | 
    
         
            -
            #  password ''
         
     | 
| 
       159 
     | 
    
         
            -
            #end
         
     | 
| 
       160 
     | 
    
         
            -
             
     | 
| 
       161 
     | 
    
         
            -
            #storage 'sql' do
         
     | 
| 
       162 
     | 
    
         
            -
            #  adapter 'postgresql'
         
     | 
| 
       163 
     | 
    
         
            -
            #  host 'localhost'
         
     | 
| 
       164 
     | 
    
         
            -
            #  port 5432
         
     | 
| 
       165 
     | 
    
         
            -
            #  database 'xmpp'
         
     | 
| 
       166 
     | 
    
         
            -
            #  username ''
         
     | 
| 
       167 
     | 
    
         
            -
            #  password ''
         
     | 
| 
       168 
     | 
    
         
            -
            #  pool 5
         
     | 
| 
       169 
     | 
    
         
            -
            #end
         
     | 
    
        data/lib/vines.rb
    CHANGED
    
    | 
         @@ -55,25 +55,22 @@ module Vines 
     | 
|
| 
       55 
55 
     | 
    
         
             
            end
         
     | 
| 
       56 
56 
     | 
    
         | 
| 
       57 
57 
     | 
    
         
             
            %w[
         
     | 
| 
       58 
     | 
    
         
            -
              active_record
         
     | 
| 
       59 
58 
     | 
    
         
             
              base64
         
     | 
| 
       60 
59 
     | 
    
         
             
              bcrypt
         
     | 
| 
       61 
60 
     | 
    
         
             
              digest/sha1
         
     | 
| 
       62 
     | 
    
         
            -
              em-http
         
     | 
| 
       63 
61 
     | 
    
         
             
              em-hiredis
         
     | 
| 
       64 
62 
     | 
    
         
             
              eventmachine
         
     | 
| 
       65 
63 
     | 
    
         
             
              fiber
         
     | 
| 
       66 
64 
     | 
    
         
             
              fileutils
         
     | 
| 
       67 
65 
     | 
    
         
             
              http/parser
         
     | 
| 
      
 66 
     | 
    
         
            +
              json
         
     | 
| 
       68 
67 
     | 
    
         
             
              logger
         
     | 
| 
       69 
     | 
    
         
            -
              mongo
         
     | 
| 
       70 
68 
     | 
    
         
             
              net/ldap
         
     | 
| 
       71 
69 
     | 
    
         
             
              nokogiri
         
     | 
| 
       72 
70 
     | 
    
         
             
              openssl
         
     | 
| 
       73 
71 
     | 
    
         
             
              resolv
         
     | 
| 
       74 
72 
     | 
    
         
             
              set
         
     | 
| 
       75 
73 
     | 
    
         
             
              socket
         
     | 
| 
       76 
     | 
    
         
            -
              uri
         
     | 
| 
       77 
74 
     | 
    
         
             
              yaml
         
     | 
| 
       78 
75 
     | 
    
         | 
| 
       79 
76 
     | 
    
         
             
              vines/log
         
     | 
| 
         @@ -110,13 +107,9 @@ end 
     | 
|
| 
       110 
107 
     | 
    
         
             
              vines/stanza/pubsub/unsubscribe
         
     | 
| 
       111 
108 
     | 
    
         | 
| 
       112 
109 
     | 
    
         
             
              vines/storage
         
     | 
| 
       113 
     | 
    
         
            -
              vines/storage/couchdb
         
     | 
| 
       114 
110 
     | 
    
         
             
              vines/storage/ldap
         
     | 
| 
       115 
111 
     | 
    
         
             
              vines/storage/local
         
     | 
| 
       116 
     | 
    
         
            -
              vines/storage/mongodb
         
     | 
| 
       117 
112 
     | 
    
         
             
              vines/storage/null
         
     | 
| 
       118 
     | 
    
         
            -
              vines/storage/redis
         
     | 
| 
       119 
     | 
    
         
            -
              vines/storage/sql
         
     | 
| 
       120 
113 
     | 
    
         | 
| 
       121 
114 
     | 
    
         
             
              vines/config
         
     | 
| 
       122 
115 
     | 
    
         
             
              vines/config/host
         
     | 
    
        data/lib/vines/command/cert.rb
    CHANGED
    
    | 
         @@ -5,8 +5,8 @@ module Vines 
     | 
|
| 
       5 
5 
     | 
    
         
             
                class Cert
         
     | 
| 
       6 
6 
     | 
    
         
             
                  def run(opts)
         
     | 
| 
       7 
7 
     | 
    
         
             
                    raise 'vines cert <domain>' unless opts[:args].size == 1
         
     | 
| 
       8 
     | 
    
         
            -
                     
     | 
| 
       9 
     | 
    
         
            -
                    create_cert(opts[:args].first,  
     | 
| 
      
 8 
     | 
    
         
            +
                    require opts[:config]
         
     | 
| 
      
 9 
     | 
    
         
            +
                    create_cert(opts[:args].first, Config.instance.certs)
         
     | 
| 
       10 
10 
     | 
    
         
             
                  end
         
     | 
| 
       11 
11 
     | 
    
         | 
| 
       12 
12 
     | 
    
         
             
                  def create_cert(domain, dir)
         
     | 
| 
         @@ -33,9 +33,9 @@ module Vines 
     | 
|
| 
       33 
33 
     | 
    
         | 
| 
       34 
34 
     | 
    
         
             
                    cert.sign(key, OpenSSL::Digest::SHA1.new)
         
     | 
| 
       35 
35 
     | 
    
         | 
| 
       36 
     | 
    
         
            -
                    {'key' => key, 'crt' => cert}.each_pair do |ext, o| 
     | 
| 
      
 36 
     | 
    
         
            +
                    {'key' => key, 'crt' => cert}.each_pair do |ext, o|
         
     | 
| 
       37 
37 
     | 
    
         
             
                      name = File.join(dir, "#{domain}.#{ext}")
         
     | 
| 
       38 
     | 
    
         
            -
                      File.open(name,  
     | 
| 
      
 38 
     | 
    
         
            +
                      File.open(name, 'w:utf-8') {|f| f.write(o.to_pem) }
         
     | 
| 
       39 
39 
     | 
    
         
             
                      File.chmod(0600, name) if ext == 'key'
         
     | 
| 
       40 
40 
     | 
    
         
             
                    end
         
     | 
| 
       41 
41 
     | 
    
         
             
                  end
         
     | 
    
        data/lib/vines/command/init.rb
    CHANGED
    
    | 
         @@ -36,8 +36,8 @@ module Vines 
     | 
|
| 
       36 
36 
     | 
    
         | 
| 
       37 
37 
     | 
    
         
             
                  def update_config(domain, dir)
         
     | 
| 
       38 
38 
     | 
    
         
             
                    config = File.expand_path('conf/config.rb', dir)
         
     | 
| 
       39 
     | 
    
         
            -
                    text = File.read(config)
         
     | 
| 
       40 
     | 
    
         
            -
                    File.open(config, 'w') do |f|
         
     | 
| 
      
 39 
     | 
    
         
            +
                    text = File.read(config, encoding: 'utf-8')
         
     | 
| 
      
 40 
     | 
    
         
            +
                    File.open(config, 'w:utf-8') do |f|
         
     | 
| 
       41 
41 
     | 
    
         
             
                      f.write(text.gsub('wonderland.lit', domain))
         
     | 
| 
       42 
42 
     | 
    
         
             
                    end
         
     | 
| 
       43 
43 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -65,4 +65,4 @@ module Vines 
     | 
|
| 
       65 
65 
     | 
    
         
             
                  end
         
     | 
| 
       66 
66 
     | 
    
         
             
                end
         
     | 
| 
       67 
67 
     | 
    
         
             
              end
         
     | 
| 
       68 
     | 
    
         
            -
            end
         
     | 
| 
      
 68 
     | 
    
         
            +
            end
         
     | 
    
        data/lib/vines/config.rb
    CHANGED
    
    | 
         @@ -21,11 +21,20 @@ module Vines 
     | 
|
| 
       21 
21 
     | 
    
         
             
                end
         
     | 
| 
       22 
22 
     | 
    
         | 
| 
       23 
23 
     | 
    
         
             
                def initialize(&block)
         
     | 
| 
      
 24 
     | 
    
         
            +
                  @certs = File.expand_path('conf/certs')
         
     | 
| 
       24 
25 
     | 
    
         
             
                  @vhosts, @ports, @cluster = {}, {}, nil
         
     | 
| 
       25 
26 
     | 
    
         
             
                  @null = Storage::Null.new
         
     | 
| 
       26 
27 
     | 
    
         
             
                  @router = Router.new(self)
         
     | 
| 
       27 
28 
     | 
    
         
             
                  instance_eval(&block)
         
     | 
| 
       28 
29 
     | 
    
         
             
                  raise "must define at least one virtual host" if @vhosts.empty?
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
                  unless @certs && File.directory?(@certs) && File.readable?(@certs)
         
     | 
| 
      
 32 
     | 
    
         
            +
                    raise 'Must provide a readable certs directory'
         
     | 
| 
      
 33 
     | 
    
         
            +
                  end
         
     | 
| 
      
 34 
     | 
    
         
            +
                end
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
                def certs(dir=nil)
         
     | 
| 
      
 37 
     | 
    
         
            +
                  dir ? @certs = File.expand_path(dir) : @certs
         
     | 
| 
       29 
38 
     | 
    
         
             
                end
         
     | 
| 
       30 
39 
     | 
    
         | 
| 
       31 
40 
     | 
    
         
             
                def host(*names, &block)
         
     | 
    
        data/lib/vines/kit.rb
    CHANGED
    
    | 
         @@ -12,17 +12,12 @@ module Vines 
     | 
|
| 
       12 
12 
     | 
    
         
             
                # Generates a random uuid per rfc 4122 that's useful for including in
         
     | 
| 
       13 
13 
     | 
    
         
             
                # stream, iq, and other xmpp stanzas.
         
     | 
| 
       14 
14 
     | 
    
         
             
                def self.uuid
         
     | 
| 
       15 
     | 
    
         
            -
                   
     | 
| 
       16 
     | 
    
         
            -
                  hex[12] = '4'
         
     | 
| 
       17 
     | 
    
         
            -
                  hex[16] = %w[8 9 a b][rand(4)]
         
     | 
| 
       18 
     | 
    
         
            -
                  hex.scan(/(\w{8})(\w{4})(\w{4})(\w{4})(\w{12})/).first.join('-')
         
     | 
| 
      
 15 
     | 
    
         
            +
                  SecureRandom.uuid
         
     | 
| 
       19 
16 
     | 
    
         
             
                end
         
     | 
| 
       20 
17 
     | 
    
         | 
| 
       21 
18 
     | 
    
         
             
                # Generates a random 128 character authentication token.
         
     | 
| 
       22 
19 
     | 
    
         
             
                def self.auth_token
         
     | 
| 
       23 
     | 
    
         
            -
                   
     | 
| 
       24 
     | 
    
         
            -
                  1024.times { hash << rand.to_s }
         
     | 
| 
       25 
     | 
    
         
            -
                  hash.hexdigest
         
     | 
| 
      
 20 
     | 
    
         
            +
                  SecureRandom.hex(64)
         
     | 
| 
       26 
21 
     | 
    
         
             
                end
         
     | 
| 
       27 
22 
     | 
    
         
             
              end
         
     | 
| 
       28 
23 
     | 
    
         
             
            end
         
     | 
    
        data/lib/vines/storage/local.rb
    CHANGED
    
    | 
         @@ -27,8 +27,8 @@ module Vines 
     | 
|
| 
       27 
27 
     | 
    
         | 
| 
       28 
28 
     | 
    
         
             
                  def find_user(jid)
         
     | 
| 
       29 
29 
     | 
    
         
             
                    jid = JID.new(jid).bare.to_s
         
     | 
| 
       30 
     | 
    
         
            -
                    file =  
     | 
| 
       31 
     | 
    
         
            -
                    record = YAML. 
     | 
| 
      
 30 
     | 
    
         
            +
                    file = "user/#{jid}" unless jid.empty?
         
     | 
| 
      
 31 
     | 
    
         
            +
                    record = YAML.load(read(file)) rescue nil
         
     | 
| 
       32 
32 
     | 
    
         
             
                    return User.new(jid: jid).tap do |user|
         
     | 
| 
       33 
33 
     | 
    
         
             
                      user.name, user.password = record.values_at('name', 'password')
         
     | 
| 
       34 
34 
     | 
    
         
             
                      (record['roster'] || {}).each_pair do |jid, props|
         
     | 
| 
         @@ -47,43 +47,48 @@ module Vines 
     | 
|
| 
       47 
47 
     | 
    
         
             
                    user.roster.each do |contact|
         
     | 
| 
       48 
48 
     | 
    
         
             
                      record['roster'][contact.jid.bare.to_s] = contact.to_h
         
     | 
| 
       49 
49 
     | 
    
         
             
                    end
         
     | 
| 
       50 
     | 
    
         
            -
                    save("user/#{user.jid.bare}") 
     | 
| 
       51 
     | 
    
         
            -
                      YAML.dump(record, f)
         
     | 
| 
       52 
     | 
    
         
            -
                    end
         
     | 
| 
      
 50 
     | 
    
         
            +
                    save("user/#{user.jid.bare}", YAML.dump(record))
         
     | 
| 
       53 
51 
     | 
    
         
             
                  end
         
     | 
| 
       54 
52 
     | 
    
         | 
| 
       55 
53 
     | 
    
         
             
                  def find_vcard(jid)
         
     | 
| 
       56 
54 
     | 
    
         
             
                    jid = JID.new(jid).bare.to_s
         
     | 
| 
       57 
55 
     | 
    
         
             
                    return if jid.empty?
         
     | 
| 
       58 
     | 
    
         
            -
                    file =  
     | 
| 
       59 
     | 
    
         
            -
                    Nokogiri::XML( 
     | 
| 
      
 56 
     | 
    
         
            +
                    file = "vcard/#{jid}"
         
     | 
| 
      
 57 
     | 
    
         
            +
                    Nokogiri::XML(read(file)).root rescue nil
         
     | 
| 
       60 
58 
     | 
    
         
             
                  end
         
     | 
| 
       61 
59 
     | 
    
         | 
| 
       62 
60 
     | 
    
         
             
                  def save_vcard(jid, card)
         
     | 
| 
       63 
61 
     | 
    
         
             
                    jid = JID.new(jid).bare.to_s
         
     | 
| 
       64 
62 
     | 
    
         
             
                    return if jid.empty?
         
     | 
| 
       65 
     | 
    
         
            -
                    save("vcard/#{jid}") 
     | 
| 
       66 
     | 
    
         
            -
                      f.write(card.to_xml)
         
     | 
| 
       67 
     | 
    
         
            -
                    end
         
     | 
| 
      
 63 
     | 
    
         
            +
                    save("vcard/#{jid}", card.to_xml)
         
     | 
| 
       68 
64 
     | 
    
         
             
                  end
         
     | 
| 
       69 
65 
     | 
    
         | 
| 
       70 
66 
     | 
    
         
             
                  def find_fragment(jid, node)
         
     | 
| 
       71 
67 
     | 
    
         
             
                    jid = JID.new(jid).bare.to_s
         
     | 
| 
       72 
68 
     | 
    
         
             
                    return if jid.empty?
         
     | 
| 
       73 
     | 
    
         
            -
                    file =  
     | 
| 
       74 
     | 
    
         
            -
                    Nokogiri::XML( 
     | 
| 
      
 69 
     | 
    
         
            +
                    file = 'fragment/%s' % fragment_id(jid, node)
         
     | 
| 
      
 70 
     | 
    
         
            +
                    Nokogiri::XML(read(file)).root rescue nil
         
     | 
| 
       75 
71 
     | 
    
         
             
                  end
         
     | 
| 
       76 
72 
     | 
    
         | 
| 
       77 
73 
     | 
    
         
             
                  def save_fragment(jid, node)
         
     | 
| 
       78 
74 
     | 
    
         
             
                    jid = JID.new(jid).bare.to_s
         
     | 
| 
       79 
75 
     | 
    
         
             
                    return if jid.empty?
         
     | 
| 
       80 
     | 
    
         
            -
                     
     | 
| 
       81 
     | 
    
         
            -
             
     | 
| 
       82 
     | 
    
         
            -
                    end
         
     | 
| 
      
 76 
     | 
    
         
            +
                    file = 'fragment/%s' % fragment_id(jid, node)
         
     | 
| 
      
 77 
     | 
    
         
            +
                    save(file, node.to_xml)
         
     | 
| 
       83 
78 
     | 
    
         
             
                  end
         
     | 
| 
       84 
79 
     | 
    
         | 
| 
       85 
80 
     | 
    
         
             
                  private
         
     | 
| 
       86 
81 
     | 
    
         | 
| 
      
 82 
     | 
    
         
            +
                  # Resolves a relative file name into an absolute path inside the
         
     | 
| 
      
 83 
     | 
    
         
            +
                  # storage directory.
         
     | 
| 
      
 84 
     | 
    
         
            +
                  #
         
     | 
| 
      
 85 
     | 
    
         
            +
                  # file - A fully-qualified or relative file name String.
         
     | 
| 
      
 86 
     | 
    
         
            +
                  #
         
     | 
| 
      
 87 
     | 
    
         
            +
                  # Returns the fully-qualified file path String.
         
     | 
| 
      
 88 
     | 
    
         
            +
                  #
         
     | 
| 
      
 89 
     | 
    
         
            +
                  # Raises RuntimeError if the resolved path is outside of the storage
         
     | 
| 
      
 90 
     | 
    
         
            +
                  # directory. This prevents directory path traversals with maliciously
         
     | 
| 
      
 91 
     | 
    
         
            +
                  # crafted JIDs.
         
     | 
| 
       87 
92 
     | 
    
         
             
                  def absolute_path(file)
         
     | 
| 
       88 
93 
     | 
    
         
             
                    File.expand_path(file, @dir).tap do |absolute|
         
     | 
| 
       89 
94 
     | 
    
         
             
                      parent = File.dirname(File.dirname(absolute))
         
     | 
| 
         @@ -91,12 +96,40 @@ module Vines 
     | 
|
| 
       91 
96 
     | 
    
         
             
                    end
         
     | 
| 
       92 
97 
     | 
    
         
             
                  end
         
     | 
| 
       93 
98 
     | 
    
         | 
| 
       94 
     | 
    
         
            -
                   
     | 
| 
      
 99 
     | 
    
         
            +
                  # Read the file from the filesystem and return its contents as a String.
         
     | 
| 
      
 100 
     | 
    
         
            +
                  # All files are assumed to be encoded as UTF-8.
         
     | 
| 
      
 101 
     | 
    
         
            +
                  #
         
     | 
| 
      
 102 
     | 
    
         
            +
                  # file - A fully-qualified or relative file name String.
         
     | 
| 
      
 103 
     | 
    
         
            +
                  #
         
     | 
| 
      
 104 
     | 
    
         
            +
                  # Returns the file content as a UTF-8 encoded String.
         
     | 
| 
      
 105 
     | 
    
         
            +
                  def read(file)
         
     | 
| 
      
 106 
     | 
    
         
            +
                    file = absolute_path(file)
         
     | 
| 
      
 107 
     | 
    
         
            +
                    File.read(file, encoding: 'utf-8')
         
     | 
| 
      
 108 
     | 
    
         
            +
                  end
         
     | 
| 
      
 109 
     | 
    
         
            +
             
     | 
| 
      
 110 
     | 
    
         
            +
                  # Write the content to the file. Make sure to consistently encode files
         
     | 
| 
      
 111 
     | 
    
         
            +
                  # we read and write as UTF-8.
         
     | 
| 
      
 112 
     | 
    
         
            +
                  #
         
     | 
| 
      
 113 
     | 
    
         
            +
                  # file    - A fully-qualified or relative file name String.
         
     | 
| 
      
 114 
     | 
    
         
            +
                  # content - The String to write.
         
     | 
| 
      
 115 
     | 
    
         
            +
                  #
         
     | 
| 
      
 116 
     | 
    
         
            +
                  # Returns nothing.
         
     | 
| 
      
 117 
     | 
    
         
            +
                  def save(file, content)
         
     | 
| 
       95 
118 
     | 
    
         
             
                    file = absolute_path(file)
         
     | 
| 
       96 
     | 
    
         
            -
                    File.open(file, 'w') {|f|  
     | 
| 
      
 119 
     | 
    
         
            +
                    File.open(file, 'w:utf-8') {|f| f.write(content) }
         
     | 
| 
       97 
120 
     | 
    
         
             
                    File.chmod(0600, file)
         
     | 
| 
       98 
121 
     | 
    
         
             
                  end
         
     | 
| 
       99 
122 
     | 
    
         | 
| 
      
 123 
     | 
    
         
            +
                  # Generates a unique file id for the user's private XML fragment.
         
     | 
| 
      
 124 
     | 
    
         
            +
                  #
         
     | 
| 
      
 125 
     | 
    
         
            +
                  # Private XML fragment storage needs to uniquely identify fragment files
         
     | 
| 
      
 126 
     | 
    
         
            +
                  # on disk. We combine the user's JID with a SHA-1 hash of the element's
         
     | 
| 
      
 127 
     | 
    
         
            +
                  # name and namespace to avoid special characters in the file name.
         
     | 
| 
      
 128 
     | 
    
         
            +
                  #
         
     | 
| 
      
 129 
     | 
    
         
            +
                  # jid  - A bare JID identifying the user who owns this fragment.
         
     | 
| 
      
 130 
     | 
    
         
            +
                  # node - A Nokogiri::XML::Node for the XML to be stored.
         
     | 
| 
      
 131 
     | 
    
         
            +
                  #
         
     | 
| 
      
 132 
     | 
    
         
            +
                  # Returns an id String suitable for use in a file name.
         
     | 
| 
       100 
133 
     | 
    
         
             
                  def fragment_id(jid, node)
         
     | 
| 
       101 
134 
     | 
    
         
             
                    id = Digest::SHA1.hexdigest("#{node.name}:#{node.namespace.href}")
         
     | 
| 
       102 
135 
     | 
    
         
             
                    "#{jid}-#{id}"
         
     | 
    
        data/lib/vines/store.rb
    CHANGED
    
    | 
         @@ -43,10 +43,12 @@ module Vines 
     | 
|
| 
       43 
43 
     | 
    
         
             
                  unless @@sources
         
     | 
| 
       44 
44 
     | 
    
         
             
                    pattern = /-{5}BEGIN CERTIFICATE-{5}\n.*?-{5}END CERTIFICATE-{5}\n/m
         
     | 
| 
       45 
45 
     | 
    
         
             
                    pairs = Dir[File.join(@dir, '*.crt')].map do |name|
         
     | 
| 
       46 
     | 
    
         
            -
                       
     | 
| 
       47 
     | 
    
         
            -
             
     | 
| 
       48 
     | 
    
         
            -
             
     | 
| 
       49 
     | 
    
         
            -
             
     | 
| 
      
 46 
     | 
    
         
            +
                      File.open(name, "r:UTF-8") do |f|
         
     | 
| 
      
 47 
     | 
    
         
            +
                        pems = f.read.scan(pattern)
         
     | 
| 
      
 48 
     | 
    
         
            +
                        certs = pems.map {|pem| OpenSSL::X509::Certificate.new(pem) }
         
     | 
| 
      
 49 
     | 
    
         
            +
                        certs.reject! {|cert| cert.not_after < Time.now }
         
     | 
| 
      
 50 
     | 
    
         
            +
                        [name, certs]
         
     | 
| 
      
 51 
     | 
    
         
            +
                      end
         
     | 
| 
       50 
52 
     | 
    
         
             
                    end
         
     | 
| 
       51 
53 
     | 
    
         
             
                    @@sources = Hash[pairs]
         
     | 
| 
       52 
54 
     | 
    
         
             
                  end
         
     | 
    
        data/lib/vines/stream.rb
    CHANGED
    
    | 
         @@ -21,7 +21,7 @@ module Vines 
     | 
|
| 
       21 
21 
     | 
    
         
             
                  @remote_addr, @local_addr = addresses
         
     | 
| 
       22 
22 
     | 
    
         
             
                  @user, @closed, @stanza_size = nil, false, 0
         
     | 
| 
       23 
23 
     | 
    
         
             
                  @bucket = TokenBucket.new(100, 10)
         
     | 
| 
       24 
     | 
    
         
            -
                  @store = Store.new( 
     | 
| 
      
 24 
     | 
    
         
            +
                  @store = Store.new(@config.certs)
         
     | 
| 
       25 
25 
     | 
    
         
             
                  @nodes = EM::Queue.new
         
     | 
| 
       26 
26 
     | 
    
         
             
                  process_node_queue
         
     | 
| 
       27 
27 
     | 
    
         
             
                  create_parser
         
     | 
| 
         
            File without changes
         
     | 
| 
         
            File without changes
         
     | 
    
        data/lib/vines/stream/parser.rb
    CHANGED
    
    | 
         @@ -66,12 +66,13 @@ module Vines 
     | 
|
| 
       66 
66 
     | 
    
         
             
                  def node(name, attrs=[], prefix=nil, uri=nil, ns=[])
         
     | 
| 
       67 
67 
     | 
    
         
             
                    ignore = stream?(name, uri) ? [] : IGNORE
         
     | 
| 
       68 
68 
     | 
    
         
             
                    doc = @node ? @node.document : Document.new
         
     | 
| 
       69 
     | 
    
         
            -
                    doc.create_element(name) do |node|
         
     | 
| 
      
 69 
     | 
    
         
            +
                    node = doc.create_element(name) do |node|
         
     | 
| 
       70 
70 
     | 
    
         
             
                      attrs.each {|attr| node[attr.localname] = attr.value }
         
     | 
| 
       71 
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 
72 
     | 
    
         
             
                      doc << node unless @node
         
     | 
| 
       74 
73 
     | 
    
         
             
                    end
         
     | 
| 
      
 74 
     | 
    
         
            +
                    node.namespace = node.add_namespace(prefix, uri) unless ignore.include?(uri)
         
     | 
| 
      
 75 
     | 
    
         
            +
                    node
         
     | 
| 
       75 
76 
     | 
    
         
             
                  end
         
     | 
| 
       76 
77 
     | 
    
         
             
                end
         
     | 
| 
       77 
78 
     | 
    
         
             
              end
         
     | 
    
        data/lib/vines/token_bucket.rb
    CHANGED
    
    | 
         @@ -9,8 +9,12 @@ module Vines 
     | 
|
| 
       9 
9 
     | 
    
         
             
              # of operations.
         
     | 
| 
       10 
10 
     | 
    
         
             
              class TokenBucket
         
     | 
| 
       11 
11 
     | 
    
         | 
| 
       12 
     | 
    
         
            -
                # Create a full bucket with capacity number of tokens to be filled
         
     | 
| 
      
 12 
     | 
    
         
            +
                # Create a full bucket with `capacity` number of tokens to be filled
         
     | 
| 
       13 
13 
     | 
    
         
             
                # at the given rate of tokens/second.
         
     | 
| 
      
 14 
     | 
    
         
            +
                #
         
     | 
| 
      
 15 
     | 
    
         
            +
                # capacity - The Fixnum maximum number of tokens the bucket can hold.
         
     | 
| 
      
 16 
     | 
    
         
            +
                # rate     - The Fixnum number of tokens per second at which the bucket is
         
     | 
| 
      
 17 
     | 
    
         
            +
                #            refilled.
         
     | 
| 
       14 
18 
     | 
    
         
             
                def initialize(capacity, rate)
         
     | 
| 
       15 
19 
     | 
    
         
             
                  raise ArgumentError.new('capacity must be > 0') unless capacity > 0
         
     | 
| 
       16 
20 
     | 
    
         
             
                  raise ArgumentError.new('rate must be > 0') unless rate > 0
         
     | 
| 
         @@ -20,24 +24,29 @@ module Vines 
     | 
|
| 
       20 
24 
     | 
    
         
             
                  @timestamp = Time.new
         
     | 
| 
       21 
25 
     | 
    
         
             
                end
         
     | 
| 
       22 
26 
     | 
    
         | 
| 
       23 
     | 
    
         
            -
                #  
     | 
| 
      
 27 
     | 
    
         
            +
                # Remove tokens from the bucket if it's full enough. There's no way, or
         
     | 
| 
      
 28 
     | 
    
         
            +
                # need, to add tokens to the bucket. It refills over time.
         
     | 
| 
      
 29 
     | 
    
         
            +
                #
         
     | 
| 
      
 30 
     | 
    
         
            +
                # tokens - The Fixnum number of tokens to attempt to take from the bucket.
         
     | 
| 
      
 31 
     | 
    
         
            +
                #
         
     | 
| 
      
 32 
     | 
    
         
            +
                # Returns true if the bucket contains enough tokens to take, false if the
         
     | 
| 
      
 33 
     | 
    
         
            +
                # bucket isn't full enough to satisy the request.
         
     | 
| 
       24 
34 
     | 
    
         
             
                def take(tokens)
         
     | 
| 
       25 
35 
     | 
    
         
             
                  raise ArgumentError.new('tokens must be > 0') unless tokens > 0
         
     | 
| 
       26 
     | 
    
         
            -
                   
     | 
| 
       27 
     | 
    
         
            -
                    @tokens -= tokens
         
     | 
| 
       28 
     | 
    
         
            -
                    true
         
     | 
| 
       29 
     | 
    
         
            -
                  else
         
     | 
| 
       30 
     | 
    
         
            -
                    false
         
     | 
| 
       31 
     | 
    
         
            -
                  end
         
     | 
| 
      
 36 
     | 
    
         
            +
                  tokens <= fill ? @tokens -= tokens : false
         
     | 
| 
       32 
37 
     | 
    
         
             
                end
         
     | 
| 
       33 
38 
     | 
    
         | 
| 
       34 
39 
     | 
    
         
             
                private
         
     | 
| 
       35 
40 
     | 
    
         | 
| 
      
 41 
     | 
    
         
            +
                # Add tokens to the bucket at the `rate` provided in the constructor. This
         
     | 
| 
      
 42 
     | 
    
         
            +
                # fills the bucket slowly over time.
         
     | 
| 
      
 43 
     | 
    
         
            +
                #
         
     | 
| 
      
 44 
     | 
    
         
            +
                # Returns the Fixnum number of tokens left in the bucket.
         
     | 
| 
       36 
45 
     | 
    
         
             
                def fill
         
     | 
| 
       37 
46 
     | 
    
         
             
                  if @tokens < @capacity
         
     | 
| 
       38 
47 
     | 
    
         
             
                    now = Time.new
         
     | 
| 
       39 
     | 
    
         
            -
                     
     | 
| 
       40 
     | 
    
         
            -
                    @tokens =  
     | 
| 
      
 48 
     | 
    
         
            +
                    @tokens += (@rate * (now - @timestamp)).round
         
     | 
| 
      
 49 
     | 
    
         
            +
                    @tokens = @capacity if @tokens > @capacity
         
     | 
| 
       41 
50 
     | 
    
         
             
                    @timestamp = now
         
     | 
| 
       42 
51 
     | 
    
         
             
                  end
         
     | 
| 
       43 
52 
     | 
    
         
             
                  @tokens
         
     | 
    
        data/lib/vines/version.rb
    CHANGED
    
    
| 
         @@ -1,45 +1,57 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            # encoding: UTF-8
         
     | 
| 
       2 
2 
     | 
    
         | 
| 
       3 
     | 
    
         
            -
            require ' 
     | 
| 
       4 
     | 
    
         
            -
             
     | 
| 
       5 
     | 
    
         
            -
             
     | 
| 
       6 
     | 
    
         
            -
             
     | 
| 
       7 
     | 
    
         
            -
               
     | 
| 
       8 
     | 
    
         
            -
                 
     | 
| 
       9 
     | 
    
         
            -
                @cluster = MiniTest::Mock.new
         
     | 
| 
       10 
     | 
    
         
            -
                @cluster.expect(:id, 'abc')
         
     | 
| 
       11 
     | 
    
         
            -
                @cluster.expect(:connection, @connection)
         
     | 
| 
       12 
     | 
    
         
            -
              end
         
     | 
| 
      
 3 
     | 
    
         
            +
            require 'test_helper'
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            describe Vines::Cluster::Publisher do
         
     | 
| 
      
 6 
     | 
    
         
            +
              subject          { Vines::Cluster::Publisher.new(cluster) }
         
     | 
| 
      
 7 
     | 
    
         
            +
              let(:connection) { MiniTest::Mock.new }
         
     | 
| 
      
 8 
     | 
    
         
            +
              let(:cluster)    { MiniTest::Mock.new }
         
     | 
| 
       13 
9 
     | 
    
         | 
| 
       14 
     | 
    
         
            -
               
     | 
| 
       15 
     | 
    
         
            -
                 
     | 
| 
       16 
     | 
    
         
            -
                 
     | 
| 
      
 10 
     | 
    
         
            +
              before do
         
     | 
| 
      
 11 
     | 
    
         
            +
                cluster.expect :id, 'abc'
         
     | 
| 
      
 12 
     | 
    
         
            +
                cluster.expect :connection, connection
         
     | 
| 
      
 13 
     | 
    
         
            +
              end
         
     | 
| 
       17 
14 
     | 
    
         | 
| 
       18 
     | 
    
         
            -
             
     | 
| 
       19 
     | 
    
         
            -
                 
     | 
| 
       20 
     | 
    
         
            -
             
     | 
| 
       21 
     | 
    
         
            -
             
     | 
| 
      
 15 
     | 
    
         
            +
              describe '#broadcast' do
         
     | 
| 
      
 16 
     | 
    
         
            +
                before do
         
     | 
| 
      
 17 
     | 
    
         
            +
                  msg = {from: 'abc', type: 'online', time: Time.now.to_i}.to_json
         
     | 
| 
      
 18 
     | 
    
         
            +
                  connection.expect :publish, nil, ["cluster:nodes:all", msg]
         
     | 
| 
      
 19 
     | 
    
         
            +
                end
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                it 'publishes the message to every cluster node' do
         
     | 
| 
      
 22 
     | 
    
         
            +
                  subject.broadcast(:online)
         
     | 
| 
      
 23 
     | 
    
         
            +
                  connection.verify
         
     | 
| 
      
 24 
     | 
    
         
            +
                  cluster.verify
         
     | 
| 
      
 25 
     | 
    
         
            +
                end
         
     | 
| 
       22 
26 
     | 
    
         
             
              end
         
     | 
| 
       23 
27 
     | 
    
         | 
| 
       24 
     | 
    
         
            -
               
     | 
| 
       25 
     | 
    
         
            -
                stanza  
     | 
| 
       26 
     | 
    
         
            -
                msg = {from: 'abc', type: 'stanza', stanza: stanza}.to_json
         
     | 
| 
       27 
     | 
    
         
            -
                @connection.expect(:publish, nil, ["cluster:nodes:node-42", msg])
         
     | 
| 
      
 28 
     | 
    
         
            +
              describe '#route' do
         
     | 
| 
      
 29 
     | 
    
         
            +
                let(:stanza) { "<message>hello</message>" }
         
     | 
| 
       28 
30 
     | 
    
         | 
| 
       29 
     | 
    
         
            -
                 
     | 
| 
       30 
     | 
    
         
            -
             
     | 
| 
       31 
     | 
    
         
            -
             
     | 
| 
       32 
     | 
    
         
            -
                 
     | 
| 
      
 31 
     | 
    
         
            +
                before do
         
     | 
| 
      
 32 
     | 
    
         
            +
                  msg = {from: 'abc', type: 'stanza', stanza: stanza}.to_json
         
     | 
| 
      
 33 
     | 
    
         
            +
                  connection.expect :publish, nil, ["cluster:nodes:node-42", msg]
         
     | 
| 
      
 34 
     | 
    
         
            +
                end
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
                it 'publishes the message to just one cluster node' do
         
     | 
| 
      
 37 
     | 
    
         
            +
                  subject.route(stanza, "node-42")
         
     | 
| 
      
 38 
     | 
    
         
            +
                  connection.verify
         
     | 
| 
      
 39 
     | 
    
         
            +
                  cluster.verify
         
     | 
| 
      
 40 
     | 
    
         
            +
                end
         
     | 
| 
       33 
41 
     | 
    
         
             
              end
         
     | 
| 
       34 
42 
     | 
    
         | 
| 
       35 
     | 
    
         
            -
               
     | 
| 
       36 
     | 
    
         
            -
                jid  
     | 
| 
       37 
     | 
    
         
            -
             
     | 
| 
       38 
     | 
    
         
            -
                 
     | 
| 
      
 43 
     | 
    
         
            +
              describe '#update_user' do
         
     | 
| 
      
 44 
     | 
    
         
            +
                let(:jid) { Vines::JID.new('alice@wonderland.lit') }
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
                before do
         
     | 
| 
      
 47 
     | 
    
         
            +
                  msg = {from: 'abc', type: 'user', jid: jid.to_s}.to_json
         
     | 
| 
      
 48 
     | 
    
         
            +
                  connection.expect :publish, nil, ["cluster:nodes:node-42", msg]
         
     | 
| 
      
 49 
     | 
    
         
            +
                end
         
     | 
| 
       39 
50 
     | 
    
         | 
| 
       40 
     | 
    
         
            -
                 
     | 
| 
       41 
     | 
    
         
            -
             
     | 
| 
       42 
     | 
    
         
            -
             
     | 
| 
       43 
     | 
    
         
            -
             
     | 
| 
      
 51 
     | 
    
         
            +
                it 'publishes the new user to just one cluster node' do
         
     | 
| 
      
 52 
     | 
    
         
            +
                  subject.update_user(jid, "node-42")
         
     | 
| 
      
 53 
     | 
    
         
            +
                  connection.verify
         
     | 
| 
      
 54 
     | 
    
         
            +
                  cluster.verify
         
     | 
| 
      
 55 
     | 
    
         
            +
                end
         
     | 
| 
       44 
56 
     | 
    
         
             
              end
         
     | 
| 
       45 
57 
     | 
    
         
             
            end
         
     |