watobo 0.9.16 → 0.9.17
Sign up to get free protection for your applications and to get access to all the features.
- 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
|