tp-blather 0.8.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (150) hide show
  1. data/.autotest +13 -0
  2. data/.gemtest +0 -0
  3. data/.gitignore +19 -0
  4. data/.rspec +3 -0
  5. data/.travis.yml +8 -0
  6. data/CHANGELOG.md +249 -0
  7. data/Gemfile +4 -0
  8. data/Guardfile +5 -0
  9. data/LICENSE +22 -0
  10. data/README.md +413 -0
  11. data/Rakefile +20 -0
  12. data/TODO.md +2 -0
  13. data/blather.gemspec +51 -0
  14. data/examples/certs/README +20 -0
  15. data/examples/certs/ca-bundle.crt +3987 -0
  16. data/examples/echo.rb +19 -0
  17. data/examples/execute.rb +17 -0
  18. data/examples/ping_pong.rb +38 -0
  19. data/examples/print_hierarchy.rb +77 -0
  20. data/examples/rosterprint.rb +15 -0
  21. data/examples/stream_only.rb +28 -0
  22. data/examples/trusted_echo.rb +21 -0
  23. data/examples/xmpp4r/echo.rb +36 -0
  24. data/lib/blather.rb +112 -0
  25. data/lib/blather/cert_store.rb +53 -0
  26. data/lib/blather/client.rb +95 -0
  27. data/lib/blather/client/client.rb +345 -0
  28. data/lib/blather/client/dsl.rb +320 -0
  29. data/lib/blather/client/dsl/pubsub.rb +174 -0
  30. data/lib/blather/core_ext/eventmachine.rb +125 -0
  31. data/lib/blather/core_ext/ipaddr.rb +20 -0
  32. data/lib/blather/errors.rb +69 -0
  33. data/lib/blather/errors/sasl_error.rb +44 -0
  34. data/lib/blather/errors/stanza_error.rb +110 -0
  35. data/lib/blather/errors/stream_error.rb +84 -0
  36. data/lib/blather/file_transfer.rb +107 -0
  37. data/lib/blather/file_transfer/ibb.rb +68 -0
  38. data/lib/blather/file_transfer/s5b.rb +114 -0
  39. data/lib/blather/jid.rb +141 -0
  40. data/lib/blather/roster.rb +118 -0
  41. data/lib/blather/roster_item.rb +146 -0
  42. data/lib/blather/stanza.rb +167 -0
  43. data/lib/blather/stanza/disco.rb +32 -0
  44. data/lib/blather/stanza/disco/capabilities.rb +161 -0
  45. data/lib/blather/stanza/disco/disco_info.rb +205 -0
  46. data/lib/blather/stanza/disco/disco_items.rb +134 -0
  47. data/lib/blather/stanza/iq.rb +144 -0
  48. data/lib/blather/stanza/iq/command.rb +339 -0
  49. data/lib/blather/stanza/iq/ibb.rb +86 -0
  50. data/lib/blather/stanza/iq/ping.rb +50 -0
  51. data/lib/blather/stanza/iq/query.rb +53 -0
  52. data/lib/blather/stanza/iq/roster.rb +185 -0
  53. data/lib/blather/stanza/iq/s5b.rb +208 -0
  54. data/lib/blather/stanza/iq/si.rb +415 -0
  55. data/lib/blather/stanza/iq/vcard.rb +149 -0
  56. data/lib/blather/stanza/message.rb +428 -0
  57. data/lib/blather/stanza/message/muc_user.rb +119 -0
  58. data/lib/blather/stanza/muc/muc_user_base.rb +54 -0
  59. data/lib/blather/stanza/presence.rb +172 -0
  60. data/lib/blather/stanza/presence/c.rb +100 -0
  61. data/lib/blather/stanza/presence/muc.rb +35 -0
  62. data/lib/blather/stanza/presence/muc_user.rb +147 -0
  63. data/lib/blather/stanza/presence/status.rb +218 -0
  64. data/lib/blather/stanza/presence/subscription.rb +100 -0
  65. data/lib/blather/stanza/pubsub.rb +119 -0
  66. data/lib/blather/stanza/pubsub/affiliations.rb +79 -0
  67. data/lib/blather/stanza/pubsub/create.rb +65 -0
  68. data/lib/blather/stanza/pubsub/errors.rb +18 -0
  69. data/lib/blather/stanza/pubsub/event.rb +139 -0
  70. data/lib/blather/stanza/pubsub/items.rb +103 -0
  71. data/lib/blather/stanza/pubsub/publish.rb +103 -0
  72. data/lib/blather/stanza/pubsub/retract.rb +92 -0
  73. data/lib/blather/stanza/pubsub/subscribe.rb +68 -0
  74. data/lib/blather/stanza/pubsub/subscription.rb +135 -0
  75. data/lib/blather/stanza/pubsub/subscriptions.rb +83 -0
  76. data/lib/blather/stanza/pubsub/unsubscribe.rb +84 -0
  77. data/lib/blather/stanza/pubsub_owner.rb +51 -0
  78. data/lib/blather/stanza/pubsub_owner/delete.rb +52 -0
  79. data/lib/blather/stanza/pubsub_owner/purge.rb +52 -0
  80. data/lib/blather/stanza/x.rb +416 -0
  81. data/lib/blather/stream.rb +266 -0
  82. data/lib/blather/stream/client.rb +32 -0
  83. data/lib/blather/stream/component.rb +39 -0
  84. data/lib/blather/stream/features.rb +70 -0
  85. data/lib/blather/stream/features/register.rb +38 -0
  86. data/lib/blather/stream/features/resource.rb +63 -0
  87. data/lib/blather/stream/features/sasl.rb +190 -0
  88. data/lib/blather/stream/features/session.rb +45 -0
  89. data/lib/blather/stream/features/tls.rb +29 -0
  90. data/lib/blather/stream/parser.rb +102 -0
  91. data/lib/blather/version.rb +3 -0
  92. data/lib/blather/xmpp_node.rb +94 -0
  93. data/spec/blather/client/client_spec.rb +687 -0
  94. data/spec/blather/client/dsl/pubsub_spec.rb +492 -0
  95. data/spec/blather/client/dsl_spec.rb +266 -0
  96. data/spec/blather/errors/sasl_error_spec.rb +33 -0
  97. data/spec/blather/errors/stanza_error_spec.rb +129 -0
  98. data/spec/blather/errors/stream_error_spec.rb +108 -0
  99. data/spec/blather/errors_spec.rb +33 -0
  100. data/spec/blather/file_transfer_spec.rb +135 -0
  101. data/spec/blather/jid_spec.rb +87 -0
  102. data/spec/blather/roster_item_spec.rb +134 -0
  103. data/spec/blather/roster_spec.rb +107 -0
  104. data/spec/blather/stanza/discos/disco_info_spec.rb +247 -0
  105. data/spec/blather/stanza/discos/disco_items_spec.rb +154 -0
  106. data/spec/blather/stanza/iq/command_spec.rb +206 -0
  107. data/spec/blather/stanza/iq/ibb_spec.rb +124 -0
  108. data/spec/blather/stanza/iq/ping_spec.rb +45 -0
  109. data/spec/blather/stanza/iq/query_spec.rb +64 -0
  110. data/spec/blather/stanza/iq/roster_spec.rb +139 -0
  111. data/spec/blather/stanza/iq/s5b_spec.rb +57 -0
  112. data/spec/blather/stanza/iq/si_spec.rb +98 -0
  113. data/spec/blather/stanza/iq/vcard_spec.rb +93 -0
  114. data/spec/blather/stanza/iq_spec.rb +61 -0
  115. data/spec/blather/stanza/message/muc_user_spec.rb +152 -0
  116. data/spec/blather/stanza/message_spec.rb +282 -0
  117. data/spec/blather/stanza/presence/c_spec.rb +56 -0
  118. data/spec/blather/stanza/presence/muc_spec.rb +37 -0
  119. data/spec/blather/stanza/presence/muc_user_spec.rb +83 -0
  120. data/spec/blather/stanza/presence/status_spec.rb +144 -0
  121. data/spec/blather/stanza/presence/subscription_spec.rb +102 -0
  122. data/spec/blather/stanza/presence_spec.rb +125 -0
  123. data/spec/blather/stanza/pubsub/affiliations_spec.rb +57 -0
  124. data/spec/blather/stanza/pubsub/create_spec.rb +56 -0
  125. data/spec/blather/stanza/pubsub/event_spec.rb +98 -0
  126. data/spec/blather/stanza/pubsub/items_spec.rb +79 -0
  127. data/spec/blather/stanza/pubsub/publish_spec.rb +83 -0
  128. data/spec/blather/stanza/pubsub/retract_spec.rb +75 -0
  129. data/spec/blather/stanza/pubsub/subscribe_spec.rb +61 -0
  130. data/spec/blather/stanza/pubsub/subscription_spec.rb +97 -0
  131. data/spec/blather/stanza/pubsub/subscriptions_spec.rb +59 -0
  132. data/spec/blather/stanza/pubsub/unsubscribe_spec.rb +74 -0
  133. data/spec/blather/stanza/pubsub_owner/delete_spec.rb +50 -0
  134. data/spec/blather/stanza/pubsub_owner/purge_spec.rb +50 -0
  135. data/spec/blather/stanza/pubsub_owner_spec.rb +27 -0
  136. data/spec/blather/stanza/pubsub_spec.rb +68 -0
  137. data/spec/blather/stanza/x_spec.rb +231 -0
  138. data/spec/blather/stanza_spec.rb +134 -0
  139. data/spec/blather/stream/client_spec.rb +1090 -0
  140. data/spec/blather/stream/component_spec.rb +108 -0
  141. data/spec/blather/stream/parser_spec.rb +152 -0
  142. data/spec/blather/stream/ssl_spec.rb +32 -0
  143. data/spec/blather/xmpp_node_spec.rb +47 -0
  144. data/spec/blather_spec.rb +34 -0
  145. data/spec/fixtures/pubsub.rb +311 -0
  146. data/spec/spec_helper.rb +17 -0
  147. data/yard/templates/default/class/html/handlers.erb +18 -0
  148. data/yard/templates/default/class/setup.rb +10 -0
  149. data/yard/templates/default/class/text/handlers.erb +1 -0
  150. metadata +459 -0
data/examples/echo.rb ADDED
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require 'blather/client'
5
+
6
+ when_ready { puts "Connected ! send messages to #{jid.stripped}." }
7
+
8
+ subscription :request? do |s|
9
+ write_to_stream s.approve!
10
+ end
11
+
12
+ message :chat?, :body => 'exit' do |m|
13
+ say m.from, 'Exiting ...'
14
+ shutdown
15
+ end
16
+
17
+ message :chat?, :body do |m|
18
+ say m.from, "You sent: #{m.body}"
19
+ end
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require 'blather/client'
5
+
6
+ message :chat?, :body => 'exit' do |m|
7
+ say m.from, 'Exiting ...'
8
+ shutdown
9
+ end
10
+
11
+ message :chat?, :body do |m|
12
+ begin
13
+ say m.from, eval(m.body)
14
+ rescue => e
15
+ say m.from, e.inspect
16
+ end
17
+ end
@@ -0,0 +1,38 @@
1
+ require 'rubygems'
2
+ require 'blather/client/dsl'
3
+ $stdout.sync = true
4
+
5
+ module Ping
6
+ extend Blather::DSL
7
+ def self.run; client.run; end
8
+
9
+ setup 'echo@jabber.local/ping', 'echo'
10
+
11
+ status :from => Blather::JID.new('echo@jabber.local/pong') do |s|
12
+ puts "serve!"
13
+ say s.from, 'ping'
14
+ end
15
+
16
+ message :chat?, :body => 'pong' do |m|
17
+ puts "ping!"
18
+ say m.from, 'ping'
19
+ end
20
+ end
21
+
22
+ module Pong
23
+ extend Blather::DSL
24
+ def self.run; client.run; end
25
+
26
+ setup 'echo@jabber.local/pong', 'echo'
27
+ message :chat?, :body => 'ping' do |m|
28
+ puts "pong!"
29
+ say m.from, 'pong'
30
+ end
31
+ end
32
+
33
+ trap(:INT) { EM.stop }
34
+ trap(:TERM) { EM.stop }
35
+ EM.run do
36
+ Ping.run
37
+ Pong.run
38
+ end
@@ -0,0 +1,77 @@
1
+ require 'rubygems'
2
+ require 'blather'
3
+
4
+ class Object
5
+ begin
6
+ ObjectSpace.each_object(Class.new) {}
7
+
8
+ # Exclude this class unless it's a subclass of our supers and is defined.
9
+ # We check defined? in case we find a removed class that has yet to be
10
+ # garbage collected. This also fails for anonymous classes -- please
11
+ # submit a patch if you have a workaround.
12
+ def subclasses_of(*superclasses)
13
+ subclasses = []
14
+
15
+ superclasses.each do |sup|
16
+ ObjectSpace.each_object(class << sup; self; end) do |k|
17
+ if k != sup && (k.name.blank? || eval("defined?(::#{k}) && ::#{k}.object_id == k.object_id"))
18
+ subclasses << k
19
+ end
20
+ end
21
+ end
22
+
23
+ subclasses
24
+ end
25
+ rescue RuntimeError
26
+ # JRuby and any implementations which cannot handle the objectspace traversal
27
+ # above fall back to this implementation
28
+ def subclasses_of(*superclasses)
29
+ subclasses = []
30
+
31
+ superclasses.each do |sup|
32
+ ObjectSpace.each_object(Class) do |k|
33
+ if superclasses.any? { |superclass| k < superclass } &&
34
+ (k.name.blank? || eval("defined?(::#{k}) && ::#{k}.object_id == k.object_id"))
35
+ subclasses << k
36
+ end
37
+ end
38
+ subclasses.uniq!
39
+ end
40
+ subclasses
41
+ end
42
+ end
43
+ end
44
+
45
+ class Hash
46
+ def deep_merge(second)
47
+ # From: http://www.ruby-forum.com/topic/142809
48
+ # Author: Stefan Rusterholz
49
+ merger = proc { |key,v1,v2| Hash === v1 && Hash === v2 ? v1.merge(v2, &merger) : v2 }
50
+ self.merge(second, &merger)
51
+ end
52
+ end
53
+
54
+ handlers = {}
55
+ (Object.subclasses_of(Blather::Stanza) + Object.subclasses_of(Blather::BlatherError)).each do |klass|
56
+ handlers = handlers.deep_merge klass.handler_hierarchy.inject('klass' => klass.to_s.gsub('Blather::', '')) { |h,k| {k.to_s => h} }
57
+ end
58
+
59
+ level = 0
60
+ runner = proc do |k,v|
61
+ next if k == 'klass'
62
+
63
+ str = ''
64
+ if level > 0
65
+ (level - 1).times { str << '| ' }
66
+ str << '|- '
67
+ end
68
+
69
+ puts str+k
70
+ if Hash === v
71
+ level += 1
72
+ v.sort.each &runner
73
+ level -= 1
74
+ end
75
+ end
76
+
77
+ handlers.sort.each &runner
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # Prints out each roster entry
4
+
5
+ require 'rubygems'
6
+ require 'blather/client'
7
+
8
+ when_ready do
9
+ roster.grouped.each do |group, items|
10
+ puts "#{'*'*3} #{group || 'Ungrouped'} #{'*'*3}"
11
+ items.each { |item| puts "- #{item.name} (#{item.jid})" }
12
+ puts
13
+ end
14
+ shutdown
15
+ end
@@ -0,0 +1,28 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require 'blather'
5
+
6
+ trap(:INT) { EM.stop }
7
+ trap(:TERM) { EM.stop }
8
+ EM.run do
9
+ Blather::Stream::Client.start(Class.new {
10
+ attr_accessor :jid
11
+
12
+ def post_init(stream, jid = nil)
13
+ @stream = stream
14
+ self.jid = jid
15
+
16
+ @stream.send_data Blather::Stanza::Presence::Status.new
17
+ puts "Stream started!"
18
+ end
19
+
20
+ def receive_data(stanza)
21
+ @stream.send_data stanza.reply!
22
+ end
23
+
24
+ def unbind
25
+ puts "Stream ended!"
26
+ end
27
+ }.new, 'echo@jabber.local', 'echo')
28
+ end
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require 'blather/client'
5
+
6
+ setup 'chris@vines.local', 'test', 'vines.local', 5222, "./certs"
7
+
8
+ when_ready { puts "Connected ! send messages to #{jid.stripped}." }
9
+
10
+ subscription :request? do |s|
11
+ write_to_stream s.approve!
12
+ end
13
+
14
+ message :chat?, :body => 'exit' do |m|
15
+ say m.from, 'Exiting ...'
16
+ shutdown
17
+ end
18
+
19
+ message :chat?, :body do |m|
20
+ say m.from, "You sent: #{m.body}"
21
+ end
@@ -0,0 +1,36 @@
1
+ #!/usr/bin/ruby
2
+
3
+ # This bot will reply to every message it receives. To end the game, send 'exit'
4
+
5
+ require 'rubygems'
6
+ require 'xmpp4r/client'
7
+ include Jabber
8
+
9
+ # settings
10
+ if ARGV.length != 2
11
+ puts "Run with ./echo_thread.rb user@server/resource password"
12
+ exit 1
13
+ end
14
+ myJID = JID.new(ARGV[0])
15
+ myPassword = ARGV[1]
16
+ cl = Client.new(myJID)
17
+ cl.connect
18
+ cl.auth(myPassword)
19
+ cl.send(Presence.new)
20
+ puts "Connected ! send messages to #{myJID.strip.to_s}."
21
+ mainthread = Thread.current
22
+ cl.add_message_callback do |m|
23
+ if m.type != :error
24
+ m2 = Message.new(m.from, "You sent: #{m.body}")
25
+ m2.type = m.type
26
+ cl.send(m2)
27
+ if m.body == 'exit'
28
+ m2 = Message.new(m.from, "Exiting ...")
29
+ m2.type = m.type
30
+ cl.send(m2)
31
+ mainthread.wakeup
32
+ end
33
+ end
34
+ end
35
+ Thread.stop
36
+ cl.close
data/lib/blather.rb ADDED
@@ -0,0 +1,112 @@
1
+ # Require the necessary files
2
+ %w[
3
+ rubygems
4
+ eventmachine
5
+ niceogiri
6
+ ipaddr
7
+ digest/md5
8
+ digest/sha1
9
+ logger
10
+ openssl
11
+ girl_friday
12
+
13
+ active_support/core_ext/class/attribute
14
+ active_support/core_ext/object/blank
15
+
16
+ blather/core_ext/eventmachine
17
+ blather/core_ext/ipaddr
18
+
19
+ blather/cert_store
20
+ blather/errors
21
+ blather/errors/sasl_error
22
+ blather/errors/stanza_error
23
+ blather/errors/stream_error
24
+ blather/file_transfer
25
+ blather/file_transfer/ibb
26
+ blather/file_transfer/s5b
27
+ blather/jid
28
+ blather/roster
29
+ blather/roster_item
30
+ blather/xmpp_node
31
+
32
+ blather/stanza
33
+ blather/stanza/iq
34
+ blather/stanza/iq/command
35
+ blather/stanza/iq/ibb
36
+ blather/stanza/iq/ping
37
+ blather/stanza/iq/query
38
+ blather/stanza/iq/roster
39
+ blather/stanza/iq/s5b
40
+ blather/stanza/iq/si
41
+ blather/stanza/iq/vcard
42
+ blather/stanza/disco
43
+ blather/stanza/disco/disco_info
44
+ blather/stanza/disco/disco_items
45
+ blather/stanza/disco/capabilities
46
+ blather/stanza/message
47
+ blather/stanza/message/muc_user
48
+ blather/stanza/presence
49
+ blather/stanza/presence/c
50
+ blather/stanza/presence/status
51
+ blather/stanza/presence/subscription
52
+ blather/stanza/presence/muc
53
+ blather/stanza/presence/muc_user
54
+
55
+ blather/stanza/pubsub
56
+ blather/stanza/pubsub/affiliations
57
+ blather/stanza/pubsub/create
58
+ blather/stanza/pubsub/event
59
+ blather/stanza/pubsub/items
60
+ blather/stanza/pubsub/publish
61
+ blather/stanza/pubsub/retract
62
+ blather/stanza/pubsub/subscribe
63
+ blather/stanza/pubsub/subscription
64
+ blather/stanza/pubsub/subscriptions
65
+ blather/stanza/pubsub/unsubscribe
66
+
67
+ blather/stanza/pubsub_owner
68
+ blather/stanza/pubsub_owner/delete
69
+ blather/stanza/pubsub_owner/purge
70
+
71
+ blather/stanza/x
72
+
73
+ blather/stream
74
+ blather/stream/client
75
+ blather/stream/component
76
+ blather/stream/parser
77
+ blather/stream/features
78
+ blather/stream/features/resource
79
+ blather/stream/features/sasl
80
+ blather/stream/features/session
81
+ blather/stream/features/tls
82
+ blather/stream/features/register
83
+ ].each { |r| require r }
84
+
85
+ module Blather
86
+ @@logger = nil
87
+
88
+ class << self
89
+
90
+ # Default logger level. Any internal call to log() will forward the log message to
91
+ # the default log level
92
+ attr_accessor :default_log_level
93
+
94
+ def logger
95
+ @@logger ||= Logger.new($stdout).tap {|logger| logger.level = Logger::INFO }
96
+ end
97
+
98
+ def logger=(logger)
99
+ @@logger = logger
100
+ end
101
+
102
+ def default_log_level
103
+ @default_log_level ||= :debug # by default is debug (as it used to be)
104
+ end
105
+
106
+ def log(message)
107
+ logger.send self.default_log_level, message
108
+ end
109
+
110
+ end
111
+
112
+ end
@@ -0,0 +1,53 @@
1
+ # encoding: UTF-8
2
+
3
+ module Blather
4
+
5
+ # An X509 certificate store that validates certificate trust chains.
6
+ # This uses the #{cert_directory}/*.crt files as the list of trusted root
7
+ # CA certificates.
8
+ class CertStore
9
+ @@certs = nil
10
+ @cert_directory = nil
11
+
12
+ def initialize(cert_directory)
13
+ @cert_directory = cert_directory
14
+ @store = OpenSSL::X509::Store.new
15
+ certs.each {|c| @store.add_cert(c) }
16
+ end
17
+
18
+ # Return true if the certificate is signed by a CA certificate in the
19
+ # store. If the certificate can be trusted, it's added to the store so
20
+ # it can be used to trust other certs.
21
+ def trusted?(pem)
22
+ if cert = OpenSSL::X509::Certificate.new(pem) rescue nil
23
+ @store.verify(cert).tap do |trusted|
24
+ @store.add_cert(cert) if trusted rescue nil
25
+ end
26
+ end
27
+ end
28
+
29
+ # Return true if the domain name matches one of the names in the
30
+ # certificate. In other words, is the certificate provided to us really
31
+ # for the domain to which we think we're connected?
32
+ def domain?(pem, domain)
33
+ if cert = OpenSSL::X509::Certificate.new(pem) rescue nil
34
+ OpenSSL::SSL.verify_certificate_identity(cert, domain) rescue false
35
+ end
36
+ end
37
+
38
+ # Return the trusted root CA certificates installed in the @cert_directory. These
39
+ # certificates are used to start the trust chain needed to validate certs
40
+ # we receive from clients and servers.
41
+ def certs
42
+ unless @@certs
43
+ pattern = /-{5}BEGIN CERTIFICATE-{5}\n.*?-{5}END CERTIFICATE-{5}\n/m
44
+ dir = @cert_directory
45
+ certs = Dir[File.join(dir, '*.crt')].map {|f| File.read(f) }
46
+ certs = certs.map {|c| c.scan(pattern) }.flatten
47
+ certs.map! {|c| OpenSSL::X509::Certificate.new(c) }
48
+ @@certs = certs.reject {|c| c.not_after < Time.now }
49
+ end
50
+ @@certs
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,95 @@
1
+ require 'optparse'
2
+ require File.join(File.dirname(__FILE__), *%w[client dsl])
3
+
4
+ include Blather::DSL
5
+
6
+ options = {}
7
+ optparse = OptionParser.new do |opts|
8
+ opts.banner = "Run with #{$0} [options] user@server/resource password [host] [port]"
9
+
10
+ opts.on('-D', '--debug', 'Run in debug mode (you will see all XMPP communication)') do
11
+ options[:debug] = true
12
+ end
13
+
14
+ opts.on('-d', '--daemonize', 'Daemonize the process') do |daemonize|
15
+ options[:daemonize] = daemonize
16
+ end
17
+
18
+ opts.on('--pid=[PID]', 'Write the PID to this file') do |pid|
19
+ if !File.writable?(File.dirname(pid))
20
+ $stderr.puts "Unable to write log file to #{pid}"
21
+ exit 1
22
+ end
23
+ options[:pid] = pid
24
+ end
25
+
26
+ opts.on('--log=[LOG]', 'Write to the [LOG] file instead of stdout/stderr') do |log|
27
+ if !File.writable?(File.dirname(log))
28
+ $stderr.puts "Unable to write log file to #{log}"
29
+ exit 1
30
+ end
31
+ options[:log] = log
32
+ end
33
+
34
+ opts.on('--certs=[CERTS DIRECTORY]', 'The directory path where the trusted certificates are stored') do |certs|
35
+ if !File.directory?(certs)
36
+ $stderr.puts "The certs directory path (#{certs}) is no good."
37
+ exit 1
38
+ end
39
+ options[:certs] = certs
40
+ end
41
+
42
+ opts.on_tail('-h', '--help', 'Show this message') do
43
+ puts opts
44
+ exit
45
+ end
46
+
47
+ opts.on_tail('-v', '--version', 'Show version') do
48
+ require 'yaml'
49
+ version = YAML.load_file File.join(File.dirname(__FILE__), %w[.. .. VERSION.yml])
50
+ puts "Blather v#{version[:major]}.#{version[:minor]}.#{version[:patch]}"
51
+ exit
52
+ end
53
+ end
54
+ optparse.parse!
55
+
56
+ at_exit do
57
+ unless client.setup?
58
+ if ARGV.length < 2
59
+ puts optparse
60
+ exit 1
61
+ end
62
+ client.setup(*ARGV)
63
+ end
64
+
65
+ def run(options)
66
+ $stdin.reopen "/dev/null"
67
+
68
+ if options[:log]
69
+ log = File.new(options[:log], 'a')
70
+ log.sync = options[:debug]
71
+ $stdout.reopen log
72
+ $stderr.reopen $stdout
73
+ end
74
+
75
+ Blather.logger.level = Logger::DEBUG if options[:debug]
76
+
77
+ trap(:INT) { EM.stop }
78
+ trap(:TERM) { EM.stop }
79
+ EM.run { client.run }
80
+ end
81
+
82
+ if options[:daemonize]
83
+ pid = fork do
84
+ Process.setsid
85
+ exit if fork
86
+ File.open(options[:pid], 'w') { |f| f << Process.pid } if options[:pid]
87
+ run options
88
+ FileUtils.rm(options[:pid]) if options[:pid]
89
+ end
90
+ ::Process.detach pid
91
+ exit
92
+ else
93
+ run options
94
+ end
95
+ end