tp-blather 0.8.2

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.
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