watobo 0.9.16 → 0.9.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG.md +31 -0
- data/lib/watobo.rb +3 -5
- data/lib/watobo/core/ca.rb +27 -19
- data/lib/watobo/core/cookie.rb +10 -6
- data/lib/watobo/core/parameter.rb +13 -1
- data/lib/watobo/core/request.rb +24 -4
- data/lib/watobo/gui/chatviewer_frame.rb +9 -2
- data/lib/watobo/gui/conversation_table_ctrl2.rb +16 -1
- data/lib/watobo/gui/custom_viewer.rb +368 -0
- data/lib/watobo/gui/main_window.rb +1 -0
- data/lib/watobo/gui/manual_request_editor.rb +5 -11
- data/lib/watobo/gui/table_editor.rb +55 -48
- data/lib/watobo/http.rb +25 -0
- data/lib/watobo/http/cookies/cookies.rb +83 -20
- data/lib/watobo/http/url/url.rb +4 -1
- data/lib/watobo/http/xml/xml.rb +143 -0
- data/lib/watobo/interceptor/proxy.rb +1 -2
- data/lib/watobo/mixins/httpparser.rb +17 -5
- data/lib/watobo/mixins/shapers.rb +2 -11
- data/lib/watobo/{http_socket.rb → sockets.rb} +2 -2
- data/lib/watobo/{http_socket → sockets}/agent.rb +0 -0
- data/lib/watobo/{http_socket → sockets}/client_socket.rb +21 -7
- data/lib/watobo/{http_socket → sockets}/connection.rb +0 -0
- data/lib/watobo/{http_socket → sockets}/http_socket.rb +0 -0
- data/lib/watobo/{http_socket → sockets}/ntlm_auth.rb +0 -0
- data/modules/active/sqlinjection/sql_boolean.rb +29 -138
- data/modules/active/sqlinjection/sqli_error.rb +13 -67
- data/modules/passive/cookie_xss.rb +16 -26
- data/modules/passive/possible_login.rb +5 -8
- data/plugins/filefinder/dbs/sap.db +157 -0
- data/plugins/wshell/lib/core.rb +4 -2
- metadata +11 -8
data/CHANGELOG.md
CHANGED
|
@@ -1,8 +1,35 @@
|
|
|
1
|
+
Version 0.9.17
|
|
2
|
+
===
|
|
3
|
+
News
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
**General**
|
|
7
|
+
|
|
8
|
+
* changed parameter parsing for better handling
|
|
9
|
+
* Boolean-SQL check now also takes xml parameters for testing
|
|
10
|
+
* new appearance of CA certificates
|
|
11
|
+
|
|
12
|
+
**Manual Request Editor**
|
|
13
|
+
|
|
14
|
+
* XML parser, xml-request parameters available in table
|
|
15
|
+
|
|
16
|
+
Fixes
|
|
17
|
+
---
|
|
18
|
+
**General**
|
|
19
|
+
|
|
20
|
+
* request line removed on `remove_header` regex match
|
|
21
|
+
* double insert of Content-Length header
|
|
22
|
+
* bad markdown format of CHANGELOG
|
|
23
|
+
* wrong parameter parsing if value contains '=' sign
|
|
24
|
+
* new custom response viewer; now you can code your own handler, e.g. `lambda{ |response| return response.content_type }`
|
|
25
|
+
* added table view on request viewer
|
|
26
|
+
|
|
1
27
|
Version 0.9.16
|
|
2
28
|
===
|
|
3
29
|
Fixes
|
|
4
30
|
---
|
|
5
31
|
**General**
|
|
32
|
+
|
|
6
33
|
* double insert of Content-Length header
|
|
7
34
|
* bad markdown format of CHANGELOG
|
|
8
35
|
|
|
@@ -11,6 +38,7 @@ Version 0.9.15
|
|
|
11
38
|
Fixes
|
|
12
39
|
---
|
|
13
40
|
**General**
|
|
41
|
+
|
|
14
42
|
* improved socket handling
|
|
15
43
|
* fixed some UTF-8 issues in passive modules
|
|
16
44
|
* added application/octet-stream to pass-through content-types
|
|
@@ -18,12 +46,15 @@ Fixes
|
|
|
18
46
|
* setting/replacing http-headers is now case-in-sensitive
|
|
19
47
|
|
|
20
48
|
**Passive Modules**
|
|
49
|
+
|
|
21
50
|
* fixed `Disclosure_ipaddr`; now all IPs inside body are reported
|
|
22
51
|
|
|
23
52
|
**Active Modules**
|
|
53
|
+
|
|
24
54
|
* Domino DB enumeration will now run on all requests
|
|
25
55
|
|
|
26
56
|
**Crawler Plugin**
|
|
57
|
+
|
|
27
58
|
* extended allowed/excluded URL checks on full url path /w query
|
|
28
59
|
|
|
29
60
|
News
|
data/lib/watobo.rb
CHANGED
|
@@ -47,16 +47,14 @@ require 'watobo/utils'
|
|
|
47
47
|
require 'watobo/mixins'
|
|
48
48
|
require 'watobo/config'
|
|
49
49
|
require 'watobo/defaults'
|
|
50
|
+
require 'watobo/http'
|
|
50
51
|
require 'watobo/core'
|
|
51
52
|
require 'watobo/externals'
|
|
52
53
|
require 'watobo/adapters'
|
|
53
54
|
require 'watobo/framework'
|
|
54
|
-
require 'watobo/http/data/data'
|
|
55
|
-
require 'watobo/http/url/url'
|
|
56
|
-
require 'watobo/http/cookies/cookies'
|
|
57
55
|
require 'watobo/parser'
|
|
58
56
|
require 'watobo/interceptor'
|
|
59
|
-
require 'watobo/
|
|
57
|
+
require 'watobo/sockets'
|
|
60
58
|
|
|
61
59
|
# WORKAROUND FOR LINUX :(
|
|
62
60
|
dont_know_why_REQUIRE_hangs = Mechanize.new
|
|
@@ -64,7 +62,7 @@ dont_know_why_REQUIRE_hangs = Mechanize.new
|
|
|
64
62
|
# @private
|
|
65
63
|
module Watobo#:nodoc: all #:nodoc: all
|
|
66
64
|
|
|
67
|
-
VERSION = "0.9.
|
|
65
|
+
VERSION = "0.9.17"
|
|
68
66
|
|
|
69
67
|
def self.base_directory
|
|
70
68
|
@base_directory ||= ""
|
data/lib/watobo/core/ca.rb
CHANGED
|
@@ -27,6 +27,20 @@ module Watobo#:nodoc: all
|
|
|
27
27
|
@hostname = %x('hostname').strip
|
|
28
28
|
@hostname = "watobo" if @hostname.empty?
|
|
29
29
|
@domain = "#{@hostname}.watobo.local"
|
|
30
|
+
|
|
31
|
+
def self.dh_key
|
|
32
|
+
dh_filename = File.join(@ca_config[:CA_dir], "watobo_dh.key")
|
|
33
|
+
unless File.exist? dh_filename
|
|
34
|
+
#puts "* no dh key file found"
|
|
35
|
+
File.open(dh_filename,"w") do |fh|
|
|
36
|
+
print "* creating SSL key (DH 1024) ... "
|
|
37
|
+
fh.write OpenSSL::PKey::DH.new(1024).to_pem
|
|
38
|
+
print " DONE\r\n"
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
OpenSSL::PKey::DH.new(File.read(dh_filename))
|
|
42
|
+
end
|
|
43
|
+
|
|
30
44
|
def self.ca_ready?
|
|
31
45
|
return false unless File.exists? @ca_config[:CA_dir]
|
|
32
46
|
return false unless File.exists? @ca_config[:private_dir]
|
|
@@ -63,8 +77,10 @@ module Watobo#:nodoc: all
|
|
|
63
77
|
:crl_days => 14,
|
|
64
78
|
:name => [
|
|
65
79
|
['C', 'DE', OpenSSL::ASN1::PRINTABLESTRING],
|
|
66
|
-
['O', @domain, OpenSSL::ASN1::UTF8STRING],
|
|
67
|
-
['
|
|
80
|
+
#['O', @domain, OpenSSL::ASN1::UTF8STRING],
|
|
81
|
+
['O', "WATOBO", OpenSSL::ASN1::UTF8STRING],
|
|
82
|
+
# ['OU', @hostname, OpenSSL::ASN1::UTF8STRING],
|
|
83
|
+
['OU', "WATOBO CA", OpenSSL::ASN1::UTF8STRING]
|
|
68
84
|
]
|
|
69
85
|
}
|
|
70
86
|
|
|
@@ -85,10 +101,10 @@ module Watobo#:nodoc: all
|
|
|
85
101
|
#print "Create Certificate ..."
|
|
86
102
|
cert = OpenSSL::X509::Certificate.new
|
|
87
103
|
#puts "done!"
|
|
88
|
-
name = @ca_config[:name].dup << ['CN', '
|
|
104
|
+
name = @ca_config[:name].dup << ['CN', 'Watobo']
|
|
89
105
|
|
|
90
106
|
cert.subject = cert.issuer = OpenSSL::X509::Name.new(name)
|
|
91
|
-
cert.not_before = Time.now
|
|
107
|
+
cert.not_before = Time.now - 24 * 60 * 60
|
|
92
108
|
cert.not_after = Time.now + @ca_config[:ca_cert_days] * 24 * 60 * 60
|
|
93
109
|
cert.public_key = keypair.public_key
|
|
94
110
|
cert.serial = 0x0
|
|
@@ -130,6 +146,8 @@ module Watobo#:nodoc: all
|
|
|
130
146
|
end
|
|
131
147
|
|
|
132
148
|
puts "Done generating certificate for #{cert.subject}"
|
|
149
|
+
puts ">> create DH key ..."
|
|
150
|
+
dh_key
|
|
133
151
|
else
|
|
134
152
|
#puts "Open Cert File ..."
|
|
135
153
|
raw = File.read @ca_config[:cert_file] # DER- or PEM-encoded
|
|
@@ -326,7 +344,7 @@ module Watobo#:nodoc: all
|
|
|
326
344
|
ex << ef.create_extension("authorityInfoAccess",
|
|
327
345
|
"OCSP;" << @ca_config[:ocsp_location])
|
|
328
346
|
end
|
|
329
|
-
|
|
347
|
+
# cert.extensions = ex
|
|
330
348
|
cert.sign ca_keypair, OpenSSL::Digest::SHA1.new
|
|
331
349
|
|
|
332
350
|
# backup_cert_file = @ca_config[:backup_certs_dir] + "/cert_#{cert.serial}.pem"
|
|
@@ -360,8 +378,9 @@ module Watobo#:nodoc: all
|
|
|
360
378
|
name = @ca_config[:name].dup
|
|
361
379
|
case cert_config[:type]
|
|
362
380
|
when 'server' then
|
|
363
|
-
|
|
364
|
-
|
|
381
|
+
# name << ['OU', 'Watobo CA']
|
|
382
|
+
name << ['CN', cert_config[:hostname]]
|
|
383
|
+
#name << ['CN', "WATOBO"]
|
|
365
384
|
when 'client' then
|
|
366
385
|
name << ['CN', cert_config[:user]]
|
|
367
386
|
name << ['emailAddress', cert_config[:email]]
|
|
@@ -396,17 +415,6 @@ module Watobo#:nodoc: all
|
|
|
396
415
|
return csr_file
|
|
397
416
|
end
|
|
398
417
|
|
|
399
|
-
|
|
400
|
-
dh_filename = File.join(@ca_config[:CA_dir], "watobo_dh.key")
|
|
401
|
-
unless File.exist? dh_filename
|
|
402
|
-
#puts "* no dh key file found"
|
|
403
|
-
File.open(dh_filename,"w") do |fh|
|
|
404
|
-
puts "* creating SSL key (DH 1024) ... "
|
|
405
|
-
fh.write OpenSSL::PKey::DH.new(1024).to_pem
|
|
406
|
-
print " DONE\r\n"
|
|
407
|
-
end
|
|
408
|
-
end
|
|
409
|
-
OpenSSL::PKey::DH.new(File.read(dh_filename))
|
|
410
|
-
end
|
|
418
|
+
|
|
411
419
|
end
|
|
412
420
|
end
|
data/lib/watobo/core/cookie.rb
CHANGED
|
@@ -30,15 +30,16 @@ module Watobo#:nodoc: all
|
|
|
30
30
|
attr :path
|
|
31
31
|
attr :secure
|
|
32
32
|
attr :http_only
|
|
33
|
-
|
|
33
|
+
|
|
34
|
+
def to_s
|
|
34
35
|
"#{@name}=#{@value}"
|
|
35
|
-
end
|
|
36
|
+
end
|
|
36
37
|
|
|
37
|
-
def initialize(
|
|
38
|
+
def initialize(prefs)
|
|
38
39
|
@secure = false
|
|
39
40
|
@http_only = false
|
|
40
41
|
|
|
41
|
-
if
|
|
42
|
+
if prefs.respond_to? :has_key?
|
|
42
43
|
@secure = prefs.has_key?(:secure) ? prefs[:secure] : false
|
|
43
44
|
@http_only = prefs.has_key?(:http_only) ? prefs[:http_only] : false
|
|
44
45
|
@location = :cookie
|
|
@@ -46,11 +47,14 @@ module Watobo#:nodoc: all
|
|
|
46
47
|
@name = prefs[:name]
|
|
47
48
|
@value = prefs[:value]
|
|
48
49
|
else
|
|
49
|
-
|
|
50
|
+
puts "= NEW COOKIE ="
|
|
51
|
+
puts prefs
|
|
52
|
+
puts prefs.class
|
|
53
|
+
chunks = prefs.split(";")
|
|
50
54
|
# first chunk
|
|
51
55
|
@name, @value = chunks.first.split(":").last.split("=")
|
|
52
56
|
|
|
53
|
-
m =
|
|
57
|
+
m = prefs.match(/path=([^;]*)/)
|
|
54
58
|
@path = m.nil? ? "" : m[1].strip
|
|
55
59
|
@secure = true if chunks.select{|c| c =~ /Secure/i }
|
|
56
60
|
@http_only = true if chunks.select{|c| c =~ /HttpOnly/i }
|
|
@@ -38,7 +38,8 @@ module Watobo#:nodoc: all
|
|
|
38
38
|
def initialize(prefs)
|
|
39
39
|
@location = nil
|
|
40
40
|
@name = prefs[:name]
|
|
41
|
-
@value = prefs[:value]
|
|
41
|
+
@value = prefs[:value]
|
|
42
|
+
@prefs = prefs
|
|
42
43
|
end
|
|
43
44
|
end
|
|
44
45
|
|
|
@@ -63,4 +64,15 @@ module Watobo#:nodoc: all
|
|
|
63
64
|
@location = :cookie
|
|
64
65
|
end
|
|
65
66
|
end
|
|
67
|
+
|
|
68
|
+
class XmlParameter < Parameter
|
|
69
|
+
attr :parent
|
|
70
|
+
attr :namespace
|
|
71
|
+
def initialize(prefs)
|
|
72
|
+
super prefs
|
|
73
|
+
@location = :xml
|
|
74
|
+
@parent = prefs.has_key?(:parent) ? prefs[:parent] : ""
|
|
75
|
+
@namespace = prefs.has_key?(:namespace) ? prefs[:namespace] : nil
|
|
76
|
+
end
|
|
77
|
+
end
|
|
66
78
|
end
|
data/lib/watobo/core/request.rb
CHANGED
|
@@ -40,7 +40,10 @@ module Watobo#:nodoc: all
|
|
|
40
40
|
attr :data
|
|
41
41
|
attr :url
|
|
42
42
|
attr :header
|
|
43
|
-
|
|
43
|
+
# attr :cookies
|
|
44
|
+
|
|
45
|
+
include Watobo::HTTP::Cookies::Mixin
|
|
46
|
+
include Watobo::HTTP::Xml::Mixin
|
|
44
47
|
|
|
45
48
|
def self.create request
|
|
46
49
|
request.extend Watobo::Mixin::Parser::Url
|
|
@@ -79,10 +82,23 @@ module Watobo#:nodoc: all
|
|
|
79
82
|
end
|
|
80
83
|
end
|
|
81
84
|
|
|
82
|
-
def parameters(*locations)
|
|
85
|
+
def parameters(*locations, &block)
|
|
86
|
+
param_locations = [ :url, :data, :wwwform, :xml, :cookies ]
|
|
87
|
+
unless locations.empty?
|
|
88
|
+
param_locations.select!{ |loc| locations.include? loc }
|
|
89
|
+
end
|
|
90
|
+
|
|
83
91
|
parms = []
|
|
84
|
-
parms.concat @
|
|
85
|
-
parms.concat
|
|
92
|
+
parms.concat @url.parameters if param_locations.include?(:url)
|
|
93
|
+
parms.concat cookies.parameters if param_locations.include?(:cookies)
|
|
94
|
+
parms.concat @data.parameters if self.is_wwwform? and ( param_locations.include?(:data) or param_locations.include?(:wwwform) )
|
|
95
|
+
|
|
96
|
+
parms.concat xml.parameters if self.is_xml? and param_locations.include?(:xml)
|
|
97
|
+
if block_given?
|
|
98
|
+
parms.each do |p|
|
|
99
|
+
yield p
|
|
100
|
+
end
|
|
101
|
+
end
|
|
86
102
|
parms
|
|
87
103
|
end
|
|
88
104
|
|
|
@@ -94,6 +110,10 @@ module Watobo#:nodoc: all
|
|
|
94
110
|
@data.set parm
|
|
95
111
|
when :url
|
|
96
112
|
@url.set parm
|
|
113
|
+
when :xml
|
|
114
|
+
xml.set parm
|
|
115
|
+
when :cookie
|
|
116
|
+
cookies.set parm
|
|
97
117
|
end
|
|
98
118
|
true
|
|
99
119
|
end
|
|
@@ -358,6 +358,10 @@ module Watobo#:nodoc: all
|
|
|
358
358
|
tab_frame = FXVerticalFrame.new(@tabBook, :opts => LAYOUT_FILL_X|LAYOUT_FILL_Y|FRAME_RAISED)
|
|
359
359
|
@hexViewer = HexViewer.new(tab_frame)
|
|
360
360
|
@viewers.push @hexViewer
|
|
361
|
+
|
|
362
|
+
FXTabItem.new(@tabBook, "Table", nil)
|
|
363
|
+
tab_frame = FXVerticalFrame.new(@tabBook, :opts => LAYOUT_FILL_X|LAYOUT_FILL_Y|FRAME_RAISED)
|
|
364
|
+
@viewers << Watobo::Gui::TableEditorFrame.new(tab_frame, :opts => LAYOUT_FILL_X|LAYOUT_FILL_Y|FRAME_SUNKEN|FRAME_THICK, :padding => 0)
|
|
361
365
|
end
|
|
362
366
|
|
|
363
367
|
end
|
|
@@ -442,10 +446,13 @@ module Watobo#:nodoc: all
|
|
|
442
446
|
@hexViewer = HexViewer.new(tab_frame)
|
|
443
447
|
@viewers << @hexViewer
|
|
444
448
|
|
|
445
|
-
|
|
446
|
-
|
|
449
|
+
FXTabItem.new(@tabBook, "HTML", nil)
|
|
447
450
|
@html_viewer = HTMLViewerFrame.new(@tabBook, :opts => LAYOUT_FILL_X|LAYOUT_FILL_Y|FRAME_RAISED)
|
|
448
451
|
@viewers << @html_viewer
|
|
452
|
+
|
|
453
|
+
FXTabItem.new(@tabBook, "Custom", nil)
|
|
454
|
+
@viewers << CustomViewer.new(@tabBook, :opts => LAYOUT_FILL_X|LAYOUT_FILL_Y|FRAME_RAISED)
|
|
455
|
+
|
|
449
456
|
end
|
|
450
457
|
|
|
451
458
|
end
|
|
@@ -303,8 +303,23 @@ module Watobo#:nodoc: all
|
|
|
303
303
|
update_text
|
|
304
304
|
# l = FXLabel.new(@filter_info, "Test")
|
|
305
305
|
|
|
306
|
-
|
|
306
|
+
bframe = FXVerticalFrame.new(f, :opts => LAYOUT_FILL_Y, :padding => 0)
|
|
307
|
+
@table_option_autoscroll = FXCheckButton.new(bframe, "autoscroll", nil, 0, ICON_BEFORE_TEXT|LAYOUT_SIDE_LEFT)
|
|
307
308
|
@table_option_autoscroll.setCheck(true)
|
|
309
|
+
|
|
310
|
+
iframe = FXHorizontalFrame.new(bframe, :opts => LAYOUT_FILL_X, :padding => 0 )
|
|
311
|
+
|
|
312
|
+
FXButton.new(iframe, "", ICON_BTN_DOWN, nil, 0, FRAME_RAISED|FRAME_THICK|LAYOUT_RIGHT).connect(SEL_COMMAND) {
|
|
313
|
+
@table.scrollDown() unless @table.nil?
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
FXButton.new(iframe, "", ICON_BTN_UP, nil, 0, FRAME_RAISED|FRAME_THICK|LAYOUT_RIGHT).connect(SEL_COMMAND) {
|
|
317
|
+
@table.scrollUp() unless @table.nil?
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
|
|
308
323
|
|
|
309
324
|
|
|
310
325
|
|
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
# .
|
|
2
|
+
# custom_viewer.rb
|
|
3
|
+
#
|
|
4
|
+
# Copyright 2013 by siberas, http://www.siberas.de
|
|
5
|
+
#
|
|
6
|
+
# This file is part of WATOBO (Web Application Tool Box)
|
|
7
|
+
# http://watobo.sourceforge.com
|
|
8
|
+
#
|
|
9
|
+
# WATOBO is free software; you can redistribute it and/or modify
|
|
10
|
+
# it under the terms of the GNU General Public License as published by
|
|
11
|
+
# the Free Software Foundation version 2 of the License.
|
|
12
|
+
#
|
|
13
|
+
# WATOBO is distributed in the hope that it will be useful,
|
|
14
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
15
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
16
|
+
# GNU General Public License for more details.
|
|
17
|
+
#
|
|
18
|
+
# You should have received a copy of the GNU General Public License
|
|
19
|
+
# along with WATOBO; if not, write to the Free Software
|
|
20
|
+
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
21
|
+
# .
|
|
22
|
+
# @private
|
|
23
|
+
module Watobo#:nodoc: all
|
|
24
|
+
module Gui
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class CustomViewer < FXVerticalFrame
|
|
28
|
+
SEL_TYPE_GREP = 0
|
|
29
|
+
SEL_TYPE_HIGHLIGHT = 1
|
|
30
|
+
attr_accessor :max_len
|
|
31
|
+
def style=(new_style)
|
|
32
|
+
@simple_text_view.style = new_style
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def editable=(value)
|
|
36
|
+
@simple_text_view.editable = value
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def setText(text, prefs={})
|
|
40
|
+
|
|
41
|
+
normalized_text = text
|
|
42
|
+
if text.is_a? Watobo::Request or text.is_a? Watobo::Response
|
|
43
|
+
return false unless @handler.respond_to? :call
|
|
44
|
+
result = call_handler(text)
|
|
45
|
+
normalized_text = result
|
|
46
|
+
else
|
|
47
|
+
return false
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
@text = normalized_text
|
|
51
|
+
#@text = text
|
|
52
|
+
@simple_text_view.max_len = -1
|
|
53
|
+
@simple_text_view.setText(normalized_text, prefs)
|
|
54
|
+
@match_pos_label.text = "0/0"
|
|
55
|
+
@match_pos_label.textColor = 'grey'
|
|
56
|
+
|
|
57
|
+
applyFilter() if @auto_apply_cbtn.checked?
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def highlight(pattern)
|
|
61
|
+
highlightPattern(pattern)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def setFont(font_type=nil, size=nil)
|
|
65
|
+
@simple_text_view.setFont(font_type, size)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def getText
|
|
69
|
+
@simple_text_view.textbox.to_s
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def initialize(owner, opts)
|
|
73
|
+
super(owner, opts)
|
|
74
|
+
|
|
75
|
+
@text_matches = []
|
|
76
|
+
|
|
77
|
+
@style = 2 # default style
|
|
78
|
+
@text = ''
|
|
79
|
+
@max_len = 5000
|
|
80
|
+
@filter_mode = SEL_TYPE_HIGHLIGHT
|
|
81
|
+
@cur_match_pos = 0
|
|
82
|
+
@text_dt = FXDataTarget.new('')
|
|
83
|
+
@filter_dt = FXDataTarget.new('')
|
|
84
|
+
|
|
85
|
+
@handler = nil
|
|
86
|
+
@handler_file = nil
|
|
87
|
+
@handler_path = nil
|
|
88
|
+
|
|
89
|
+
handler_ctrl_frame = FXHorizontalFrame.new(self, :opts => LAYOUT_FILL_X, :padding => 0)
|
|
90
|
+
@handler_status_lbl = FXLabel.new(handler_ctrl_frame, "No handler!")
|
|
91
|
+
@handler_status_lbl.textColor = "red"
|
|
92
|
+
add_handler_btn = FXButton.new(handler_ctrl_frame, "add", nil, nil, 0, FRAME_RAISED|LAYOUT_FILL_Y|LAYOUT_RIGHT)
|
|
93
|
+
add_handler_btn.connect(SEL_COMMAND){ add_handler }
|
|
94
|
+
reload_handler_btn = FXButton.new(handler_ctrl_frame, "reload", nil, nil, 0, FRAME_RAISED|LAYOUT_FILL_Y|LAYOUT_RIGHT)
|
|
95
|
+
reload_handler_btn.connect(SEL_COMMAND){ load_handler(@handler_file) }
|
|
96
|
+
reset_handler_btn = FXButton.new(handler_ctrl_frame, "reset", nil, nil, 0, FRAME_RAISED|LAYOUT_FILL_Y|LAYOUT_RIGHT)
|
|
97
|
+
reset_handler_btn.connect(SEL_COMMAND){
|
|
98
|
+
@handler = nil
|
|
99
|
+
@handler_file = nil
|
|
100
|
+
@handler_status_lbl = FXLabel.new(handler_ctrl_frame, "No handler!")
|
|
101
|
+
@handler_status_lbl.textColor = "red"
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
text_view_header = FXHorizontalFrame.new(self, :opts => LAYOUT_FILL_X|LAYOUT_SIDE_BOTTOM|LAYOUT_FIX_HEIGHT,:height => 24, :padding => 0)
|
|
105
|
+
|
|
106
|
+
#@auto_apply_cbtn.connect(SEL_COMMAND, method(:onInterceptChanged))
|
|
107
|
+
|
|
108
|
+
pmatch_btn = FXButton.new(text_view_header, "<", nil, nil, 0, FRAME_RAISED|LAYOUT_FILL_Y)
|
|
109
|
+
@match_pos_label = FXLabel.new(text_view_header, "0/0", :opts => LAYOUT_FILL_Y)
|
|
110
|
+
@match_pos_label.textColor = 'grey'
|
|
111
|
+
pmatch_btn.connect(SEL_COMMAND) { gotoPrevMatch() }
|
|
112
|
+
nmatch_btn = FXButton.new(text_view_header, ">", nil, nil, 0, FRAME_RAISED|LAYOUT_FILL_Y)
|
|
113
|
+
nmatch_btn.connect(SEL_COMMAND) { gotoNextMatch() }
|
|
114
|
+
|
|
115
|
+
# @filter_text = FXTextField.new(text_view_header, 10,
|
|
116
|
+
# :target => @filter_dt, :selector => FXDataTarget::ID_VALUE,
|
|
117
|
+
# :opts => FRAME_SUNKEN|FRAME_THICK|LAYOUT_FILL_X|LAYOUT_FILL_Y)
|
|
118
|
+
|
|
119
|
+
@filter_text = FXComboBox.new(text_view_header, 20, @filter_dt, 0, FRAME_SUNKEN|FRAME_THICK|LAYOUT_SIDE_TOP|LAYOUT_FILL_X)
|
|
120
|
+
@filter_text.connect(SEL_COMMAND){
|
|
121
|
+
applyFilter()
|
|
122
|
+
addFilterHistory()
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
@filter_text.connect(SEL_CHANGED) {
|
|
126
|
+
applyFilter()
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
menu = FXMenuPane.new(self)
|
|
130
|
+
FXMenuCommand.new(menu, "&Highlight").connect(SEL_COMMAND){
|
|
131
|
+
@filter_mode = SEL_TYPE_HIGHLIGHT
|
|
132
|
+
applyFilter()
|
|
133
|
+
@mode_btn.text = "Highlight"
|
|
134
|
+
}#, method(:switchMethod))
|
|
135
|
+
FXMenuCommand.new(menu, "&Grep").connect(SEL_COMMAND){
|
|
136
|
+
@filter_mode = SEL_TYPE_GREP
|
|
137
|
+
applyFilter()
|
|
138
|
+
@mode_btn.text = "Grep"
|
|
139
|
+
}#, method(:switchMethod))
|
|
140
|
+
|
|
141
|
+
@auto_apply_cbtn = FXCheckButton.new(text_view_header, "auto-apply", nil, 0, ICON_BEFORE_TEXT|LAYOUT_SIDE_TOP|LAYOUT_RIGHT|LAYOUT_FILL_Y)
|
|
142
|
+
@mode_btn = FXMenuButton.new(text_view_header, "Highlight", nil, menu,
|
|
143
|
+
:opts=> MENUBUTTON_DOWN|FRAME_RAISED|FRAME_THICK|ICON_AFTER_TEXT|LAYOUT_RIGHT|LAYOUT_FILL_Y)
|
|
144
|
+
|
|
145
|
+
reset_button = FXButton.new(text_view_header, "&Reset", nil, nil, 0, FRAME_RAISED|FRAME_THICK|LAYOUT_FILL_Y)
|
|
146
|
+
reset_button.connect(SEL_COMMAND){ resetFilter() }
|
|
147
|
+
|
|
148
|
+
text_box_frame = FXVerticalFrame.new(self, LAYOUT_FILL_X|LAYOUT_FILL_Y|FRAME_SUNKEN|FRAME_THICK, :padding => 0)
|
|
149
|
+
|
|
150
|
+
@simple_text_view = SimpleTextView.new(text_box_frame, :opts => LAYOUT_FILL_X|LAYOUT_FILL_Y,:padding => 0)
|
|
151
|
+
@simple_text_view.style = 1
|
|
152
|
+
@simple_text_view.editable = false
|
|
153
|
+
@simple_text_view.textStyle -= TEXT_WORDWRAP
|
|
154
|
+
|
|
155
|
+
addHotkeyHandler(@simple_text_view.textbox)
|
|
156
|
+
addHotkeyHandler(@filter_text)
|
|
157
|
+
|
|
158
|
+
@filter_dt.connect(SEL_COMMAND) { applyFilter() }
|
|
159
|
+
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
def applyFilter
|
|
163
|
+
pattern = @filter_text.text
|
|
164
|
+
@match_pos_label.text = "0/0"
|
|
165
|
+
@simple_text_view.resetMatches()
|
|
166
|
+
@simple_text_view.setText(@text)
|
|
167
|
+
@match_pos_label.textColor = 'grey'
|
|
168
|
+
return true if pattern == ''
|
|
169
|
+
case @filter_mode
|
|
170
|
+
when SEL_TYPE_GREP
|
|
171
|
+
grepPattern(pattern)
|
|
172
|
+
when SEL_TYPE_HIGHLIGHT
|
|
173
|
+
highlightPattern(pattern)
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
private
|
|
178
|
+
|
|
179
|
+
def add_handler
|
|
180
|
+
|
|
181
|
+
handler_filename = FXFileDialog.getOpenFilename(self, "Select handler file", @handler_path, "*.rb\n*")
|
|
182
|
+
if handler_filename != "" then
|
|
183
|
+
if File.exists?(handler_filename) then
|
|
184
|
+
@handler_file = handler_filename
|
|
185
|
+
@handler_path = File.dirname(handler_filename) + "/"
|
|
186
|
+
load_handler(handler_filename)
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
def load_handler(file)
|
|
193
|
+
@handler = nil
|
|
194
|
+
return false if file.nil?
|
|
195
|
+
return false unless File.exist? file
|
|
196
|
+
begin
|
|
197
|
+
source = File.read(file)
|
|
198
|
+
#puts source
|
|
199
|
+
result = eval(source)
|
|
200
|
+
if result.respond_to? :call
|
|
201
|
+
@handler = result
|
|
202
|
+
@handler_status_lbl.text = "Handler ready!"
|
|
203
|
+
@handler_status_lbl.textColor = "green"
|
|
204
|
+
end
|
|
205
|
+
return true
|
|
206
|
+
|
|
207
|
+
rescue SyntaxError, LocalJumpError, NameError => e
|
|
208
|
+
out = e.to_s
|
|
209
|
+
out << e.backtrace.join("\n")
|
|
210
|
+
rescue => bang
|
|
211
|
+
out = bang
|
|
212
|
+
out << bang.backtrace.join("\n")
|
|
213
|
+
end
|
|
214
|
+
puts out
|
|
215
|
+
return false
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
def call_handler(object)
|
|
219
|
+
begin
|
|
220
|
+
result = @handler.call(object)
|
|
221
|
+
return result
|
|
222
|
+
rescue => bang
|
|
223
|
+
result = bang.to_s
|
|
224
|
+
result << bang.backtrace.join("\n")
|
|
225
|
+
return result
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
def gotoNextMatch()
|
|
231
|
+
@cur_match_pos += 1 if @cur_match_pos < @simple_text_view.numMatches-1
|
|
232
|
+
@simple_text_view.makeMatchVisible(@cur_match_pos)
|
|
233
|
+
@match_pos_label.text = "#{@cur_match_pos+1}/#{@simple_text_view.numMatches}" if @simple_text_view.numMatches > 0
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
def gotoPrevMatch()
|
|
237
|
+
@cur_match_pos -= 1 if @cur_match_pos > 0
|
|
238
|
+
@simple_text_view.makeMatchVisible(@cur_match_pos)
|
|
239
|
+
@match_pos_label.text = "#{@cur_match_pos+1}/#{@simple_text_view.numMatches}" if @simple_text_view.numMatches > 0
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
def addFilterHistory()
|
|
243
|
+
text = @filter_text.text
|
|
244
|
+
return true if text == ''
|
|
245
|
+
has_item = false
|
|
246
|
+
@filter_text.each do |item, data|
|
|
247
|
+
has_item = true if data == text
|
|
248
|
+
end
|
|
249
|
+
@filter_text.appendItem(text, text) unless has_item == true
|
|
250
|
+
@filter_text.numVisible = @filter_text.numItems
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
def resetFilter
|
|
254
|
+
@simple_text_view.max_len = 0
|
|
255
|
+
@simple_text_view.setText(@text)
|
|
256
|
+
@match_pos_label.text = "0/0"
|
|
257
|
+
@match_pos_label.textColor = 'grey'
|
|
258
|
+
@filter_text.text = ''
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
def highlightPattern(pattern)
|
|
262
|
+
@cur_match_pos = 0
|
|
263
|
+
@simple_text_view.max_len = 0
|
|
264
|
+
|
|
265
|
+
@match_pos_label.textColor = 'black'
|
|
266
|
+
|
|
267
|
+
@simple_text_view.setText(@text)
|
|
268
|
+
@simple_text_view.highlight(pattern)
|
|
269
|
+
@match_pos_label.text = "0/#{@simple_text_view.numMatches()}"
|
|
270
|
+
|
|
271
|
+
@simple_text_view.makeMatchVisible(0)
|
|
272
|
+
|
|
273
|
+
@match_pos_label.text = "1/#{@simple_text_view.numMatches()}" if @simple_text_view.numMatches() > 0
|
|
274
|
+
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
def grepPattern(pattern)
|
|
278
|
+
@cur_match_pos = 0
|
|
279
|
+
@simple_text_view.max_len = 0
|
|
280
|
+
|
|
281
|
+
@match_pos_label.textColor = 'black'
|
|
282
|
+
|
|
283
|
+
@simple_text_view.setText(@text)
|
|
284
|
+
|
|
285
|
+
@simple_text_view.filter(pattern)
|
|
286
|
+
@match_pos_label.text = "0/#{@simple_text_view.numMatches()}"
|
|
287
|
+
@simple_text_view.highlight(pattern)
|
|
288
|
+
@simple_text_view.makeMatchVisible(0)
|
|
289
|
+
|
|
290
|
+
@filter_mode = SEL_TYPE_GREP
|
|
291
|
+
|
|
292
|
+
@match_pos_label.text = "1/#{@simple_text_view.numMatches()}" if @simple_text_view.numMatches() > 0
|
|
293
|
+
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
def addHotkeyHandler(widget)
|
|
297
|
+
@ctrl_pressed = false
|
|
298
|
+
|
|
299
|
+
widget.connect(SEL_KEYPRESS) { |sender, sel, event|
|
|
300
|
+
# puts event.code
|
|
301
|
+
@ctrl_pressed = true if event.code == KEY_Control_L or event.code == KEY_Control_R
|
|
302
|
+
# @shift_pressed = true if @ctrl_pressed and ( event.code == KEY_Shift_L or event.code == KEY_Shift_R )
|
|
303
|
+
if event.code == KEY_Return
|
|
304
|
+
highlight(@filter_text.text)
|
|
305
|
+
true # special handling of KEY_Return, because we don't want a linebreak in textbox.
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
if event.code == KEY_F1
|
|
309
|
+
|
|
310
|
+
unless event.moved?
|
|
311
|
+
FXMenuPane.new(self) do |menu_pane|
|
|
312
|
+
FXMenuCaption.new(menu_pane, "Hotkeys:")
|
|
313
|
+
FXMenuSeparator.new(menu_pane)
|
|
314
|
+
[ "<ctrl-r> - Reset Filter",
|
|
315
|
+
"<ctrl-g> - Grep",
|
|
316
|
+
"<ctrl-h> - Highlight",
|
|
317
|
+
"<ctrl-n> - Goto Next",
|
|
318
|
+
"<ctrl-shift-n> - Goto Prev",
|
|
319
|
+
"<ctrl-w> - Switch Wordwrap"
|
|
320
|
+
].each do |hk|
|
|
321
|
+
FXMenuCaption.new(menu_pane, hk).backColor = 'yellow'
|
|
322
|
+
end
|
|
323
|
+
|
|
324
|
+
menu_pane.create
|
|
325
|
+
menu_pane.popup(nil, event.root_x, event.root_y)
|
|
326
|
+
app.runModalWhileShown(menu_pane)
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
end
|
|
330
|
+
end
|
|
331
|
+
|
|
332
|
+
if @ctrl_pressed
|
|
333
|
+
case event.code
|
|
334
|
+
when KEY_n
|
|
335
|
+
gotoNextMatch()
|
|
336
|
+
addFilterHistory()
|
|
337
|
+
when KEY_N
|
|
338
|
+
gotoPrevMatch()
|
|
339
|
+
addFilterHistory()
|
|
340
|
+
when KEY_w
|
|
341
|
+
@simple_text_view.textbox.textStyle ^= TEXT_WORDWRAP
|
|
342
|
+
when KEY_h
|
|
343
|
+
@mode_btn.text = "Highlight"
|
|
344
|
+
@filter_mode = SEL_TYPE_HIGHLIGHT
|
|
345
|
+
addFilterHistory()
|
|
346
|
+
applyFilter()
|
|
347
|
+
when KEY_g
|
|
348
|
+
@mode_btn.text = "Grep"
|
|
349
|
+
@filter_mode = SEL_TYPE_GREP
|
|
350
|
+
addFilterHistory()
|
|
351
|
+
applyFilter()
|
|
352
|
+
when KEY_r
|
|
353
|
+
resetFilter()
|
|
354
|
+
|
|
355
|
+
end
|
|
356
|
+
end
|
|
357
|
+
false
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
widget.connect(SEL_KEYRELEASE) { |sender, sel, event|
|
|
361
|
+
@ctrl_pressed = false if event.code == KEY_Control_L or event.code == KEY_Control_R
|
|
362
|
+
false
|
|
363
|
+
}
|
|
364
|
+
end
|
|
365
|
+
|
|
366
|
+
end
|
|
367
|
+
end
|
|
368
|
+
end
|