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.
- data/LICENSE +459 -0
- data/README +29 -0
- data/bin/gnms +11 -0
- data/gnms.gemspec +70 -0
- data/lib/cmd_parse.rb +747 -0
- data/lib/config_global.rb +414 -0
- data/lib/contact.rb +171 -0
- data/lib/external/crypt/blowfish-tables.rb +190 -0
- data/lib/external/crypt/blowfish.rb +110 -0
- data/lib/external/crypt/cbc.rb +123 -0
- data/lib/external/crypt/stringxor.rb +27 -0
- data/lib/external/fog.rb +41 -0
- data/lib/external/jjmx.rb +45 -0
- data/lib/external/sms.rb +282 -0
- data/lib/external/xmpp4r-simple.rb +497 -0
- data/lib/format.rb +85 -0
- data/lib/gnms.rb +329 -0
- data/lib/gui/about_window.rb +177 -0
- data/lib/gui/adddelnode.rb +799 -0
- data/lib/gui/base_window.rb +789 -0
- data/lib/gui/canvas_map.rb +498 -0
- data/lib/gui/charts/gruff.rb +51 -0
- data/lib/gui/color_macro.rb +145 -0
- data/lib/gui/config_window.rb +2151 -0
- data/lib/gui/custom_plugin_window.rb +90 -0
- data/lib/gui/debug_window.rb +46 -0
- data/lib/gui/event_window.rb +469 -0
- data/lib/gui/find_node_window.rb +438 -0
- data/lib/gui/help_window.rb +47 -0
- data/lib/gui/ipcalculator.rb +147 -0
- data/lib/gui/jmx_ref_window.rb +309 -0
- data/lib/gui/link_view.rb +46 -0
- data/lib/gui/mib_browser.rb +585 -0
- data/lib/gui/node_host_property.rb +3712 -0
- data/lib/gui/node_host_view.rb +270 -0
- data/lib/gui/node_network_property.rb +1059 -0
- data/lib/gui/node_network_view.rb +186 -0
- data/lib/gui/node_view.rb +672 -0
- data/lib/gui/oid_ref_window.rb +288 -0
- data/lib/gui/snmptrap_ref_window.rb +288 -0
- data/lib/gui/sub/account_frame.rb +196 -0
- data/lib/gui/sub/contact_frame.rb +519 -0
- data/lib/gui/syslog_ref_window.rb +288 -0
- data/lib/gui/treenode_window.rb +320 -0
- data/lib/gui/update_window.rb +124 -0
- data/lib/gui/widgets.rb +169 -0
- data/lib/gui/wmi_ref_window.rb +286 -0
- data/lib/interface.rb +16 -0
- data/lib/ipcalc.rb +266 -0
- data/lib/link.rb +45 -0
- data/lib/main.rb +1091 -0
- data/lib/monitor/client/custom/custom_monitoring.rb +40 -0
- data/lib/monitor/client/jmx/jmx_attr_monitoring.rb +36 -0
- data/lib/monitor/client/jmx/jmx_monitoring.rb +83 -0
- data/lib/monitor/client/jmx/jmx_ref.rb +114 -0
- data/lib/monitor/client/snmp/mibs/SNMPv2-MIB.txt +854 -0
- data/lib/monitor/client/snmp/mibs/SNMPv2-SMI.txt +344 -0
- data/lib/monitor/client/snmp/oid_ref.rb +141 -0
- data/lib/monitor/client/snmp/snmp.rb +178 -0
- data/lib/monitor/client/snmp/snmp_monitoring.rb +58 -0
- data/lib/monitor/client/wmi/wmi.rb +139 -0
- data/lib/monitor/client/wmi/wmi_monitoring.rb +60 -0
- data/lib/monitor/client/wmi/wmi_ref.rb +141 -0
- data/lib/monitor/custom_plugin.rb +50 -0
- data/lib/monitor/server/msg_buffer.rb +39 -0
- data/lib/monitor/server/snmp/snmptrap_analyzer.rb +81 -0
- data/lib/monitor/server/snmp/snmptrap_capture.rb +26 -0
- data/lib/monitor/server/snmp/snmptrap_monitoring.rb +32 -0
- data/lib/monitor/server/snmp/snmptrap_ref.rb +135 -0
- data/lib/monitor/server/snmp/snmptrap_server.rb +50 -0
- data/lib/monitor/server/syslog/syslog_analyzer.rb +141 -0
- data/lib/monitor/server/syslog/syslog_capture.rb +28 -0
- data/lib/monitor/server/syslog/syslog_monitoring.rb +32 -0
- data/lib/monitor/server/syslog/syslog_ref.rb +136 -0
- data/lib/monitor/server/syslog/syslog_server.rb +43 -0
- data/lib/node.rb +1020 -0
- data/lib/node_db.rb +2668 -0
- data/lib/node_host.rb +215 -0
- data/lib/node_listener.rb +639 -0
- data/lib/node_network.rb +269 -0
- data/lib/node_property.rb +39 -0
- data/lib/notify/alarm.rb +247 -0
- data/lib/notify/irc_bot.rb +167 -0
- data/lib/notify/xmpp_bot.rb +107 -0
- data/lib/service.rb +36 -0
- data/lib/version.rb +8 -0
- data/pixmap/bg/map_bg.jpg +0 -0
- data/pixmap/bg/map_bg2.jpg +0 -0
- data/pixmap/logo.jpg +0 -0
- data/pixmap/logo_icon.png +0 -0
- data/pixmap/os/3com.png +0 -0
- data/pixmap/os/aix.gif +0 -0
- data/pixmap/os/amiga.png +0 -0
- data/pixmap/os/beos.png +0 -0
- data/pixmap/os/cisco.png +0 -0
- data/pixmap/os/freebsd.png +0 -0
- data/pixmap/os/hp.png +0 -0
- data/pixmap/os/hurd.gif +0 -0
- data/pixmap/os/irix.gif +0 -0
- data/pixmap/os/linux.png +0 -0
- data/pixmap/os/mac.png +0 -0
- data/pixmap/os/netbsd.gif +0 -0
- data/pixmap/os/netware.png +0 -0
- data/pixmap/os/openbsd.png +0 -0
- data/pixmap/os/os2warp.gif +0 -0
- data/pixmap/os/qnx.gif +0 -0
- data/pixmap/os/sco.gif +0 -0
- data/pixmap/os/sgi.xpm +111 -0
- data/pixmap/os/solaris.gif +0 -0
- data/pixmap/os/unix.gif +0 -0
- data/pixmap/os/unknown.png +0 -0
- data/pixmap/os/vms.xpm +345 -0
- data/pixmap/os/windows.png +0 -0
- data/pixmap/type/cloud.png +0 -0
- data/pixmap/type/device.png +0 -0
- data/pixmap/type/firewall.png +0 -0
- data/pixmap/type/gateway.png +0 -0
- data/pixmap/type/home.png +0 -0
- data/pixmap/type/host.png +0 -0
- data/pixmap/type/network.png +0 -0
- data/pixmap/type/printer.png +0 -0
- data/pixmap/type/router.png +0 -0
- data/pixmap/type/server.png +0 -0
- data/pixmap/type/switch.png +0 -0
- data/pixmap/type/vm.png +0 -0
- data/plugins/Defaultme.rb +7 -0
- data/plugins/DnsCheck.rb +46 -0
- data/plugins/FooMonitor.rb +26 -0
- data/plugins/HttpVirtualHostMonitor.rb +43 -0
- data/plugins/MysqlShowStatusMonitor.rb +64 -0
- data/plugins/PgsqlPercentConnectionMonitor.rb +72 -0
- data/plugins/SshCpuLoadMonitor.rb +85 -0
- data/plugins/SshDiskMonitor.rb +69 -0
- data/plugins/SshFgtAvExpiredMonitor.rb +75 -0
- data/plugins/SshPercentUsedRamMonitor.rb +74 -0
- data/plugins/TelnetCpuLoadMonitor.rb +69 -0
- data/plugins/example1.rb +15 -0
- data/plugins/example2.rb +7 -0
- data/sound/beep.wav +0 -0
- metadata +378 -0
data/lib/external/fog.rb
ADDED
@@ -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
|
+
}
|
data/lib/external/sms.rb
ADDED
@@ -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
|
+
|