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