gnms 2.1.0.rc1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of gnms might be problematic. Click here for more details.

Files changed (140) hide show
  1. data/LICENSE +459 -0
  2. data/README +29 -0
  3. data/bin/gnms +11 -0
  4. data/gnms.gemspec +70 -0
  5. data/lib/cmd_parse.rb +747 -0
  6. data/lib/config_global.rb +414 -0
  7. data/lib/contact.rb +171 -0
  8. data/lib/external/crypt/blowfish-tables.rb +190 -0
  9. data/lib/external/crypt/blowfish.rb +110 -0
  10. data/lib/external/crypt/cbc.rb +123 -0
  11. data/lib/external/crypt/stringxor.rb +27 -0
  12. data/lib/external/fog.rb +41 -0
  13. data/lib/external/jjmx.rb +45 -0
  14. data/lib/external/sms.rb +282 -0
  15. data/lib/external/xmpp4r-simple.rb +497 -0
  16. data/lib/format.rb +85 -0
  17. data/lib/gnms.rb +329 -0
  18. data/lib/gui/about_window.rb +177 -0
  19. data/lib/gui/adddelnode.rb +799 -0
  20. data/lib/gui/base_window.rb +789 -0
  21. data/lib/gui/canvas_map.rb +498 -0
  22. data/lib/gui/charts/gruff.rb +51 -0
  23. data/lib/gui/color_macro.rb +145 -0
  24. data/lib/gui/config_window.rb +2151 -0
  25. data/lib/gui/custom_plugin_window.rb +90 -0
  26. data/lib/gui/debug_window.rb +46 -0
  27. data/lib/gui/event_window.rb +469 -0
  28. data/lib/gui/find_node_window.rb +438 -0
  29. data/lib/gui/help_window.rb +47 -0
  30. data/lib/gui/ipcalculator.rb +147 -0
  31. data/lib/gui/jmx_ref_window.rb +309 -0
  32. data/lib/gui/link_view.rb +46 -0
  33. data/lib/gui/mib_browser.rb +585 -0
  34. data/lib/gui/node_host_property.rb +3712 -0
  35. data/lib/gui/node_host_view.rb +270 -0
  36. data/lib/gui/node_network_property.rb +1059 -0
  37. data/lib/gui/node_network_view.rb +186 -0
  38. data/lib/gui/node_view.rb +672 -0
  39. data/lib/gui/oid_ref_window.rb +288 -0
  40. data/lib/gui/snmptrap_ref_window.rb +288 -0
  41. data/lib/gui/sub/account_frame.rb +196 -0
  42. data/lib/gui/sub/contact_frame.rb +519 -0
  43. data/lib/gui/syslog_ref_window.rb +288 -0
  44. data/lib/gui/treenode_window.rb +320 -0
  45. data/lib/gui/update_window.rb +124 -0
  46. data/lib/gui/widgets.rb +169 -0
  47. data/lib/gui/wmi_ref_window.rb +286 -0
  48. data/lib/interface.rb +16 -0
  49. data/lib/ipcalc.rb +266 -0
  50. data/lib/link.rb +45 -0
  51. data/lib/main.rb +1091 -0
  52. data/lib/monitor/client/custom/custom_monitoring.rb +40 -0
  53. data/lib/monitor/client/jmx/jmx_attr_monitoring.rb +36 -0
  54. data/lib/monitor/client/jmx/jmx_monitoring.rb +83 -0
  55. data/lib/monitor/client/jmx/jmx_ref.rb +114 -0
  56. data/lib/monitor/client/snmp/mibs/SNMPv2-MIB.txt +854 -0
  57. data/lib/monitor/client/snmp/mibs/SNMPv2-SMI.txt +344 -0
  58. data/lib/monitor/client/snmp/oid_ref.rb +141 -0
  59. data/lib/monitor/client/snmp/snmp.rb +178 -0
  60. data/lib/monitor/client/snmp/snmp_monitoring.rb +58 -0
  61. data/lib/monitor/client/wmi/wmi.rb +139 -0
  62. data/lib/monitor/client/wmi/wmi_monitoring.rb +60 -0
  63. data/lib/monitor/client/wmi/wmi_ref.rb +141 -0
  64. data/lib/monitor/custom_plugin.rb +50 -0
  65. data/lib/monitor/server/msg_buffer.rb +39 -0
  66. data/lib/monitor/server/snmp/snmptrap_analyzer.rb +81 -0
  67. data/lib/monitor/server/snmp/snmptrap_capture.rb +26 -0
  68. data/lib/monitor/server/snmp/snmptrap_monitoring.rb +32 -0
  69. data/lib/monitor/server/snmp/snmptrap_ref.rb +135 -0
  70. data/lib/monitor/server/snmp/snmptrap_server.rb +50 -0
  71. data/lib/monitor/server/syslog/syslog_analyzer.rb +141 -0
  72. data/lib/monitor/server/syslog/syslog_capture.rb +28 -0
  73. data/lib/monitor/server/syslog/syslog_monitoring.rb +32 -0
  74. data/lib/monitor/server/syslog/syslog_ref.rb +136 -0
  75. data/lib/monitor/server/syslog/syslog_server.rb +43 -0
  76. data/lib/node.rb +1020 -0
  77. data/lib/node_db.rb +2668 -0
  78. data/lib/node_host.rb +215 -0
  79. data/lib/node_listener.rb +639 -0
  80. data/lib/node_network.rb +269 -0
  81. data/lib/node_property.rb +39 -0
  82. data/lib/notify/alarm.rb +247 -0
  83. data/lib/notify/irc_bot.rb +167 -0
  84. data/lib/notify/xmpp_bot.rb +107 -0
  85. data/lib/service.rb +36 -0
  86. data/lib/version.rb +8 -0
  87. data/pixmap/bg/map_bg.jpg +0 -0
  88. data/pixmap/bg/map_bg2.jpg +0 -0
  89. data/pixmap/logo.jpg +0 -0
  90. data/pixmap/logo_icon.png +0 -0
  91. data/pixmap/os/3com.png +0 -0
  92. data/pixmap/os/aix.gif +0 -0
  93. data/pixmap/os/amiga.png +0 -0
  94. data/pixmap/os/beos.png +0 -0
  95. data/pixmap/os/cisco.png +0 -0
  96. data/pixmap/os/freebsd.png +0 -0
  97. data/pixmap/os/hp.png +0 -0
  98. data/pixmap/os/hurd.gif +0 -0
  99. data/pixmap/os/irix.gif +0 -0
  100. data/pixmap/os/linux.png +0 -0
  101. data/pixmap/os/mac.png +0 -0
  102. data/pixmap/os/netbsd.gif +0 -0
  103. data/pixmap/os/netware.png +0 -0
  104. data/pixmap/os/openbsd.png +0 -0
  105. data/pixmap/os/os2warp.gif +0 -0
  106. data/pixmap/os/qnx.gif +0 -0
  107. data/pixmap/os/sco.gif +0 -0
  108. data/pixmap/os/sgi.xpm +111 -0
  109. data/pixmap/os/solaris.gif +0 -0
  110. data/pixmap/os/unix.gif +0 -0
  111. data/pixmap/os/unknown.png +0 -0
  112. data/pixmap/os/vms.xpm +345 -0
  113. data/pixmap/os/windows.png +0 -0
  114. data/pixmap/type/cloud.png +0 -0
  115. data/pixmap/type/device.png +0 -0
  116. data/pixmap/type/firewall.png +0 -0
  117. data/pixmap/type/gateway.png +0 -0
  118. data/pixmap/type/home.png +0 -0
  119. data/pixmap/type/host.png +0 -0
  120. data/pixmap/type/network.png +0 -0
  121. data/pixmap/type/printer.png +0 -0
  122. data/pixmap/type/router.png +0 -0
  123. data/pixmap/type/server.png +0 -0
  124. data/pixmap/type/switch.png +0 -0
  125. data/pixmap/type/vm.png +0 -0
  126. data/plugins/Defaultme.rb +7 -0
  127. data/plugins/DnsCheck.rb +46 -0
  128. data/plugins/FooMonitor.rb +26 -0
  129. data/plugins/HttpVirtualHostMonitor.rb +43 -0
  130. data/plugins/MysqlShowStatusMonitor.rb +64 -0
  131. data/plugins/PgsqlPercentConnectionMonitor.rb +72 -0
  132. data/plugins/SshCpuLoadMonitor.rb +85 -0
  133. data/plugins/SshDiskMonitor.rb +69 -0
  134. data/plugins/SshFgtAvExpiredMonitor.rb +75 -0
  135. data/plugins/SshPercentUsedRamMonitor.rb +74 -0
  136. data/plugins/TelnetCpuLoadMonitor.rb +69 -0
  137. data/plugins/example1.rb +15 -0
  138. data/plugins/example2.rb +7 -0
  139. data/sound/beep.wav +0 -0
  140. metadata +378 -0
@@ -0,0 +1,41 @@
1
+ class Fog < String
2
+ VERSION = '1.0.1'
3
+
4
+ # Returns an encrypted +string+ based on the +degree+ that you pass. The
5
+ # default degree is 13. The +degree+ is more or less a salt value.
6
+ #
7
+ def initialize(string, degree=13)
8
+ @degree = degree.to_i
9
+ str = encrypt(string, degree)
10
+ super(str)
11
+ end
12
+
13
+ # Decrypts the string using the +degree+ passed to the constructor.
14
+ #
15
+ def decrypt
16
+ unpack('C*').map{ |e| e -= @degree }.pack('C*')
17
+ end
18
+
19
+ # The to_s method is identical to inspect, in order to prevent the
20
+ # literal quotation marks from potentially messing up things.
21
+ #
22
+ def to_s
23
+ inspect
24
+ end
25
+
26
+ # Decrypts an arbitrary string using +degree+. You must know the degree
27
+ # with which the string was originally encrypted in order for this method
28
+ # to work properly.
29
+ #
30
+ def self.decrypt(string, degree=13)
31
+ string.unpack('C*').map{ |e| e -= degree }.pack('C*')
32
+ end
33
+
34
+ private
35
+
36
+ # A private method used to encrypt a string based on +degree+.
37
+ #
38
+ def encrypt(str=self, degree=@degree)
39
+ str.unpack('C*').map{ |e| e += degree }.pack('C*')
40
+ end
41
+ end
@@ -0,0 +1,45 @@
1
+ #!/usr/bin/env jruby
2
+ require 'rubygems'
3
+ require 'jmx4r'
4
+ #./jjmx.rb 192.168.2.10 9003 user pass "java.lang:type=Memory" "heap_memory_usage non_heap_memory_usage"
5
+
6
+ if ARGV.length < 6
7
+ print "error: missing parameters\n"
8
+ exit
9
+ end
10
+
11
+ host = ARGV[0]
12
+ port = ARGV[1]
13
+ user = ARGV[2]
14
+ pass = ARGV[3]
15
+ object = ARGV[4]
16
+ attrs = ARGV[5]
17
+
18
+ begin
19
+ if user != ""
20
+ JMX::MBean.establish_connection :host => host, :port => port, :username => user, :password => pass
21
+ else
22
+ JMX::MBean.establish_connection :host => host, :port => port
23
+ end
24
+ rescue Exception => msg
25
+ print "error: ",msg, "\n"
26
+ exit
27
+ end
28
+
29
+ mbean = JMX::MBean.find_by_name object
30
+
31
+ attrs.split.each {|attr|
32
+ print attr,": "
33
+ begin
34
+ val=mbean.send(attr)
35
+ if val.class == Java::JavaxManagementOpenmbean::CompositeDataSupport
36
+ print val["used"]
37
+ else
38
+ print val
39
+ end
40
+ rescue
41
+ print "error"
42
+ end
43
+
44
+ print "\n"
45
+ }
@@ -0,0 +1,282 @@
1
+ # encoding: utf-8
2
+
3
+ begin
4
+ require "serialport"
5
+ rescue LoadError
6
+ $log.error("Serialport is not installed, you should install the gem to have SMS notification")
7
+ end
8
+ require "socket"
9
+
10
+
11
+ class SMSController
12
+
13
+ def initialize(bluetoothDevice,phoneNum,smsText)
14
+ @@bluetoothDevice = bluetoothDevice
15
+ @@phoneNum = phoneNum
16
+ @@smsText = smsText
17
+ end
18
+
19
+ @@t_bin =
20
+ {
21
+ "0000" => "0",
22
+ '0001' => '1',
23
+ '0010' => '2',
24
+ '0011' => '3',
25
+ '0100' => '4',
26
+ '0101' => '5',
27
+ '0110' => '6',
28
+ '0111' => '7',
29
+ '1000' => '8',
30
+ '1001' => '9',
31
+ '1010' => 'A',
32
+ '1011' => 'B',
33
+ '1100' => 'C',
34
+ '1101' => 'D',
35
+ '1110' => 'E',
36
+ '1111' => 'F'
37
+ }
38
+
39
+ @@bit7 =
40
+ {
41
+ "0000000" => "@",
42
+ "0000001" => "£",
43
+ "0000010" => "$",
44
+ "0000011" => "¥",
45
+ "0000100" => "è",
46
+ "0000101" => "é",
47
+ "0000110" => "ù",
48
+ "0000111" => "ì",
49
+ "0001000" => "ò",
50
+ "0001001" => "Ç",
51
+ "0001010" => "\r",
52
+ "0001011" => "Ø",
53
+ "0001100" => "ø",
54
+ "0001101" => "\n\r",
55
+ "0001110" => "Å",
56
+ "0001111" => "å",
57
+ "0010000" => "∆",
58
+ "0010001" => "_",
59
+ "0010010" => "fi",
60
+ "0010011" => "GAMMA",
61
+ "0010100" => "LAMBDA",
62
+ "0010101" => "Ω",
63
+ "0010110" => "π",
64
+ "0010111" => "PSI",
65
+ "0011000" => "∑",
66
+ "0011001" => "THETA",
67
+ "0011010" => "XI",
68
+ "0011011" => "\n",
69
+ "0011100" => "Æ",
70
+ "0011101" => "æ",
71
+ "0011110" => "ß",
72
+ "0011111" => "É",
73
+ "0100000" => " ",
74
+ "0100001" => "!",
75
+ "0100010" => "\"",
76
+ "0100011" => "#",
77
+ "0100100" => "¢",
78
+ "0100101" => "%",
79
+ "0100110" => "&",
80
+ "0100111" => "\'",
81
+ "0101000" => "(",
82
+ "0101001" => ")",
83
+ "0101010" => "*",
84
+ "0101011" => "+",
85
+ "0101100" => ",",
86
+ "0101101" => "-",
87
+ "0101110" => ".",
88
+ "0101111" => "/",
89
+ "0110000" => "0",
90
+ "0110001" => "1",
91
+ "0110010" => "2",
92
+ "0110011" => "3",
93
+ "0110100" => "4",
94
+ "0110101" => "5",
95
+ "0110110" => "6",
96
+ "0110111" => "7",
97
+ "0111000" => "8",
98
+ "0111001" => "9",
99
+ "0111010" => ":",
100
+ "0111011" => ",",
101
+ "0111100" => "<",
102
+ "0111101" => "=",
103
+ "0111110" => ">",
104
+ "0111111" => "?",
105
+ "1000000" => "¡",
106
+ "1000001" => "A",
107
+ "1000010" => "B",
108
+ "1000011" => "C",
109
+ "1000100" => "D",
110
+ "1000101" => "E",
111
+ "1000110" => "F",
112
+ "1000111" => "G",
113
+ "1001000" => "H",
114
+ "1001001" => "I",
115
+ "1001010" => "J",
116
+ "1001011" => "K",
117
+ "1001100" => "L",
118
+ "1001101" => "M",
119
+ "1001110" => "N",
120
+ "1001111" => "O",
121
+ "1010000" => "P",
122
+ "1010001" => "Q",
123
+ "1010010" => "R",
124
+ "1010011" => "S",
125
+ "1010100" => "T",
126
+ "1010101" => "U",
127
+ "1010110" => "V",
128
+ "1010111" => "W",
129
+ "1011000" => "X",
130
+ "1011001" => "Y",
131
+ "1011010" => "Z",
132
+ "1011011" => "Ä",
133
+ "1011100" => "Ö",
134
+ "1011101" => "N",
135
+ "1011110" => "Ü",
136
+ "1011111" => "§",
137
+ "1100000" => "?",
138
+ "1100001" => "a",
139
+ "1100010" => "b",
140
+ "1100011" => "c",
141
+ "1100100" => "d",
142
+ "1100101" => "e",
143
+ "1100110" => "f",
144
+ "1100111" => "g",
145
+ "1101000" => "h",
146
+ "1101001" => "i",
147
+ "1101010" => "j",
148
+ "1101011" => "k",
149
+ "1101100" => "l",
150
+ "1101101" => "m",
151
+ "1101110" => "n",
152
+ "1101111" => "o",
153
+ "1110000" => "p",
154
+ "1110001" => "q",
155
+ "1110010" => "r",
156
+ "1110011" => "s",
157
+ "1110100" => "t",
158
+ "1110101" => "u",
159
+ "1110110" => "v",
160
+ "1110111" => "w",
161
+ "1111000" => "x",
162
+ "1111001" => "y",
163
+ "1111010" => "Z",
164
+ "1111011" => "ä",
165
+ "1111100" => "ö",
166
+ "1111101" => "ñ",
167
+ "1111110" => "ü",
168
+ "1111111" => "à",
169
+ }
170
+
171
+ def sendSMS()
172
+ createSMS(@@phoneNum,@@smsText)
173
+ end
174
+
175
+
176
+ def nibble_swap(str)
177
+ strLen = str.length
178
+ swap_str = ""
179
+ if strLen%2 != 0 then
180
+ str = str + "f"
181
+ end
182
+ i = 0
183
+ while i < strLen
184
+ swap_str = swap_str + str.slice(i..(i+1)).reverse
185
+ i += 2
186
+ end
187
+ return swap_str
188
+ end
189
+
190
+ def ascii2bin7bit(str)
191
+ strLen = str.length
192
+ binStr = ""
193
+ i = 0
194
+ while i < strLen
195
+ char = str.slice(i..i)
196
+ @@bit7.each do |k,v|
197
+ if v == char then
198
+ binStr = binStr + k
199
+ end
200
+ end
201
+ i += 1
202
+ end
203
+ return binStr
204
+ end
205
+
206
+ def code7bin2octet(septets)
207
+
208
+ spLen = septets.length - 6
209
+ octets = ""
210
+ octets_n = ""
211
+ oct = {}
212
+ n = 0
213
+ i = 0
214
+ while i < spLen
215
+ if (n <= 6) then
216
+ sep = septets.slice((i)..(i+6))
217
+ sep_n = septets.slice((i+7)..(i+13))
218
+ if !(sep_n.length == 0) then
219
+ oct[n] = sep_n.slice((6-n)..6) + sep.slice(0..(6-n))
220
+ octets_n = octets_n + oct[n]
221
+ else
222
+ oct[n] = sep.slice(0..(6-n))
223
+ while oct[n].length < 8
224
+ oct[n] = "0" + oct[n]
225
+ end
226
+ octets_n = octets_n + oct[n]
227
+ end
228
+ n += 1
229
+ else
230
+ n = 0
231
+ end
232
+ i += 7
233
+ end
234
+ hex = []
235
+ j = 0
236
+ while j < octets_n.length
237
+ bin = octets_n.slice(j..j+3)
238
+ hex.insert(-1,@@t_bin["#{bin}"])
239
+ j += 4
240
+ end
241
+ return hex.to_s
242
+ end
243
+
244
+ def createSMS(phoneNum,smsText)
245
+
246
+ @sms_praefix = "001100"
247
+
248
+ @phone_num = phoneNum
249
+ @sms_msg = smsText
250
+ @pn_len = @phone_num.length
251
+ @pn_len = sprintf('%02X',@pn_len)
252
+ @trp = "91"
253
+ @phone_num = nibble_swap(@phone_num)
254
+ @sms_infix = "0000FF"
255
+ @msg_len = @sms_msg.length
256
+ @msg_len = sprintf('%02X',@msg_len)
257
+ @sms_msg = ascii2bin7bit(@sms_msg)
258
+ @sms_msg = code7bin2octet(@sms_msg)
259
+ @sms = @sms_praefix + @pn_len + @trp + @phone_num + @sms_infix + @msg_len + @sms_msg
260
+ @sms_len = @sms.length/2 -1
261
+
262
+ sp = SerialPort.new(@@bluetoothDevice, 9600,8,1,SerialPort::NONE)
263
+ open(@@bluetoothDevice)
264
+
265
+ sp.write( "at+cmgs=#{@sms_len}" + "\n\r")
266
+
267
+ sleep 2
268
+
269
+ sp.write( "#{@sms}" + "\032")
270
+
271
+ sleep 2
272
+
273
+ sp.close
274
+ end
275
+
276
+ end
277
+
278
+ #mySMS = SMSController.new("/dev/cu.Your_Paired_Bluetooth_Device","Number_Of_SMS_Receiver,"SMS_Message")
279
+ #mySMS = SMSController.new("/dev/cu.W900i-SerialPort-1","43676844256222","hallo world!")
280
+
281
+ #mySMS.sendSMS
282
+
@@ -0,0 +1,497 @@
1
+ # Jabber::Simple - An extremely easy-to-use Jabber client library.
2
+ # Copyright 2006 Blaine Cook <blaine@obvious.com>, Obvious Corp.
3
+ #
4
+ # Jabber::Simple is free software; you can redistribute it and/or modify
5
+ # it under the terms of the GNU General Public License as published by
6
+ # the Free Software Foundation; either version 2 of the License, or
7
+ # (at your option) any later version.
8
+ #
9
+ # Jabber::Simple is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU General Public License
15
+ # along with Jabber::Simple; if not, write to the Free Software
16
+ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17
+
18
+ # copy the lib as the default version
19
+ # 20090422 and fix from https://github.com/blaine/xmpp4r-simple/blob/master/lib/xmpp4r-simple.rb
20
+
21
+ require 'rubygems'
22
+ require 'xmpp4r'
23
+ require 'xmpp4r/roster'
24
+ require 'xmpp4r/vcard'
25
+
26
+ module Jabber
27
+
28
+ class ConnectionError < StandardError #:nodoc:
29
+ end
30
+
31
+ class Contact #:nodoc:
32
+
33
+ include DRb::DRbUndumped if defined?(DRb::DRbUndumped)
34
+
35
+ def initialize(client, jid)
36
+ @jid = jid.respond_to?(:resource) ? jid : JID.new(jid)
37
+ @client = client
38
+ end
39
+
40
+ def inspect
41
+ "Jabber::Contact #{jid.to_s}"
42
+ end
43
+
44
+ def subscribed?
45
+ [:to, :both].include?(subscription)
46
+ end
47
+
48
+ def subscription
49
+ roster_item && roster_item.subscription
50
+ end
51
+
52
+ def ask_for_authorization!
53
+ subscription_request = Presence.new.set_type(:subscribe)
54
+ subscription_request.to = jid
55
+ client.send!(subscription_request)
56
+ end
57
+
58
+ def unsubscribe!
59
+ unsubscription_request = Presence.new.set_type(:unsubscribe)
60
+ unsubscription_request.to = jid
61
+ client.send!(unsubscription_request)
62
+ client.send!(unsubscription_request.set_type(:unsubscribed))
63
+ end
64
+
65
+ def jid(bare=true)
66
+ bare ? @jid.strip : @jid
67
+ end
68
+
69
+ private
70
+
71
+ def roster_item
72
+ client.roster.items[jid]
73
+ end
74
+
75
+ def client
76
+ @client
77
+ end
78
+ end
79
+
80
+ class Simple
81
+
82
+ include DRb::DRbUndumped if defined?(DRb::DRbUndumped)
83
+
84
+ # Create a new Jabber::Simple client. You will be automatically connected
85
+ # to the Jabber server and your status message will be set to the string
86
+ # passed in as the status_message argument.
87
+ #
88
+ # jabber = Jabber::Simple.new("me@example.com", "password", "Chat with me - Please!")
89
+ def initialize(jid, password, status = nil, status_message = "Available")
90
+ @jid = jid
91
+ @password = password
92
+ @disconnected = false
93
+ status(status, status_message)
94
+ start_deferred_delivery_thread
95
+ end
96
+
97
+ def inspect #:nodoc:
98
+ "Jabber::Simple #{@jid}"
99
+ end
100
+
101
+ # Send a message to jabber user jid.
102
+ #
103
+ # Valid message types are:
104
+ #
105
+ # * :normal (default): a normal message.
106
+ # * :chat: a one-to-one chat message.
107
+ # * :groupchat: a group-chat message.
108
+ # * :headline: a "headline" message.
109
+ # * :error: an error message.
110
+ #
111
+ # If the recipient is not in your contacts list, the message will be queued
112
+ # for later delivery, and the Contact will be automatically asked for
113
+ # authorization (see Jabber::Simple#add).
114
+ #
115
+ # message should be a string or a valid Jabber::Message object. In either case,
116
+ # the message recipient will be set to jid.
117
+ def deliver(jid, message, type=:chat)
118
+ contacts(jid) do |friend|
119
+ unless subscribed_to? friend
120
+ add(friend.jid)
121
+ return deliver_deferred(friend.jid, message, type)
122
+ end
123
+ if message.kind_of?(Jabber::Message)
124
+ msg = message
125
+ msg.to = friend.jid
126
+ else
127
+ msg = Message.new(friend.jid)
128
+ msg.type = type
129
+ msg.body = message
130
+ end
131
+ send!(msg)
132
+ end
133
+ end
134
+
135
+ # Set your presence, with a message.
136
+ #
137
+ # Available values for presence are:
138
+ #
139
+ # * nil: online.
140
+ # * :chat: free for chat.
141
+ # * :away: away from the computer.
142
+ # * :dnd: do not disturb.
143
+ # * :xa: extended away.
144
+ #
145
+ # It's not possible to set an offline status - to do that, disconnect! :-)
146
+ def status(presence, message)
147
+ @presence = presence
148
+ @status_message = message
149
+ stat_msg = Presence.new(@presence, @status_message)
150
+ send!(stat_msg)
151
+ end
152
+
153
+ # Ask the users specified by jids for authorization (i.e., ask them to add
154
+ # you to their contact list). If you are already in the user's contact list,
155
+ # add() will not attempt to re-request authorization. In order to force
156
+ # re-authorization, first remove() the user, then re-add them.
157
+ #
158
+ # Example usage:
159
+ #
160
+ # jabber_simple.add("friend@friendosaurus.com")
161
+ #
162
+ # Because the authorization process might take a few seconds, or might
163
+ # never happen depending on when (and if) the user accepts your
164
+ # request, results are placed in the Jabber::Simple#new_subscriptions queue.
165
+ def add(*jids)
166
+ contacts(*jids) do |friend|
167
+ next if subscribed_to? friend
168
+ friend.ask_for_authorization!
169
+ end
170
+ end
171
+
172
+ # Remove the jabber users specified by jids from the contact list.
173
+ def remove(*jids)
174
+ contacts(*jids) do |unfriend|
175
+ unfriend.unsubscribe!
176
+ end
177
+ end
178
+
179
+ # Returns true if this Jabber account is subscribed to status updates for
180
+ # the jabber user jid, false otherwise.
181
+ def subscribed_to?(jid)
182
+ contacts(jid) do |contact|
183
+ return contact.subscribed?
184
+ end
185
+ end
186
+
187
+ # If contacts is a single contact, returns a Jabber::Contact object
188
+ # representing that user; if contacts is an array, returns an array of
189
+ # Jabber::Contact objects.
190
+ #
191
+ # When called with a block, contacts will yield each Jabber::Contact object
192
+ # in turn. This is mainly used internally, but exposed as an utility
193
+ # function.
194
+ def contacts(*contacts, &block)
195
+ @contacts ||= {}
196
+ contakts = []
197
+ contacts.each do |contact|
198
+ jid = contact.to_s
199
+ unless @contacts[jid]
200
+ @contacts[jid] = contact.respond_to?(:ask_for_authorization!) ? contact : Contact.new(self, contact)
201
+ end
202
+ yield @contacts[jid] if block_given?
203
+ contakts << @contacts[jid]
204
+ end
205
+ contakts.size > 1 ? contakts : contakts.first
206
+ end
207
+
208
+ # Returns true if the Jabber client is connected to the Jabber server,
209
+ # false otherwise.
210
+ def connected?
211
+ @client ||= nil
212
+ connected = @client.respond_to?(:is_connected?) && @client.is_connected?
213
+ return connected
214
+ end
215
+
216
+ # Returns an array of messages received since the last time
217
+ # received_messages was called. Passing a block will yield each message in
218
+ # turn, allowing you to break part-way through processing (especially
219
+ # useful when your message handling code is not thread-safe (e.g.,
220
+ # ActiveRecord).
221
+ #
222
+ # e.g.:
223
+ #
224
+ # jabber.received_messages do |message|
225
+ # puts "Received message from #{message.from}: #{message.body}"
226
+ # end
227
+ def received_messages(&block)
228
+ dequeue(:received_messages, &block)
229
+ end
230
+
231
+ # Returns true if there are unprocessed received messages waiting in the
232
+ # queue, false otherwise.
233
+ def received_messages?
234
+ !queue(:received_messages).empty?
235
+ end
236
+
237
+ # Returns an array of presence updates received since the last time
238
+ # presence_updates was called. Passing a block will yield each update in
239
+ # turn, allowing you to break part-way through processing (especially
240
+ # useful when your presence handling code is not thread-safe (e.g.,
241
+ # ActiveRecord).
242
+ #
243
+ # e.g.:
244
+ #
245
+ # jabber.presence_updates do |friend, new_presence|
246
+ # puts "Received presence update from #{friend}: #{new_presence}"
247
+ # end
248
+ def presence_updates(&block)
249
+ updates = []
250
+ @presence_mutex.synchronize do
251
+ dequeue(:presence_updates) do |friend|
252
+ presence = @presence_updates[friend]
253
+ next unless presence
254
+ new_update = [friend, presence[0], presence[1]]
255
+ yield new_update if block_given?
256
+ updates << new_update
257
+ @presence_updates.delete(friend)
258
+ end
259
+ end
260
+ return updates
261
+ end
262
+
263
+ # Returns true if there are unprocessed presence updates waiting in the
264
+ # queue, false otherwise.
265
+ def presence_updates?
266
+ !queue(:presence_updates).empty?
267
+ end
268
+
269
+ # Returns an array of subscription notifications received since the last
270
+ # time new_subscriptions was called. Passing a block will yield each update
271
+ # in turn, allowing you to break part-way through processing (especially
272
+ # useful when your subscription handling code is not thread-safe (e.g.,
273
+ # ActiveRecord).
274
+ #
275
+ # e.g.:
276
+ #
277
+ # jabber.new_subscriptions do |friend, presence|
278
+ # puts "Received presence update from #{friend.to_s}: #{presence}"
279
+ # end
280
+ def new_subscriptions(&block)
281
+ dequeue(:new_subscriptions, &block)
282
+ end
283
+
284
+ # Returns true if there are unprocessed presence updates waiting in the
285
+ # queue, false otherwise.
286
+ def new_subscriptions?
287
+ !queue(:new_subscriptions).empty?
288
+ end
289
+
290
+ # Returns an array of subscription notifications received since the last
291
+ # time subscription_requests was called. Passing a block will yield each update
292
+ # in turn, allowing you to break part-way through processing (especially
293
+ # useful when your subscription handling code is not thread-safe (e.g.,
294
+ # ActiveRecord).
295
+ #
296
+ # e.g.:
297
+ #
298
+ # jabber.subscription_requests do |friend, presence|
299
+ # puts "Received presence update from #{friend.to_s}: #{presence}"
300
+ # end
301
+ def subscription_requests(&block)
302
+ dequeue(:subscription_requests, &block)
303
+ end
304
+
305
+ # Returns true if auto-accept subscriptions (friend requests) is enabled
306
+ # (default), false otherwise.
307
+ def accept_subscriptions?
308
+ @accept_subscriptions = true if @accept_subscriptions.nil?
309
+ @accept_subscriptions
310
+ end
311
+
312
+ # Change whether or not subscriptions (friend requests) are automatically accepted.
313
+ def accept_subscriptions=(accept_status)
314
+ @accept_subscriptions = accept_status
315
+ end
316
+
317
+ # Direct access to the underlying Roster helper.
318
+ def roster
319
+ return @roster if @roster
320
+ self.roster = Roster::Helper.new(client)
321
+ end
322
+
323
+ # Direct access to the underlying Jabber client.
324
+ def client
325
+ connect!() unless connected?
326
+ @client
327
+ end
328
+
329
+ # Send a Jabber stanza over-the-wire.
330
+ def send!(msg)
331
+ attempts = 0
332
+ begin
333
+ attempts += 1
334
+ client.send(msg)
335
+ rescue Errno::EPIPE, IOError => e
336
+ sleep 1
337
+ disconnect
338
+ reconnect
339
+ retry unless attempts > 3
340
+ raise e
341
+ rescue Errno::ECONNRESET => e
342
+ sleep (attempts^2) * 60 + 60
343
+ disconnect
344
+ reconnect
345
+ retry unless attempts > 3
346
+ raise e
347
+ end
348
+ end
349
+
350
+ # Use this to force the client to reconnect after a force_disconnect.
351
+ def reconnect
352
+ @disconnected = false
353
+ connect!
354
+ end
355
+
356
+ # Use this to force the client to disconnect and not automatically
357
+ # reconnect.
358
+ def disconnect
359
+ disconnect!
360
+ end
361
+
362
+ # Queue messages for delivery once a user has accepted our authorization
363
+ # request. Works in conjunction with the deferred delivery thread.
364
+ #
365
+ # You can use this method if you want to manually add friends and still
366
+ # have the message queued for later delivery.
367
+ def deliver_deferred(jid, message, type)
368
+ msg = {:to => jid, :message => message, :type => type}
369
+ queue(:pending_messages) << [msg]
370
+ end
371
+
372
+ private
373
+
374
+ def client=(client)
375
+ self.roster = nil # ensure we clear the roster, since that's now associated with a different client.
376
+ @client = client
377
+ end
378
+
379
+ def roster=(new_roster)
380
+ @roster = new_roster
381
+ end
382
+
383
+ def connect!
384
+ raise ConnectionError, "Connections are disabled - use Jabber::Simple::force_connect() to reconnect." if @disconnected
385
+ # Pre-connect
386
+ @connect_mutex ||= Mutex.new
387
+
388
+ # don't try to connect if another thread is already connecting.
389
+ return if @connect_mutex.locked?
390
+
391
+ @connect_mutex.lock
392
+ disconnect!(false) if connected?
393
+
394
+ # Connect
395
+ jid = JID.new(@jid)
396
+ my_client = Client.new(@jid)
397
+ my_client.connect
398
+ my_client.auth(@password)
399
+ self.client = my_client
400
+
401
+ # Post-connect
402
+ register_default_callbacks
403
+ status(@presence, @status_message)
404
+ @connect_mutex.unlock
405
+ end
406
+
407
+ def disconnect!(auto_reconnect = true)
408
+ if client.respond_to?(:is_connected?) && client.is_connected?
409
+ begin
410
+ client.close
411
+ rescue Errno::EPIPE, IOError => e
412
+ # probably should log this.
413
+ nil
414
+ end
415
+ end
416
+ client = nil
417
+ @disconnected = auto_reconnect
418
+ end
419
+
420
+ def register_default_callbacks
421
+ client.add_message_callback do |message|
422
+ queue(:received_messages) << message unless message.body.nil?
423
+ end
424
+
425
+ roster.add_subscription_callback do |roster_item, presence|
426
+ if presence.type == :subscribed
427
+ queue(:new_subscriptions) << [roster_item, presence]
428
+ end
429
+ end
430
+
431
+ roster.add_subscription_request_callback do |roster_item, presence|
432
+ if accept_subscriptions?
433
+ roster.accept_subscription(presence.from)
434
+ else
435
+ queue(:subscription_requests) << [roster_item, presence]
436
+ end
437
+ end
438
+
439
+ @presence_updates = {}
440
+ @presence_mutex = Mutex.new
441
+ roster.add_presence_callback do |roster_item, old_presence, new_presence|
442
+ simple_jid = roster_item.jid.strip.to_s
443
+ presence = case new_presence.type
444
+ when nil then new_presence.show || :online
445
+ when :unavailable then :unavailable
446
+ else
447
+ nil
448
+ end
449
+
450
+ if presence && @presence_updates[simple_jid] != presence
451
+ queue(:presence_updates) << simple_jid
452
+ @presence_mutex.synchronize { @presence_updates[simple_jid] = [presence, new_presence.status] }
453
+ end
454
+ end
455
+ end
456
+
457
+ # This thread facilitates the delivery of messages to users who haven't yet
458
+ # accepted an invitation from us. When we attempt to deliver a message, if
459
+ # the user hasn't subscribed, we place the message in a queue for later
460
+ # delivery. Once a user has accepted our authorization request, we deliver
461
+ # any messages that have been queued up in the meantime.
462
+ def start_deferred_delivery_thread #:nodoc:
463
+ Thread.new {
464
+ loop {
465
+ messages = [queue(:pending_messages).pop].flatten
466
+ messages.each do |message|
467
+ if subscribed_to?(message[:to])
468
+ deliver(message[:to], message[:message], message[:type])
469
+ else
470
+ queue(:pending_messages) << message
471
+ end
472
+ end
473
+ sleep 10 * 60 # minutes
474
+ }
475
+ }
476
+ end
477
+
478
+ def queue(queue)
479
+ @queues ||= Hash.new { |h,k| h[k] = Queue.new }
480
+ @queues[queue]
481
+ end
482
+
483
+ def dequeue(queue, non_blocking = true, max_items = 100, &block)
484
+ queue_items = []
485
+ max_items.times do
486
+ queue_item = queue(queue).pop(non_blocking) rescue nil
487
+ break if queue_item.nil?
488
+ queue_items << queue_item
489
+ yield queue_item if block_given?
490
+ end
491
+ queue_items
492
+ end
493
+ end
494
+ end
495
+
496
+ true
497
+