vines 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/LICENSE +19 -0
- data/README +34 -0
- data/Rakefile +55 -0
- data/bin/vines +95 -0
- data/conf/certs/README +32 -0
- data/conf/certs/ca-bundle.crt +3987 -0
- data/conf/config.rb +114 -0
- data/lib/vines.rb +155 -0
- data/lib/vines/command/bcrypt.rb +12 -0
- data/lib/vines/command/cert.rb +49 -0
- data/lib/vines/command/init.rb +58 -0
- data/lib/vines/command/ldap.rb +35 -0
- data/lib/vines/command/restart.rb +12 -0
- data/lib/vines/command/schema.rb +24 -0
- data/lib/vines/command/start.rb +28 -0
- data/lib/vines/command/stop.rb +18 -0
- data/lib/vines/config.rb +191 -0
- data/lib/vines/contact.rb +99 -0
- data/lib/vines/daemon.rb +78 -0
- data/lib/vines/error.rb +150 -0
- data/lib/vines/jid.rb +56 -0
- data/lib/vines/kit.rb +23 -0
- data/lib/vines/router.rb +125 -0
- data/lib/vines/stanza.rb +55 -0
- data/lib/vines/stanza/iq.rb +50 -0
- data/lib/vines/stanza/iq/auth.rb +18 -0
- data/lib/vines/stanza/iq/disco_info.rb +25 -0
- data/lib/vines/stanza/iq/disco_items.rb +23 -0
- data/lib/vines/stanza/iq/error.rb +16 -0
- data/lib/vines/stanza/iq/ping.rb +16 -0
- data/lib/vines/stanza/iq/query.rb +10 -0
- data/lib/vines/stanza/iq/result.rb +16 -0
- data/lib/vines/stanza/iq/roster.rb +153 -0
- data/lib/vines/stanza/iq/session.rb +22 -0
- data/lib/vines/stanza/iq/vcard.rb +58 -0
- data/lib/vines/stanza/message.rb +41 -0
- data/lib/vines/stanza/presence.rb +119 -0
- data/lib/vines/stanza/presence/error.rb +23 -0
- data/lib/vines/stanza/presence/probe.rb +38 -0
- data/lib/vines/stanza/presence/subscribe.rb +66 -0
- data/lib/vines/stanza/presence/subscribed.rb +64 -0
- data/lib/vines/stanza/presence/unavailable.rb +15 -0
- data/lib/vines/stanza/presence/unsubscribe.rb +57 -0
- data/lib/vines/stanza/presence/unsubscribed.rb +50 -0
- data/lib/vines/storage.rb +216 -0
- data/lib/vines/storage/couchdb.rb +119 -0
- data/lib/vines/storage/ldap.rb +59 -0
- data/lib/vines/storage/local.rb +66 -0
- data/lib/vines/storage/redis.rb +108 -0
- data/lib/vines/storage/sql.rb +174 -0
- data/lib/vines/store.rb +51 -0
- data/lib/vines/stream.rb +198 -0
- data/lib/vines/stream/client.rb +131 -0
- data/lib/vines/stream/client/auth.rb +94 -0
- data/lib/vines/stream/client/auth_restart.rb +33 -0
- data/lib/vines/stream/client/bind.rb +58 -0
- data/lib/vines/stream/client/bind_restart.rb +25 -0
- data/lib/vines/stream/client/closed.rb +13 -0
- data/lib/vines/stream/client/ready.rb +15 -0
- data/lib/vines/stream/client/start.rb +27 -0
- data/lib/vines/stream/client/tls.rb +37 -0
- data/lib/vines/stream/component.rb +53 -0
- data/lib/vines/stream/component/handshake.rb +25 -0
- data/lib/vines/stream/component/ready.rb +24 -0
- data/lib/vines/stream/component/start.rb +19 -0
- data/lib/vines/stream/http.rb +111 -0
- data/lib/vines/stream/http/http_request.rb +22 -0
- data/lib/vines/stream/http/http_state.rb +139 -0
- data/lib/vines/stream/http/http_states.rb +53 -0
- data/lib/vines/stream/parser.rb +78 -0
- data/lib/vines/stream/server.rb +126 -0
- data/lib/vines/stream/server/auth.rb +13 -0
- data/lib/vines/stream/server/auth_restart.rb +19 -0
- data/lib/vines/stream/server/final_restart.rb +20 -0
- data/lib/vines/stream/server/outbound/auth.rb +31 -0
- data/lib/vines/stream/server/outbound/auth_restart.rb +20 -0
- data/lib/vines/stream/server/outbound/auth_result.rb +28 -0
- data/lib/vines/stream/server/outbound/final_features.rb +27 -0
- data/lib/vines/stream/server/outbound/final_restart.rb +20 -0
- data/lib/vines/stream/server/outbound/start.rb +20 -0
- data/lib/vines/stream/server/outbound/tls.rb +30 -0
- data/lib/vines/stream/server/outbound/tls_result.rb +31 -0
- data/lib/vines/stream/server/ready.rb +20 -0
- data/lib/vines/stream/server/start.rb +13 -0
- data/lib/vines/stream/server/tls.rb +13 -0
- data/lib/vines/stream/state.rb +55 -0
- data/lib/vines/token_bucket.rb +46 -0
- data/lib/vines/user.rb +124 -0
- data/lib/vines/version.rb +5 -0
- data/lib/vines/xmpp_server.rb +25 -0
- data/test/config_test.rb +396 -0
- data/test/error_test.rb +59 -0
- data/test/ext/nokogiri.rb +14 -0
- data/test/jid_test.rb +71 -0
- data/test/kit_test.rb +21 -0
- data/test/router_test.rb +60 -0
- data/test/stanza/iq/roster_test.rb +198 -0
- data/test/stanza/iq/session_test.rb +30 -0
- data/test/stanza/iq/vcard_test.rb +159 -0
- data/test/stanza/message_test.rb +124 -0
- data/test/stanza/presence/subscribe_test.rb +75 -0
- data/test/storage/couchdb_test.rb +102 -0
- data/test/storage/ldap_test.rb +207 -0
- data/test/storage/local_test.rb +54 -0
- data/test/storage/redis_test.rb +75 -0
- data/test/storage/sql_test.rb +55 -0
- data/test/storage/storage_tests.rb +134 -0
- data/test/storage_test.rb +90 -0
- data/test/stream/client/auth_test.rb +127 -0
- data/test/stream/client/ready_test.rb +47 -0
- data/test/stream/component/handshake_test.rb +46 -0
- data/test/stream/component/ready_test.rb +105 -0
- data/test/stream/component/start_test.rb +41 -0
- data/test/stream/parser_test.rb +121 -0
- data/test/stream/server/outbound/auth_test.rb +77 -0
- data/test/stream/server/ready_test.rb +100 -0
- data/test/token_bucket_test.rb +24 -0
- data/test/user_test.rb +64 -0
- metadata +318 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# encoding: UTF-8
|
|
2
|
+
|
|
3
|
+
module Vines
|
|
4
|
+
module Command
|
|
5
|
+
class Start
|
|
6
|
+
def run(opts)
|
|
7
|
+
raise 'vines [--pid FILE] start' unless opts[:args].size == 0
|
|
8
|
+
require opts[:config]
|
|
9
|
+
server = XmppServer.new(Config.instance)
|
|
10
|
+
daemonize(opts) if opts[:daemonize]
|
|
11
|
+
server.start
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
private
|
|
15
|
+
|
|
16
|
+
def daemonize(opts)
|
|
17
|
+
daemon = Daemon.new(:pid => opts[:pid], :stdout => opts[:log],
|
|
18
|
+
:stderr => opts[:log])
|
|
19
|
+
if daemon.running?
|
|
20
|
+
raise "Vines is running as process #{daemon.pid}"
|
|
21
|
+
else
|
|
22
|
+
puts "Vines has started"
|
|
23
|
+
daemon.start
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# encoding: UTF-8
|
|
2
|
+
|
|
3
|
+
module Vines
|
|
4
|
+
module Command
|
|
5
|
+
class Stop
|
|
6
|
+
def run(opts)
|
|
7
|
+
raise 'vines [--pid FILE] stop' unless opts[:args].size == 0
|
|
8
|
+
daemon = Daemon.new(:pid => opts[:pid])
|
|
9
|
+
if daemon.running?
|
|
10
|
+
daemon.stop
|
|
11
|
+
puts 'Vines has been shutdown'
|
|
12
|
+
else
|
|
13
|
+
puts 'Vines is not running'
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
data/lib/vines/config.rb
ADDED
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
# encoding: UTF-8
|
|
2
|
+
|
|
3
|
+
module Vines
|
|
4
|
+
|
|
5
|
+
# A Config object is passed to the stream handlers to give them access
|
|
6
|
+
# to server configuration information like virtual host names, storage
|
|
7
|
+
# systems, etc. This class provides the DSL methods used in the
|
|
8
|
+
# config/vines.rb file.
|
|
9
|
+
class Config
|
|
10
|
+
LOG_LEVELS = %w[debug info warn error fatal].freeze
|
|
11
|
+
|
|
12
|
+
attr_reader :vhosts
|
|
13
|
+
|
|
14
|
+
@@instance = nil
|
|
15
|
+
def self.configure(&block)
|
|
16
|
+
@@instance = self.new(&block)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def self.instance
|
|
20
|
+
@@instance
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def initialize(&block)
|
|
24
|
+
@vhosts, @ports = {}, {}
|
|
25
|
+
instance_eval(&block)
|
|
26
|
+
raise "must define at least one virtual host" if @vhosts.empty?
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def host(*names, &block)
|
|
30
|
+
names = names.flatten.map {|name| name.downcase }
|
|
31
|
+
dupes = names.uniq.size != names.size || (@vhosts.keys & names).any?
|
|
32
|
+
raise "one host definition per domain allowed" if dupes
|
|
33
|
+
names.each do |name|
|
|
34
|
+
@vhosts.merge! Host.new(name, &block).to_hash
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
%w[client server http component].each do |name|
|
|
39
|
+
define_method(name) do |*args, &block|
|
|
40
|
+
port = Vines::Config.const_get("#{name.capitalize}Port")
|
|
41
|
+
raise "one #{name} port definition allowed" if @ports[name.to_sym]
|
|
42
|
+
@ports[name.to_sym] = port.new(self, *args) do
|
|
43
|
+
instance_eval(&block) if block
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def log(level)
|
|
49
|
+
const = Logger.const_get(level.to_s.upcase) rescue nil
|
|
50
|
+
unless LOG_LEVELS.include?(level.to_s) && const
|
|
51
|
+
raise "log level must be one of: #{LOG_LEVELS.join(', ')}"
|
|
52
|
+
end
|
|
53
|
+
Class.new.extend(Vines::Log).log.level = const
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def ports
|
|
57
|
+
@ports.values
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def vhost?(domain)
|
|
61
|
+
@vhosts.key?(domain)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Returns true if server-to-server connections are allowed with the
|
|
65
|
+
# given domain.
|
|
66
|
+
def s2s?(domain)
|
|
67
|
+
@ports[:server] && @ports[:server].hosts.include?(domain)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Retrieve the Port subclass with this name:
|
|
71
|
+
# [:client, :server, :http, :component]
|
|
72
|
+
def [](name)
|
|
73
|
+
@ports[name] or raise ArgumentError.new("no port named #{name}")
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
class Host
|
|
77
|
+
def initialize(name, &block)
|
|
78
|
+
@name, @storage, @ldap = name, nil, nil
|
|
79
|
+
instance_eval(&block)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def storage(name, &block)
|
|
83
|
+
raise "one storage mechanism per host allowed" if @storage
|
|
84
|
+
@storage = Storage.from_name(name, &block)
|
|
85
|
+
@storage.ldap = @ldap
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def ldap(host='localhost', port=636, &block)
|
|
89
|
+
@ldap = Storage::Ldap.new(host, port, &block)
|
|
90
|
+
@storage.ldap = @ldap if @storage
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def to_hash
|
|
94
|
+
raise "storage required for #{@name}" unless @storage
|
|
95
|
+
{@name => @storage}
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
class Port
|
|
100
|
+
include Vines::Log
|
|
101
|
+
|
|
102
|
+
attr_reader :config, :stream
|
|
103
|
+
|
|
104
|
+
%w[host port].each do |name|
|
|
105
|
+
define_method(name) do
|
|
106
|
+
@settings[name.to_sym]
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def initialize(config, host, port, &block)
|
|
111
|
+
@config, @settings = config, {}
|
|
112
|
+
instance_eval(&block) if block
|
|
113
|
+
defaults = {:host => host, :port => port,
|
|
114
|
+
:max_resources_per_account => 5, :max_stanza_size => 128 * 1024}
|
|
115
|
+
@settings = defaults.merge(@settings)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def max_stanza_size(max=nil)
|
|
119
|
+
if max
|
|
120
|
+
# rfc 6120 section 13.12
|
|
121
|
+
@settings[:max_stanza_size] = [10000, max].max
|
|
122
|
+
else
|
|
123
|
+
@settings[:max_stanza_size]
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def start
|
|
128
|
+
type = stream.name.split('::').last.downcase
|
|
129
|
+
log.info("Accepting #{type} connections on #{host}:#{port}")
|
|
130
|
+
EventMachine::start_server(host, port, stream, config)
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
class ClientPort < Port
|
|
135
|
+
def initialize(config, host='0.0.0.0', port=5222, &block)
|
|
136
|
+
@stream = Vines::Stream::Client
|
|
137
|
+
super(config, host, port, &block)
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def max_resources_per_account(max=nil)
|
|
141
|
+
if max
|
|
142
|
+
@settings[:max_resources_per_account] = max
|
|
143
|
+
else
|
|
144
|
+
@settings[:max_resources_per_account]
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
class ServerPort < Port
|
|
150
|
+
def initialize(config, host='0.0.0.0', port=5269, &block)
|
|
151
|
+
@hosts, @stream = [], Vines::Stream::Server
|
|
152
|
+
super(config, host, port, &block)
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def hosts(*hosts)
|
|
156
|
+
if hosts.any?
|
|
157
|
+
@hosts << hosts
|
|
158
|
+
@hosts.flatten!
|
|
159
|
+
else
|
|
160
|
+
@hosts
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
class HttpPort < Port
|
|
166
|
+
def initialize(config, host='0.0.0.0', port=5280, &block)
|
|
167
|
+
@stream = Vines::Stream::Http
|
|
168
|
+
super(config, host, port, &block)
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
class ComponentPort < Port
|
|
173
|
+
def initialize(config, host='0.0.0.0', port=5347, &block)
|
|
174
|
+
@components, @stream = {}, Vines::Stream::Component
|
|
175
|
+
super(config, host, port, &block)
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
def components(options=nil)
|
|
179
|
+
if options
|
|
180
|
+
@components = options
|
|
181
|
+
else
|
|
182
|
+
@components
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
def password(component)
|
|
187
|
+
@components[component]
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
end
|
|
@@ -0,0 +1,99 @@
|
|
|
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' unless @jid.node && !@jid.domain.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
|
+
self.jid.to_s <=> contact.jid.to_s
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def eql?(contact)
|
|
24
|
+
contact.is_a?(Contact) && self == contact
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def hash
|
|
28
|
+
jid.to_s.hash
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def update_from(contact)
|
|
32
|
+
@name = contact.name
|
|
33
|
+
@subscription = contact.subscription
|
|
34
|
+
@ask = contact.ask
|
|
35
|
+
@groups = contact.groups.clone
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Returns true if this contact is in a state that allows the user
|
|
39
|
+
# to subscribe to their presence updates.
|
|
40
|
+
def can_subscribe?
|
|
41
|
+
@ask == 'subscribe' && %w[none from].include?(@subscription)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def subscribe_to
|
|
45
|
+
@subscription = (@subscription == 'none') ? 'to' : 'both'
|
|
46
|
+
@ask = nil
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def unsubscribe_to
|
|
50
|
+
@subscription = (@subscription == 'both') ? 'from' : 'none'
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def subscribe_from
|
|
54
|
+
@subscription = (@subscription == 'none') ? 'from' : 'both'
|
|
55
|
+
@ask = nil
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def unsubscribe_from
|
|
59
|
+
@subscription = (@subscription == 'both') ? 'to' : 'none'
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Returns true if the user is subscribed to this contact's
|
|
63
|
+
# presence updates.
|
|
64
|
+
def subscribed_to?
|
|
65
|
+
%w[to both].include?(@subscription)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Returns true if the user has a presence subscription from
|
|
69
|
+
# this contact. The contact is subscribed to this user's presence.
|
|
70
|
+
def subscribed_from?
|
|
71
|
+
%w[from both].include?(@subscription)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Returns a hash of this contact's attributes suitable for persisting in
|
|
75
|
+
# a document store.
|
|
76
|
+
def to_h
|
|
77
|
+
{
|
|
78
|
+
'name' => @name,
|
|
79
|
+
'subscription' => @subscription,
|
|
80
|
+
'ask' => @ask,
|
|
81
|
+
'groups' => @groups.sort!
|
|
82
|
+
}
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# Returns this contact as an xmpp <item> element.
|
|
86
|
+
def to_roster_xml
|
|
87
|
+
doc = Nokogiri::XML::Document.new
|
|
88
|
+
doc.create_element('item') do |el|
|
|
89
|
+
el['ask'] = @ask unless @ask.nil? || @ask.empty?
|
|
90
|
+
el['jid'] = @jid.bare.to_s
|
|
91
|
+
el['name'] = @name unless @name.nil? || @name.empty?
|
|
92
|
+
el['subscription'] = @subscription
|
|
93
|
+
@groups.sort!.each do |group|
|
|
94
|
+
el << doc.create_element('group', group)
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
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 Confict < 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
|