Compare commits

...

7 Commits

Author SHA1 Message Date
gh0stkey
1c63841140 Version: 2.5.10 Update 2023-12-12 14:19:50 +08:00
gh0stkey
105c506039 Version: 2.5.10 Update 2023-12-12 14:19:28 +08:00
gh0stkey
f1941bccd7 Version: 2.5.9 Update 2023-11-28 15:26:25 +08:00
gh0stkey
d38e70523a Version: 2.5.9 Update 2023-11-28 09:11:56 +08:00
gh0stkey
1f7651c114 Version: 2.5.9 Update 2023-11-27 15:09:31 +08:00
gh0stkey
fc9a253d2b Version: 2.5.9 Update 2023-11-27 14:55:28 +08:00
ᴋᴇʏ
4cbcc1bcc4 Update issue templates 2023-11-27 09:11:52 +08:00
16 changed files with 219 additions and 318 deletions

View File

@@ -11,6 +11,7 @@ assignees: ''
``` ```
HaE版本 HaE版本
是否有自定义的HaE规则
BurpSuite版本 BurpSuite版本
JDK版本 JDK版本
操作系统版本: 操作系统版本:

View File

@@ -33,7 +33,7 @@ HaE目前的规则一共有6个字段分别是规则名称、规则正则、
|-----------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| |-----------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Name | 规则名称,主要用于简短概括当前规则的作用。 | | Name | 规则名称,主要用于简短概括当前规则的作用。 |
| Regex | 规则正则主要用于填写正则表达式。在HaE中所需提取匹配的内容需要用`(``)`将正则表达式进行包裹。 | | Regex | 规则正则主要用于填写正则表达式。在HaE中所需提取匹配的内容需要用`(``)`将正则表达式进行包裹。 |
| Scope | 规则作用域,主要用于表示当前规则作用HTTP报文的哪个部分。 | | Scope | 规则作用域,主要用于表示当前规则作用HTTP报文的哪个部分。 |
| Engine | 正则引擎,主要用于表示当前规则的正则表达式所使用的引擎。**DFA引擎**:对于文本串里的每一个字符只需扫描一次,速度快、特性少;**NFA引擎**:要翻来覆去标注字符、取消标注字符,速度慢,但是特性(如:分组、替换、分割)丰富。 | | Engine | 正则引擎,主要用于表示当前规则的正则表达式所使用的引擎。**DFA引擎**:对于文本串里的每一个字符只需扫描一次,速度快、特性少;**NFA引擎**:要翻来覆去标注字符、取消标注字符,速度慢,但是特性(如:分组、替换、分割)丰富。 |
| Color | 规则匹配颜色主要用于表示当前规则匹配到对应HTTP报文时所需标记的高亮颜色。 | | Color | 规则匹配颜色主要用于表示当前规则匹配到对应HTTP报文时所需标记的高亮颜色。 |
| Sensitive | 规则敏感性,主要用于表示当前规则对于大小写字母是否敏感,敏感(`True`)则严格按照大小写要求匹配,不敏感(`False`)则反之。 | | Sensitive | 规则敏感性,主要用于表示当前规则对于大小写字母是否敏感,敏感(`True`)则严格按照大小写要求匹配,不敏感(`False`)则反之。 |
@@ -56,16 +56,6 @@ HaE目前的规则一共有6个字段分别是规则名称、规则正则、
| Config配置信息管理 | <img src="images/config.png" style="width: 80%" /> | | Config配置信息管理 | <img src="images/config.png" style="width: 80%" /> |
| Databoard数据集合面板 | <img src="images/databoard.png" style="width: 80%" /> | | Databoard数据集合面板 | <img src="images/databoard.png" style="width: 80%" /> |
## 实际使用
使用 RGPerson 生成测试数据,放入网站根目录文件中:
![-w467](images/rgperson.jpg)
访问该地址,在`Proxy - HTTP History`中可以看见高亮请求,响应标签页中含有`MarkInfo`标签,其中将匹配到的信息提取了出来。
![-w1047](images/markinfo.png)
## 文末随笔 ## 文末随笔
正义感是一个不可丢失的东西。 正义感是一个不可丢失的东西。

Binary file not shown.

Before

Width:  |  Height:  |  Size: 65 KiB

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 242 KiB

After

Width:  |  Height:  |  Size: 666 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 140 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 117 KiB

After

Width:  |  Height:  |  Size: 100 KiB

View File

@@ -3,12 +3,10 @@ package burp;
import burp.config.ConfigLoader; import burp.config.ConfigLoader;
import burp.core.processor.ColorProcessor; import burp.core.processor.ColorProcessor;
import burp.core.processor.MessageProcessor; import burp.core.processor.MessageProcessor;
import burp.core.utils.StringHelper;
import burp.ui.MainUI; import burp.ui.MainUI;
import burp.ui.board.DatatablePanel; import burp.ui.board.DatatablePanel;
import burp.ui.board.MessagePanel; import burp.ui.board.MessagePanel;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.net.URL;
import java.util.*; import java.util.*;
import javax.swing.*; import javax.swing.*;
import java.awt.*; import java.awt.*;
@@ -38,7 +36,7 @@ public class BurpExtender implements IBurpExtender, IHttpListener, IMessageEdito
new ConfigLoader(); new ConfigLoader();
String version = "2.5.8"; String version = "2.5.10";
callbacks.setExtensionName(String.format("HaE (%s) - Highlighter and Extractor", version)); callbacks.setExtensionName(String.format("HaE (%s) - Highlighter and Extractor", version));
// 定义输出 // 定义输出
@@ -74,43 +72,7 @@ public class BurpExtender implements IBurpExtender, IHttpListener, IMessageEdito
@Override @Override
public Component getUiComponent() { public Component getUiComponent() {
JTabbedPane HaETabbedPane = new JTabbedPane(); return main;
HaETabbedPane.addTab("", getImageIcon(false), main);
HaETabbedPane.addTab(" Highlighter and Extractor - Empower ethical hacker for efficient operations ", null);
HaETabbedPane.setEnabledAt(1, false);
HaETabbedPane.addPropertyChangeListener("background", new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent e) {
boolean isDarkBg = isDarkBg();
HaETabbedPane.setIconAt(0, getImageIcon(isDarkBg));
}
private boolean isDarkBg() {
Color bg = HaETabbedPane.getBackground();
int r = bg.getRed();
int g = bg.getGreen();
int b = bg.getBlue();
int avg = (r + g + b) / 3;
return avg < 128;
}
});
return HaETabbedPane;
}
private ImageIcon getImageIcon(boolean isDark) {
ClassLoader classLoader = getClass().getClassLoader();
URL imageURL;
if (isDark) {
imageURL = classLoader.getResource("logo.png");
} else {
imageURL = classLoader.getResource("logo_black.png");
}
ImageIcon originalIcon = new ImageIcon(imageURL);
Image originalImage = originalIcon.getImage();
Image scaledImage = originalImage.getScaledInstance(30, 20, Image.SCALE_FAST);
ImageIcon scaledIcon = new ImageIcon(scaledImage);
return scaledIcon;
} }
/** /**
@@ -145,7 +107,7 @@ public class BurpExtender implements IBurpExtender, IHttpListener, IMessageEdito
String addComment = String.join(", ", result.get(1).get("comment")); String addComment = String.join(", ", result.get(1).get("comment"));
String allComment = !Objects.equals(originalComment, "") ? String.format("%s, %s", originalComment, addComment) : addComment; String allComment = !Objects.equals(originalComment, "") ? String.format("%s, %s", originalComment, addComment) : addComment;
String resComment = mergeComment(allComment); String resComment = StringHelper.mergeComment(allComment);
messageInfo.setComment(resComment); messageInfo.setComment(resComment);
messagePanel.add(messageInfo, resComment, resColor); messagePanel.add(messageInfo, resComment, resColor);
@@ -158,39 +120,6 @@ public class BurpExtender implements IBurpExtender, IHttpListener, IMessageEdito
} }
} }
private String mergeComment(String comment) {
if (!comment.contains(",")) {
return comment;
}
Map<String, Integer> itemCounts = new HashMap<>();
String[] items = comment.split(", ");
for (String item : items) {
if (item.contains("(") && item.contains(")")) {
int openParenIndex = item.lastIndexOf("(");
int closeParenIndex = item.lastIndexOf(")");
String itemName = item.substring(0, openParenIndex).trim();
int count = Integer.parseInt(item.substring(openParenIndex + 1, closeParenIndex).trim());
itemCounts.put(itemName, itemCounts.getOrDefault(itemName, 0) + count);
} else {
itemCounts.put(item, 0);
}
}
StringBuilder mergedItems = new StringBuilder();
for (Map.Entry<String, Integer> entry : itemCounts.entrySet()) {
String itemName = entry.getKey();
int count = entry.getValue();
if (count != 0) {
mergedItems.append(itemName).append(" (").append(count).append("), ");
}
}
return mergedItems.substring(0, mergedItems.length() - 2);
}
class MarkInfoTab implements IMessageEditorTab { class MarkInfoTab implements IMessageEditorTab {
private final JTabbedPane jTabbedPane = new JTabbedPane(); private final JTabbedPane jTabbedPane = new JTabbedPane();
private DatatablePanel dataPanel; private DatatablePanel dataPanel;
@@ -223,27 +152,27 @@ public class BurpExtender implements IBurpExtender, IHttpListener, IMessageEdito
@Override @Override
public boolean isEnabled(byte[] content, boolean isRequest) { public boolean isEnabled(byte[] content, boolean isRequest) {
List<Map<String, String>> result = null; List<Map<String, String>> result = null;
if (content.length != 0 && !helpers.bytesToString(content).equals("Loading...")) {
try { try {
if (isRequest) { if (isRequest) {
result = messageProcessor.processRequestMessage(helpers, content, "", false); result = messageProcessor.processRequestMessage(helpers, content, "", false);
} else { } else {
result = messageProcessor.processResponseMessage(helpers, content, "", false); result = messageProcessor.processResponseMessage(helpers, content, "", false);
}
} catch (Exception e) {
e.printStackTrace();
} }
} catch (Exception e) {
e.printStackTrace();
}
if (result != null && !result.isEmpty()) { if (result != null && !result.isEmpty()) {
Map<String, String> dataMap = result.get(0); Map<String, String> dataMap = result.get(0);
if (isRequest) { if (isRequest) {
extractRequestMap = dataMap; extractRequestMap = dataMap;
} else { } else {
extractResponseMap = dataMap; extractResponseMap = dataMap;
}
return true;
} }
return true;
} }
return false; return false;
} }

View File

@@ -3,6 +3,7 @@ package burp.config;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class ConfigEntry { public class ConfigEntry {
public static String excludeSuffix = "3g2|3gp|7z|aac|abw|aif|aifc|aiff|apk|arc|au|avi|azw|bat|bin|bmp|bz|bz2|cmd|cmx|cod|com|csh|css|csv|dll|doc|docx|ear|eot|epub|exe|flac|flv|gif|gz|ico|ics|ief|jar|jfif|jpe|jpeg|jpg|less|m3u|mid|midi|mjs|mkv|mov|mp2|mp3|mp4|mpa|mpe|mpeg|mpg|mpkg|mpp|mpv2|odp|ods|odt|oga|ogg|ogv|ogx|otf|pbm|pdf|pgm|png|pnm|ppm|ppt|pptx|ra|ram|rar|ras|rgb|rmi|rtf|scss|sh|snd|svg|swf|tar|tif|tiff|ttf|vsd|war|wav|weba|webm|webp|wmv|woff|woff2|xbm|xls|xlsx|xpm|xul|xwd|zip"; public static String excludeSuffix = "3g2|3gp|7z|aac|abw|aif|aifc|aiff|apk|arc|au|avi|azw|bat|bin|bmp|bz|bz2|cmd|cmx|cod|com|csh|css|csv|dll|doc|docx|ear|eot|epub|exe|flac|flv|gif|gz|ico|ics|ief|jar|jfif|jpe|jpeg|jpg|less|m3u|mid|midi|mjs|mkv|mov|mp2|mp3|mp4|mpa|mpe|mpeg|mpg|mpkg|mpp|mpv2|odp|ods|odt|oga|ogg|ogv|ogx|otf|pbm|pdf|pgm|png|pnm|ppm|ppt|pptx|ra|ram|rar|ras|rgb|rmi|rtf|scss|sh|snd|svg|swf|tar|tif|tiff|ttf|vsd|war|wav|weba|webm|webp|wmv|woff|woff2|xbm|xls|xlsx|xpm|xul|xwd|zip";
@@ -38,5 +39,5 @@ public class ConfigEntry {
public static Map<String,Object[][]> globalRules = null; public static Map<String,Object[][]> globalRules = null;
public static Map<String, Map<String, List<String>>> globalDataMap = new HashMap<>(); public static ConcurrentHashMap<String, Map<String, List<String>>> globalDataMap = new ConcurrentHashMap<>();
} }

View File

@@ -143,7 +143,7 @@ public class ConfigLoader {
Map<String,Object> r = new LinkedHashMap<>(); Map<String,Object> r = new LinkedHashMap<>();
r.put("excludeSuffix", excludeSuffix); r.put("excludeSuffix", excludeSuffix);
try{ try{
Writer ws = new OutputStreamWriter(Files.newOutputStream(Paths.get(RulesFilePath)), StandardCharsets.UTF_8); Writer ws = new OutputStreamWriter(Files.newOutputStream(Paths.get(ConfigFilePath)), StandardCharsets.UTF_8);
yaml.dump(r, ws); yaml.dump(r, ws);
ws.close(); ws.close();
}catch (Exception ex){ }catch (Exception ex){

View File

@@ -1,5 +1,6 @@
package burp.core.processor; package burp.core.processor;
import burp.BurpExtender;
import burp.core.GlobalCachePool; import burp.core.GlobalCachePool;
import burp.core.utils.HashCalculator; import burp.core.utils.HashCalculator;
import burp.core.utils.MatchTool; import burp.core.utils.MatchTool;
@@ -13,6 +14,7 @@ import java.nio.charset.StandardCharsets;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.util.*; import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import jregex.Matcher; import jregex.Matcher;
import jregex.Pattern; import jregex.Pattern;
@@ -93,31 +95,37 @@ public class DataProcessingUnit {
break; break;
} }
if ("nfa".equals(engine)) { try {
Pattern pattern; if ("nfa".equals(engine)) {
// 判断规则是否大小写敏感 Pattern pattern;
if (sensitive) { // 判断规则是否大小写敏感
pattern = new Pattern(regex); if (sensitive) {
} else { pattern = new Pattern(regex);
pattern = new Pattern(regex, Pattern.IGNORE_CASE); } else {
} pattern = new Pattern(regex, Pattern.IGNORE_CASE);
}
Matcher matcher = pattern.matcher(matchContent); Matcher matcher = pattern.matcher(matchContent);
while (matcher.find()) { while (matcher.find()) {
// 添加匹配数据至list // 添加匹配数据至list
// 强制用户使用()包裹正则 // 强制用户使用()包裹正则
result.add(matcher.group(1)); result.add(matcher.group(1));
} }
} else { } else {
RegExp regexp = new RegExp(regex); RegExp regexp = new RegExp(regex);
Automaton auto = regexp.toAutomaton(); Automaton auto = regexp.toAutomaton();
RunAutomaton runAuto = new RunAutomaton(auto, true); RunAutomaton runAuto = new RunAutomaton(auto, true);
AutomatonMatcher autoMatcher = runAuto.newMatcher(matchContent); AutomatonMatcher autoMatcher = runAuto.newMatcher(matchContent);
while (autoMatcher.find()) { while (autoMatcher.find()) {
// 添加匹配数据至list // 添加匹配数据至list
// 强制用户使用()包裹正则 // 强制用户使用()包裹正则
result.add(autoMatcher.group()); result.add(autoMatcher.group());
}
} }
} catch (Exception e) {
BurpExtender.stdout.println(String.format("[x] Error Info:\nName: %s\nRegex: %s", name, regex));
e.printStackTrace();
continue;
} }
// 去除重复内容 // 去除重复内容
@@ -135,7 +143,7 @@ public class DataProcessingUnit {
if (!Objects.equals(host, "") && host != null) { if (!Objects.equals(host, "") && host != null) {
List<String> dataList = Arrays.asList(dataStr.split("\n")); List<String> dataList = Arrays.asList(dataStr.split("\n"));
if (ConfigEntry.globalDataMap.containsKey(host)) { if (ConfigEntry.globalDataMap.containsKey(host)) {
Map<String, List<String>> gRuleMap = new HashMap<>(ConfigEntry.globalDataMap.get(host)); ConcurrentHashMap<String, List<String>> gRuleMap = new ConcurrentHashMap<>(ConfigEntry.globalDataMap.get(host));
if (gRuleMap.containsKey(name)) { if (gRuleMap.containsKey(name)) {
// gDataList为不可变列表因此需要重新创建一个列表以便于使用addAll方法 // gDataList为不可变列表因此需要重新创建一个列表以便于使用addAll方法
List<String> gDataList = gRuleMap.get(name); List<String> gDataList = gRuleMap.get(name);

View File

@@ -23,9 +23,29 @@ public class MessageProcessor {
List<Map<String, String>> reqObj = processRequestMessage(helpers, requestByte, host, actionFlag); List<Map<String, String>> reqObj = processRequestMessage(helpers, requestByte, host, actionFlag);
List<Map<String, String>> resObj = processResponseMessage(helpers, responseByte, host, actionFlag); List<Map<String, String>> resObj = processResponseMessage(helpers, responseByte, host, actionFlag);
List<Map<String, String>> mergedList = new ArrayList<>();
List<Map<String, String>> mergedList = new ArrayList<>(reqObj); if (reqObj != null && !reqObj.isEmpty()) {
mergedList.addAll(resObj); if (resObj != null && !resObj.isEmpty()) {
List<String> colorList = new ArrayList<>();
colorList.add(reqObj.get(0).get("color"));
colorList.add(resObj.get(0).get("color"));
Map<String, String> colorMap = new HashMap<>();
colorMap.put("color", colorProcessor.retrieveFinalColor(colorProcessor.retrieveColorIndices(colorList)));
Map<String, String> commentMap = new HashMap<>();
String commentList = String.format("%s, %s", reqObj.get(1).get("comment"), resObj.get(1).get("comment"));
commentMap.put("comment", commentList);
mergedList.add(0, colorMap);
mergedList.add(1, commentMap);
} else {
mergedList = new ArrayList<>(reqObj);
}
} else if (resObj != null && !resObj.isEmpty()){
mergedList = new ArrayList<>(resObj);
}
return mergedList; return mergedList;
} }

View File

@@ -1,5 +1,8 @@
package burp.core.utils; package burp.core.utils;
import java.util.HashMap;
import java.util.Map;
public class StringHelper { public class StringHelper {
public static String replaceFirstOccurrence(String original, String find, String replace) { public static String replaceFirstOccurrence(String original, String find, String replace) {
int index = original.indexOf(find); int index = original.indexOf(find);
@@ -27,4 +30,37 @@ public class StringHelper {
// 如果patternIndex为-1表示pattern字符串已经完全匹配 // 如果patternIndex为-1表示pattern字符串已经完全匹配
return patternIndex == -1; return patternIndex == -1;
} }
public static String mergeComment(String comment) {
if (!comment.contains(",")) {
return comment;
}
Map<String, Integer> itemCounts = new HashMap<>();
String[] items = comment.split(", ");
for (String item : items) {
if (item.contains("(") && item.contains(")")) {
int openParenIndex = item.lastIndexOf("(");
int closeParenIndex = item.lastIndexOf(")");
String itemName = item.substring(0, openParenIndex).trim();
int count = Integer.parseInt(item.substring(openParenIndex + 1, closeParenIndex).trim());
itemCounts.put(itemName, itemCounts.getOrDefault(itemName, 0) + count);
} else {
itemCounts.put(item, 0);
}
}
StringBuilder mergedItems = new StringBuilder();
for (Map.Entry<String, Integer> entry : itemCounts.entrySet()) {
String itemName = entry.getKey();
int count = entry.getValue();
if (count != 0) {
mergedItems.append(itemName).append(" (").append(count).append("), ");
}
}
return mergedItems.substring(0, mergedItems.length() - 2);
}
} }

View File

@@ -6,6 +6,9 @@ import burp.rule.RuleProcessor;
import burp.ui.board.Databoard; import burp.ui.board.Databoard;
import burp.ui.board.MessagePanel; import burp.ui.board.MessagePanel;
import burp.ui.rule.RulePane; import burp.ui.rule.RulePane;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.net.URL;
import javax.swing.*; import javax.swing.*;
import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener; import javax.swing.event.ChangeListener;
@@ -134,7 +137,31 @@ public class MainUI extends JPanel {
mainTabbedPane.addTab("Config", rulePanel); mainTabbedPane.addTab("Config", rulePanel);
mainTabbedPane.addTab("Databoard", this.databoardPanel); mainTabbedPane.addTab("Databoard", this.databoardPanel);
} }
add(mainTabbedPane, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0,
// 新增Logo
JTabbedPane HaETabbedPane = new JTabbedPane();
HaETabbedPane.addTab("", getImageIcon(false), mainTabbedPane);
HaETabbedPane.addTab(" Highlighter and Extractor - Empower ethical hacker for efficient operations ", null);
HaETabbedPane.setEnabledAt(1, false);
HaETabbedPane.addPropertyChangeListener("background", new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent e) {
boolean isDarkBg = isDarkBg();
HaETabbedPane.setIconAt(0, getImageIcon(isDarkBg));
}
private boolean isDarkBg() {
Color bg = HaETabbedPane.getBackground();
int r = bg.getRed();
int g = bg.getGreen();
int b = bg.getBlue();
int avg = (r + g + b) / 3;
return avg < 128;
}
});
add(HaETabbedPane, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0,
GridBagConstraints.CENTER, GridBagConstraints.BOTH, GridBagConstraints.CENTER, GridBagConstraints.BOTH,
new Insets(0, 0, 0, 0), 0, 0)); new Insets(0, 0, 0, 0), 0, 0));
@@ -153,6 +180,21 @@ public class MainUI extends JPanel {
tabMenu.add(deleteMenuItem); tabMenu.add(deleteMenuItem);
} }
private ImageIcon getImageIcon(boolean isDark) {
ClassLoader classLoader = getClass().getClassLoader();
URL imageURL;
if (isDark) {
imageURL = classLoader.getResource("logo.png");
} else {
imageURL = classLoader.getResource("logo_black.png");
}
ImageIcon originalIcon = new ImageIcon(imageURL);
Image originalImage = originalIcon.getImage();
Image scaledImage = originalImage.getScaledInstance(30, 20, Image.SCALE_FAST);
ImageIcon scaledIcon = new ImageIcon(scaledImage);
return scaledIcon;
}
private JTabbedPane ruleTabbedPane; private JTabbedPane ruleTabbedPane;
private JTextField rulesPathTextField; private JTextField rulesPathTextField;
private JTextField excludeSuffixTextField; private JTextField excludeSuffixTextField;

View File

@@ -5,8 +5,8 @@ import burp.core.utils.StringHelper;
import burp.ui.board.MessagePanel.Table; import burp.ui.board.MessagePanel.Table;
import java.util.*; import java.util.*;
import javax.swing.event.ChangeEvent; import java.util.concurrent.ConcurrentHashMap;
import javax.swing.event.ChangeListener; import javax.swing.event.*;
import javax.swing.table.TableColumn; import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel; import javax.swing.table.TableColumnModel;
import javax.swing.table.TableModel; import javax.swing.table.TableModel;
@@ -15,8 +15,6 @@ import java.awt.*;
import java.awt.event.*; import java.awt.event.*;
import java.util.List; import java.util.List;
import javax.swing.*; import javax.swing.*;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
/** /**
* @author LinChen && EvilChen * @author LinChen && EvilChen
@@ -48,7 +46,6 @@ public class Databoard extends JPanel {
} }
}; };
public Databoard(MessagePanel messagePanel) { public Databoard(MessagePanel messagePanel) {
this.messagePanel = messagePanel; this.messagePanel = messagePanel;
initComponents(); initComponents();
@@ -94,42 +91,25 @@ public class Databoard extends JPanel {
//---- hostLabel ---- //---- hostLabel ----
hostLabel.setText("Host:"); hostLabel.setText("Host:");
add(hostLabel, new GridBagConstraints(1, 0, 1, 1, 0.0, 0.0, add(hostLabel, new GridBagConstraints(1, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH,
GridBagConstraints.CENTER, GridBagConstraints.BOTH,
new Insets(8, 0, 5, 5), 0, 0)); new Insets(8, 0, 5, 5), 0, 0));
add(hostTextField, new GridBagConstraints(2, 0, 1, 1, 0.0, 0.0, add(hostTextField, new GridBagConstraints(2, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH,
GridBagConstraints.CENTER, GridBagConstraints.BOTH,
new Insets(8, 0, 5, 5), 0, 0)); new Insets(8, 0, 5, 5), 0, 0));
clearButton.setText("Clear"); clearButton.setText("Clear");
clearButton.addActionListener(this::clearActionPerformed); clearButton.addActionListener(this::clearActionPerformed);
add(clearButton, new GridBagConstraints(3, 0, 1, 1, 0.0, 0.0, add(clearButton, new GridBagConstraints(3, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH,
GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(8, 0, 5, 5), 0, 0));
hostComboBox.setMaximumRowCount(5);
add(hostComboBox, new GridBagConstraints(2, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH,
new Insets(8, 0, 5, 5), 0, 0)); new Insets(8, 0, 5, 5), 0, 0));
splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT); splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
splitPane.setVisible(false); splitPane.setVisible(false);
add(splitPane, new GridBagConstraints(1, 1, 3, 3, 0.0, 0.0,
add(splitPane, new GridBagConstraints(1, 1, 3, 2, 0.0, 0.0,
GridBagConstraints.CENTER, GridBagConstraints.BOTH, GridBagConstraints.CENTER, GridBagConstraints.BOTH,
new Insets(8, 0, 5, 5), 0, 0)); new Insets(8, 0, 5, 5), 0, 0));
hostTextField.setLayout(new BorderLayout());
hostTextField.add(hostComboBox, BorderLayout.SOUTH);
hostComboBox.setMaximumRowCount(5);
hostComboBox.setPreferredSize(new Dimension(super.getPreferredSize().width, 0));
// 由于主题切换造成的UI组件重绘而自定义组件没有正确地与之同步因此需要事件监听来进行同步
UIManager.addPropertyChangeListener(evt -> {
if ("lookAndFeel".equals(evt.getPropertyName())) {
SwingUtilities.invokeLater(() -> {
hostTextField.remove(hostComboBox);
hostTextField.add(hostComboBox, BorderLayout.SOUTH);
hostTextField.revalidate();
hostTextField.repaint();
});
}
});
setAutoMatch(); setAutoMatch();
} }
@@ -156,22 +136,19 @@ public class Databoard extends JPanel {
hostTextField.getDocument().addDocumentListener(new DocumentListener() { hostTextField.getDocument().addDocumentListener(new DocumentListener() {
@Override @Override
public void insertUpdate(DocumentEvent e) { public void insertUpdate(DocumentEvent e) {
update(e); filterComboBoxList();
} }
@Override @Override
public void removeUpdate(DocumentEvent e) { public void removeUpdate(DocumentEvent e) {
update(e); filterComboBoxList();
} }
@Override @Override
public void changedUpdate(DocumentEvent e) { public void changedUpdate(DocumentEvent e) {
update(e);
}
public void update(DocumentEvent e) {
filterComboBoxList(); filterComboBoxList();
} }
}); });
} }
@@ -188,7 +165,6 @@ public class Databoard extends JPanel {
populateTabbedPaneByHost(selectedHost); populateTabbedPaneByHost(selectedHost);
} }
} }
private void handleKeyEvents(KeyEvent e) { private void handleKeyEvents(KeyEvent e) {
isMatchHost = true; isMatchHost = true;
int keyCode = e.getKeyCode(); int keyCode = e.getKeyCode();
@@ -197,14 +173,14 @@ public class Databoard extends JPanel {
e.setKeyCode(KeyEvent.VK_ENTER); e.setKeyCode(KeyEvent.VK_ENTER);
} }
if (Arrays.asList(KeyEvent.VK_ENTER, KeyEvent.VK_UP, KeyEvent.VK_DOWN).contains(keyCode)) { if (Arrays.asList(KeyEvent.VK_DOWN, KeyEvent.VK_UP).contains(keyCode)) {
e.setSource(hostComboBox);
hostComboBox.dispatchEvent(e); hostComboBox.dispatchEvent(e);
if (keyCode == KeyEvent.VK_ENTER) { }
updateTextFieldFromComboBox();
hostComboBox.setPopupVisible(false); if (keyCode == KeyEvent.VK_ENTER) {
e.consume(); isMatchHost = false;
} handleComboBoxAction(null);
hostComboBox.setPopupVisible(false);
} }
if (keyCode == KeyEvent.VK_ESCAPE) { if (keyCode == KeyEvent.VK_ESCAPE) {
@@ -214,15 +190,6 @@ public class Databoard extends JPanel {
isMatchHost = false; isMatchHost = false;
} }
private void updateTextFieldFromComboBox() {
Object selectedItem = hostComboBox.getSelectedItem();
if (selectedItem != null) {
String selectedHost = selectedItem.toString();
hostTextField.setText(selectedHost);
populateTabbedPaneByHost(selectedHost);
}
}
private void filterComboBoxList() { private void filterComboBoxList() {
isMatchHost = true; isMatchHost = true;
comboBoxModel.removeAllElements(); comboBoxModel.removeAllElements();
@@ -249,24 +216,28 @@ public class Databoard extends JPanel {
private void applyHostFilter(String filterText) { private void applyHostFilter(String filterText) {
TableRowSorter<TableModel> sorter = (TableRowSorter<TableModel>) table.getRowSorter(); TableRowSorter<TableModel> sorter = (TableRowSorter<TableModel>) table.getRowSorter();
if (filterText.contains("*.")) { String cleanedText = StringHelper.replaceFirstOccurrence(filterText, "*.", "");
filterText = StringHelper.replaceFirstOccurrence(filterText, "*.", "");
} else if (filterText.contains("*")) { if (cleanedText.contains("*")) {
filterText = ""; cleanedText = "";
} }
RowFilter<TableModel, Integer> filter = RowFilter.regexFilter(filterText, 1); RowFilter<TableModel, Integer> filter = RowFilter.regexFilter(cleanedText, 1);
sorter.setRowFilter(filter); sorter.setRowFilter(filter);
filterText = filterText.isEmpty() ? "*" : filterText;
messagePanel.applyHostFilter(filterText); messagePanel.applyHostFilter(filterText);
} }
private void populateTabbedPaneByHost(String selectedHost) { private void populateTabbedPaneByHost(String selectedHost) {
if (!Objects.equals(selectedHost, "")) { if (!Objects.equals(selectedHost, "")) {
Map<String, Map<String, List<String>>> dataMap = ConfigEntry.globalDataMap; ConcurrentHashMap<String, Map<String, List<String>>> dataMap = ConfigEntry.globalDataMap;
Map<String, List<String>> selectedDataMap; Map<String, List<String>> selectedDataMap;
dataTabbedPane.removeAll();
dataTabbedPane.setPreferredSize(new Dimension(500,0));
dataTabbedPane.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT);
splitPane.setLeftComponent(dataTabbedPane);
if (selectedHost.contains("*")) { if (selectedHost.contains("*")) {
// 通配符数据 // 通配符数据
selectedDataMap = new HashMap<>(); selectedDataMap = new HashMap<>();
@@ -291,14 +262,8 @@ public class Databoard extends JPanel {
selectedDataMap = dataMap.get(selectedHost); selectedDataMap = dataMap.get(selectedHost);
} }
dataTabbedPane.removeAll();
dataTabbedPane.setPreferredSize(new Dimension(500,0));
dataTabbedPane.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT);
splitPane.setLeftComponent(dataTabbedPane);
if (selectedHost.equals("**")) { if (selectedHost.equals("**")) {
for (Map.Entry<String, Map<String, List<String>>> entry : dataMap.entrySet()) { for (ConcurrentHashMap.Entry<String, Map<String, List<String>>> entry : dataMap.entrySet()) {
JTabbedPane newTabbedPane = new JTabbedPane(); JTabbedPane newTabbedPane = new JTabbedPane();
newTabbedPane.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT); newTabbedPane.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT);
@@ -368,20 +333,6 @@ public class Databoard extends JPanel {
hostTextField.setText(selectedHost); hostTextField.setText(selectedHost);
ChangeListener changeListener = new ChangeListener() {
public void stateChanged(ChangeEvent e) {
JTabbedPane tabSource = (JTabbedPane) e.getSource();
int index = tabSource.getSelectedIndex();
if (index != -1) {
Component selectedComponent = tabSource.getComponentAt(index);
if (selectedComponent instanceof DatatablePanel) {
((DatatablePanel) selectedComponent).updatePageSize();
}
}
}
};
dataTabbedPane.addChangeListener(changeListener);
} }
} }
} }

View File

@@ -7,19 +7,16 @@ import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.StringSelection; import java.awt.datatransfer.StringSelection;
import java.awt.event.ActionEvent; import java.awt.event.ActionEvent;
import java.awt.event.ActionListener; import java.awt.event.ActionListener;
import java.awt.event.AdjustmentEvent;
import java.awt.event.AdjustmentListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.FocusEvent; import java.awt.event.FocusEvent;
import java.awt.event.FocusListener; import java.awt.event.FocusListener;
import jregex.Pattern;
import jregex.REFlags;
import java.awt.event.ItemEvent; import java.awt.event.ItemEvent;
import java.awt.event.ItemListener; import java.awt.event.ItemListener;
import java.awt.event.MouseAdapter; import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent; import java.awt.event.MouseEvent;
import java.util.Comparator; import java.util.Comparator;
import java.util.List; import java.util.List;
import java.util.regex.Pattern;
import javax.swing.BorderFactory; import javax.swing.BorderFactory;
import javax.swing.Box; import javax.swing.Box;
import javax.swing.BoxLayout; import javax.swing.BoxLayout;
@@ -45,20 +42,11 @@ public class DatatablePanel extends JPanel {
private final DefaultTableModel model; private final DefaultTableModel model;
private final JTextField searchField; private final JTextField searchField;
private TableRowSorter<DefaultTableModel> sorter; private TableRowSorter<DefaultTableModel> sorter;
private int pageSize; // 动态计算的,每页显示多少条记录
private int currentPage; // 当前页码
private List<String> fullList; // 假设这是一个包含所有数据的列表
private JScrollPane scrollPane; private JScrollPane scrollPane;
private String tableName; private String tableName;
private final int SHOW_LENGTH = 5000;
private JCheckBox searchMode = new JCheckBox("Reverse search"); private JCheckBox searchMode = new JCheckBox("Reverse search");
private JCheckBox showMode = new JCheckBox("Show all data");
private boolean scrollFlag = true;
public DatatablePanel(String tableName, List<String> list) { public DatatablePanel(String tableName, List<String> list) {
fullList = list;
currentPage = 0;
pageSize = 10;
this.tableName = tableName; this.tableName = tableName;
String[] columnNames = {"#", "Information"}; String[] columnNames = {"#", "Information"};
@@ -77,6 +65,10 @@ public class DatatablePanel extends JPanel {
TableColumn idColumn = table.getColumnModel().getColumn(0); TableColumn idColumn = table.getColumnModel().getColumn(0);
idColumn.setMaxWidth(50); idColumn.setMaxWidth(50);
for (String item : list) {
addRowToTable(model, new Object[]{item});
}
String defaultText = "Search"; String defaultText = "Search";
searchField = new JTextField(defaultText); searchField = new JTextField(defaultText);
@@ -122,31 +114,6 @@ public class DatatablePanel extends JPanel {
// 设置布局 // 设置布局
scrollPane = new JScrollPane(table); scrollPane = new JScrollPane(table);
scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS); scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
scrollPane.addComponentListener(new ComponentAdapter() {
@Override
public void componentResized(ComponentEvent e) {
updatePageSize();
}
});
// 添加滚动监听器,以加载更多数据
AdjustmentListener scrollListener = new AdjustmentListener() {
@Override
public void adjustmentValueChanged(AdjustmentEvent e) {
if (fullList.size() > SHOW_LENGTH) {
if (!e.getValueIsAdjusting() && !scrollPane.getVerticalScrollBar().getValueIsAdjusting()) {
if (scrollPane.getVerticalScrollBar().getValue() == scrollPane.getVerticalScrollBar().getMaximum() - scrollPane.getVerticalScrollBar().getVisibleAmount()) {
if ((currentPage + 1) * pageSize < fullList.size()) {
currentPage++;
loadPageData();
}
}
}
}
}
};
scrollPane.getVerticalScrollBar().addAdjustmentListener(scrollListener);
searchMode.addItemListener(new ItemListener() { searchMode.addItemListener(new ItemListener() {
public void itemStateChanged(ItemEvent e) { public void itemStateChanged(ItemEvent e) {
@@ -154,18 +121,6 @@ public class DatatablePanel extends JPanel {
} }
}); });
showMode.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent e) {
model.setRowCount(0);
if (e.getStateChange() == ItemEvent.SELECTED) {
scrollFlag = false;
loadPageData();
showMode.setEnabled(false);
}
}
});
setLayout(new BorderLayout(0, 5)); setLayout(new BorderLayout(0, 5));
JPanel optionsPanel = new JPanel(); JPanel optionsPanel = new JPanel();
@@ -173,11 +128,10 @@ public class DatatablePanel extends JPanel {
optionsPanel.setLayout(new BoxLayout(optionsPanel, BoxLayout.X_AXIS)); optionsPanel.setLayout(new BoxLayout(optionsPanel, BoxLayout.X_AXIS));
// 新增复选框要在这修改rows // 新增复选框要在这修改rows
JPanel menuPanel = new JPanel(new GridLayout(2, 1)); JPanel menuPanel = new JPanel(new GridLayout(1, 1));
menuPanel.setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3)); menuPanel.setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3));
JPopupMenu menu = new JPopupMenu(); JPopupMenu menu = new JPopupMenu();
menuPanel.add(searchMode); menuPanel.add(searchMode);
menuPanel.add(showMode);
menu.add(menuPanel); menu.add(menuPanel);
JButton settingsButton = new JButton("Settings"); JButton settingsButton = new JButton("Settings");
@@ -196,8 +150,16 @@ public class DatatablePanel extends JPanel {
add(scrollPane, BorderLayout.CENTER); add(scrollPane, BorderLayout.CENTER);
add(optionsPanel, BorderLayout.SOUTH); add(optionsPanel, BorderLayout.SOUTH);
}
loadPageData(); private static void addRowToTable(DefaultTableModel model, Object[] data) {
// 获取当前ID
int rowCount = model.getRowCount();
int id = rowCount > 0 ? (Integer) model.getValueAt(rowCount - 1, 0) + 1 : 1;
Object[] rowData = new Object[data.length + 1];
rowData[0] = id; // 设置ID列的值
System.arraycopy(data, 0, rowData, 1, data.length); // 拷贝其余数据
model.addRow(rowData); // 添加行
} }
private void performSearch() { private void performSearch() {
@@ -215,14 +177,16 @@ public class DatatablePanel extends JPanel {
rowFilter = new RowFilter<DefaultTableModel, Object>() { rowFilter = new RowFilter<DefaultTableModel, Object>() {
public boolean include(Entry<? extends DefaultTableModel, ? extends Object> entry) { public boolean include(Entry<? extends DefaultTableModel, ? extends Object> entry) {
// 对每一行的第二列进行判断假设第二列的索引是1 // 对每一行的第二列进行判断假设第二列的索引是1
String value = (String) entry.getValue(1); String entryValue = (String) entry.getValue(1);
// 如果该列的值不包含搜索文本则返回true否则返回false // 如果该列的值不包含搜索文本则返回true否则返回false
return searchText.isEmpty() ? true : !value.toLowerCase().contains(searchText.toLowerCase()); Pattern pattern = new Pattern(searchText, REFlags.IGNORE_CASE);
return searchText.isEmpty() || !pattern.matcher(entryValue).find();
} }
}; };
} else { } else {
// 正向搜索:创建一个过滤器以包含与正则表达式匹配的行 // 正向搜索:创建一个过滤器以包含与正则表达式匹配的行
rowFilter = RowFilter.regexFilter("(?i)" + Pattern.quote(searchText), 1); rowFilter = RowFilter.regexFilter(String.format("(?i)%s", searchText), 1);
} }
// 设置过滤器到排序器 // 设置过滤器到排序器
@@ -230,51 +194,6 @@ public class DatatablePanel extends JPanel {
} }
} }
// 加载指定页的数据
private void loadPageData() {
if (fullList.size() > SHOW_LENGTH && scrollFlag) {
int start = currentPage * pageSize;
int end = Math.min((currentPage + 1) * pageSize, fullList.size());
int lastRow = model.getRowCount();
start = Math.max(start, lastRow);
for (int i = start; i < end; i++) {
addRowToTable(model, new Object[]{fullList.get(i)});
}
} else {
for (String item : fullList) {
addRowToTable(model, new Object[]{item});
}
}
}
private static void addRowToTable(DefaultTableModel model, Object[] data) {
// 获取当前ID
int rowCount = model.getRowCount();
int id = rowCount > 0 ? (Integer) model.getValueAt(rowCount - 1, 0) + 1 : 1;
Object[] rowData = new Object[data.length + 1];
rowData[0] = id; // 设置ID列的值
System.arraycopy(data, 0, rowData, 1, data.length); // 拷贝其余数据
model.addRow(rowData); // 添加行
}
public void updatePageSize() {
if (fullList.size() > SHOW_LENGTH && isShowing()) {
int oldPageSize = pageSize;
pageSize = getDynamicSize();
if (oldPageSize != pageSize) {
currentPage = 0;
loadPageData();
}
}
}
private int getDynamicSize() {
int visibleHeight = scrollPane.getViewport().getViewRect().height;
int rowHeight = table.getRowHeight();
return Math.max(1, visibleHeight / rowHeight + 5);
}
public void setTableListener(MessagePanel messagePanel) { public void setTableListener(MessagePanel messagePanel) {
table.setDefaultEditor(Object.class, null); table.setDefaultEditor(Object.class, null);

View File

@@ -182,9 +182,13 @@ public class MessagePanel extends AbstractTableModel implements IMessageEditorCo
public void applyHostFilter(String filterText) { public void applyHostFilter(String filterText) {
filteredLog.clear(); filteredLog.clear();
fireTableDataChanged(); fireTableDataChanged();
String cleanedText = StringHelper.replaceFirstOccurrence(filterText, "*.", "");
for (LogEntry entry : log) { for (LogEntry entry : log) {
String host = entry.getUrl().getHost(); String host = entry.getUrl().getHost();
if (StringHelper.matchFromEnd(host, filterText) || filterText.contains("*")) { if (filterText.contains("*.") && StringHelper.matchFromEnd(host, cleanedText)) {
filteredLog.add(entry);
} else if (host.equals(filterText) || filterText.contains("*")) {
filteredLog.add(entry); filteredLog.add(entry);
} }
} }