diff --git a/src/main/java/core/packetproxy/DNSSpoofingIPGetter.java b/src/main/java/core/packetproxy/DNSSpoofingIPGetter.java index 2e69a30f..85070edb 100644 --- a/src/main/java/core/packetproxy/DNSSpoofingIPGetter.java +++ b/src/main/java/core/packetproxy/DNSSpoofingIPGetter.java @@ -15,29 +15,29 @@ */ package packetproxy; -import packetproxy.gui.GUIOptionPrivateDNS; +import packetproxy.platform.SpoofingIPSource; public class DNSSpoofingIPGetter { - private final GUIOptionPrivateDNS gui; + private final SpoofingIPSource source; - public DNSSpoofingIPGetter(GUIOptionPrivateDNS gui) { - this.gui = gui; + public DNSSpoofingIPGetter(SpoofingIPSource source) { + this.source = source; } public boolean isAuto() { - return gui.isAutoSpoofing(); + return source.isAuto(); } public String get() { - return gui.getSpoofingIP(); + return source.get(); } public String get6() { - return gui.getSpoofingIP6(); + return source.get6(); } public String getInt() { - return gui.getBindInterface(); + return source.getInt(); } } diff --git a/src/main/java/core/packetproxy/Duplex.java b/src/main/java/core/packetproxy/Duplex.java index 9d9665f1..98f7d12f 100644 --- a/src/main/java/core/packetproxy/Duplex.java +++ b/src/main/java/core/packetproxy/Duplex.java @@ -22,10 +22,11 @@ import java.io.PipedInputStream; import java.io.PipedOutputStream; import java.util.EventListener; -import javax.swing.event.EventListenerList; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; public abstract class Duplex { - protected EventListenerList duplexEventListenerList = new EventListenerList(); + private final List duplexEventListeners = new CopyOnWriteArrayList<>(); private boolean flag_event_listener; private int PIPE_SIZE = 65536; private PipedOutputStream clientOutputForFlowControl; @@ -61,7 +62,7 @@ boolean isEnabledDuplexEventListener() { } public void addDuplexEventListener(DuplexEventListener listener) { - duplexEventListenerList.add(DuplexEventListener.class, listener); + duplexEventListeners.add(listener); enableDuplexEventListener(); } @@ -70,7 +71,7 @@ public int callOnClientPacketReceived(byte[] data) throws Exception { return data.length; } - for (DuplexEventListener listener : duplexEventListenerList.getListeners(DuplexEventListener.class)) { + for (DuplexEventListener listener : duplexEventListeners) { return listener.onClientPacketReceived(data); } @@ -82,7 +83,7 @@ public int callOnServerPacketReceived(byte[] data) throws Exception { return data.length; } - for (DuplexEventListener listener : duplexEventListenerList.getListeners(DuplexEventListener.class)) { + for (DuplexEventListener listener : duplexEventListeners) { return listener.onServerPacketReceived(data); } @@ -94,7 +95,7 @@ public void callOnClientChunkArrived(byte[] data) throws Exception { inputClientData.write(data); } - for (DuplexEventListener listener : duplexEventListenerList.getListeners(DuplexEventListener.class)) { + for (DuplexEventListener listener : duplexEventListeners) { listener.onClientChunkArrived(data); } @@ -105,7 +106,7 @@ public void callOnServerChunkArrived(byte[] data) throws Exception { inputServerData.write(data); } - for (DuplexEventListener listener : duplexEventListenerList.getListeners(DuplexEventListener.class)) { + for (DuplexEventListener listener : duplexEventListeners) { listener.onServerChunkArrived(data); } @@ -116,7 +117,7 @@ public byte[] callOnClientChunkPassThrough() throws Exception { return null; } - for (DuplexEventListener listener : duplexEventListenerList.getListeners(DuplexEventListener.class)) { + for (DuplexEventListener listener : duplexEventListeners) { return listener.onClientChunkPassThrough(); } @@ -128,7 +129,7 @@ public byte[] callOnServerChunkPassThrough() throws Exception { return null; } - for (DuplexEventListener listener : duplexEventListenerList.getListeners(DuplexEventListener.class)) { + for (DuplexEventListener listener : duplexEventListeners) { return listener.onServerChunkPassThrough(); } @@ -142,7 +143,7 @@ public byte[] callOnClientChunkAvailable() throws Exception { inputClientData.reset(); return ret; } - for (DuplexEventListener listener : duplexEventListenerList.getListeners(DuplexEventListener.class)) { + for (DuplexEventListener listener : duplexEventListeners) { return listener.onClientChunkAvailable(); } @@ -156,7 +157,7 @@ public byte[] callOnServerChunkAvailable() throws Exception { inputServerData.reset(); return ret; } - for (DuplexEventListener listener : duplexEventListenerList.getListeners(DuplexEventListener.class)) { + for (DuplexEventListener listener : duplexEventListeners) { return listener.onServerChunkAvailable(); } @@ -168,7 +169,7 @@ public byte[] callOnClientChunkReceived(byte[] data) throws Exception { return data; } - for (DuplexEventListener listener : duplexEventListenerList.getListeners(DuplexEventListener.class)) { + for (DuplexEventListener listener : duplexEventListeners) { return listener.onClientChunkReceived(data); } @@ -180,7 +181,7 @@ public byte[] callOnServerChunkReceived(byte[] data) throws Exception { return data; } - for (DuplexEventListener listener : duplexEventListenerList.getListeners(DuplexEventListener.class)) { + for (DuplexEventListener listener : duplexEventListeners) { return listener.onServerChunkReceived(data); } @@ -192,7 +193,7 @@ public byte[] callOnClientChunkSend(byte[] data) throws Exception { return data; } - for (DuplexEventListener listener : duplexEventListenerList.getListeners(DuplexEventListener.class)) { + for (DuplexEventListener listener : duplexEventListeners) { return listener.onClientChunkSend(data); } @@ -204,7 +205,7 @@ public byte[] callOnServerChunkSend(byte[] data) throws Exception { return data; } - for (DuplexEventListener listener : duplexEventListenerList.getListeners(DuplexEventListener.class)) { + for (DuplexEventListener listener : duplexEventListeners) { return listener.onServerChunkSend(data); } @@ -216,7 +217,7 @@ public byte[] callOnClientChunkSendForced(byte[] data) throws Exception { return data; } - for (DuplexEventListener listener : duplexEventListenerList.getListeners(DuplexEventListener.class)) { + for (DuplexEventListener listener : duplexEventListeners) { return listener.onClientChunkSendForced(data); } @@ -228,7 +229,7 @@ public byte[] callOnServerChunkSendForced(byte[] data) throws Exception { return data; } - for (DuplexEventListener listener : duplexEventListenerList.getListeners(DuplexEventListener.class)) { + for (DuplexEventListener listener : duplexEventListeners) { return listener.onServerChunkSendForced(data); } @@ -241,7 +242,7 @@ public void callOnClientChunkFlowControl(byte[] data) throws Exception { clientOutputForFlowControl.write(data); clientOutputForFlowControl.flush(); } - for (DuplexEventListener listener : duplexEventListenerList.getListeners(DuplexEventListener.class)) { + for (DuplexEventListener listener : duplexEventListeners) { listener.onClientChunkFlowControl(data); } @@ -252,7 +253,7 @@ public void closeOnClientChunkFlowControl() throws Exception { clientOutputForFlowControl.close(); } - for (DuplexEventListener listener : duplexEventListenerList.getListeners(DuplexEventListener.class)) { + for (DuplexEventListener listener : duplexEventListeners) { listener.closeClientChunkFlowControl(); } @@ -263,7 +264,7 @@ public InputStream getClientChunkFlowControlSink() throws Exception { return clientInputForFlowControl; } - for (DuplexEventListener listener : duplexEventListenerList.getListeners(DuplexEventListener.class)) { + for (DuplexEventListener listener : duplexEventListeners) { return listener.getClientChunkFlowControlSink(); } @@ -276,7 +277,7 @@ public void callOnServerChunkFlowControl(byte[] data) throws Exception { serverOutputForFlowControl.write(data); serverOutputForFlowControl.flush(); } - for (DuplexEventListener listener : duplexEventListenerList.getListeners(DuplexEventListener.class)) { + for (DuplexEventListener listener : duplexEventListeners) { listener.onServerChunkFlowControl(data); } @@ -287,7 +288,7 @@ public void closeOnServerChunkFlowControl() throws Exception { serverOutputForFlowControl.close(); } - for (DuplexEventListener listener : duplexEventListenerList.getListeners(DuplexEventListener.class)) { + for (DuplexEventListener listener : duplexEventListeners) { listener.closeServerChunkFlowControl(); } @@ -298,7 +299,7 @@ public InputStream getServerChunkFlowControlSink() throws Exception { return serverInputForFlowControl; } - for (DuplexEventListener listener : duplexEventListenerList.getListeners(DuplexEventListener.class)) { + for (DuplexEventListener listener : duplexEventListeners) { return listener.getServerChunkFlowControlSink(); } diff --git a/src/main/java/core/packetproxy/PPContextMenuManager.java b/src/main/java/core/packetproxy/PPContextMenuManager.java index 2b64c28e..e2466c52 100644 --- a/src/main/java/core/packetproxy/PPContextMenuManager.java +++ b/src/main/java/core/packetproxy/PPContextMenuManager.java @@ -30,14 +30,14 @@ import javax.tools.JavaFileObject; import javax.tools.StandardLocation; import javax.tools.ToolProvider; -import packetproxy.ppcontextmenu.PPContextMenu; +import packetproxy.gui.ppcontextmenu.PPContextMenu; public class PPContextMenuManager { private static PPContextMenuManager instance; private List module_list; - private static final String item_package = "packetproxy.ppcontextmenu"; - private static final Class item_class = packetproxy.ppcontextmenu.PPContextMenu.class; + private static final String item_package = "packetproxy.gui.ppcontextmenu"; + private static final Class item_class = packetproxy.gui.ppcontextmenu.PPContextMenu.class; public static PPContextMenuManager getInstance() throws Exception { if (instance == null) { diff --git a/src/main/java/core/packetproxy/common/ConfigHttpServer.java b/src/main/java/core/packetproxy/common/ConfigHttpServer.java index ca125627..8de7bf7d 100644 --- a/src/main/java/core/packetproxy/common/ConfigHttpServer.java +++ b/src/main/java/core/packetproxy/common/ConfigHttpServer.java @@ -7,13 +7,13 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import javax.swing.*; -import packetproxy.gui.GUIMain; import packetproxy.model.*; +import packetproxy.platform.ConfigHttpUiActions; public class ConfigHttpServer extends NanoHTTPD { private final String allowedAccessToken; + private final ConfigHttpUiActions uiActions; private static class DaoHub { @@ -30,9 +30,10 @@ private static class DaoHub { List sslPassThroughList; } - public ConfigHttpServer(String hostname, int port, String allowedAccessToken) { + public ConfigHttpServer(String hostname, int port, String allowedAccessToken, ConfigHttpUiActions uiActions) { super(hostname, port); this.allowedAccessToken = allowedAccessToken; + this.uiActions = uiActions; } private void fixUpServerList(Map serverMap, List serverList) { @@ -133,18 +134,9 @@ public Response serve(IHTTPSession session) { try { - GUIMain.getInstance().setAlwaysOnTop(true); - GUIMain.getInstance().setVisible(true); + uiActions.showOptionsTab(); - GUIMain.getInstance().getTabbedPane().setSelectedIndex(GUIMain.Panes.OPTIONS.ordinal()); - - int option = JOptionPane.showConfirmDialog(GUIMain.getInstance(), - I18nString.get("Do you want to overwrite config?"), I18nString.get("Loading config"), - JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE); - - GUIMain.getInstance().setAlwaysOnTop(false); - - if (option == JOptionPane.NO_OPTION) { + if (!uiActions.confirmOverwriteConfig()) { return NanoHTTPD.newFixedLengthResponse(Response.Status.UNAUTHORIZED, MIME_HTML, null); } diff --git a/src/main/java/core/packetproxy/controller/ResendController.java b/src/main/java/core/packetproxy/controller/ResendController.java index f3d362ca..51ac4521 100644 --- a/src/main/java/core/packetproxy/controller/ResendController.java +++ b/src/main/java/core/packetproxy/controller/ResendController.java @@ -20,9 +20,13 @@ import java.net.SocketTimeoutException; import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; -import javax.swing.SwingWorker; import packetproxy.Duplex; import packetproxy.DuplexAsync; import packetproxy.DuplexFactory; @@ -38,6 +42,16 @@ public class ResendController { private static ResendController instance; + private static volatile ResendProgressHandler progressHandler = (worker, packets) -> {}; + private final ExecutorService executor = Executors.newCachedThreadPool(); + + public static void setProgressHandler(ResendProgressHandler handler) { + progressHandler = handler; + } + + static void notifyProgress(ResendWorker worker, OneShotPacket packet) { + progressHandler.onProgress(worker, Collections.singletonList(packet)); + } public static ResendController getInstance() throws Exception { if (instance == null) { @@ -62,15 +76,14 @@ public void resend(OneShotPacket oneshot, int count) throws Exception { /** レスポンスを受け取って処理する必要がないとき用 */ public void resend(OneShotPacket oneshot, int count, boolean wait) throws Exception { - SwingWorker worker; - worker = new ResendWorker(oneshot, count); - worker.execute(); + var worker = new ResendWorker(oneshot, count); + Future future = executor.submit(worker::runInBackground); if (wait && count != 1) { try { // InterceptでForward x 20した時に先に本体が処理されると困るので待つ - worker.get(20000, TimeUnit.MILLISECONDS); + future.get(20000, TimeUnit.MILLISECONDS); } catch (Exception e) { errWithStackTrace(e); @@ -85,10 +98,10 @@ public void resend(OneShotPacket oneshot, int count, boolean wait) throws Except * @param worker */ public void resend(ResendWorker worker) { - worker.execute(); + executor.submit(worker::runInBackground); } - public static class ResendWorker extends SwingWorker { + public static class ResendWorker { int count; OneShotPacket oneshot; @@ -108,8 +121,23 @@ public ResendWorker(OneShotPacket[] oneshots) { this.oneshots = oneshots; } - @Override - protected Object doInBackground() throws Exception { + protected void publish(OneShotPacket packet) { + ResendController.notifyProgress(this, packet); + } + + public void process(List packets) {} + + public void runInBackground() { + try { + + doInBackground(); + } catch (Exception e) { + + errWithStackTrace(e); + } + } + + protected void doInBackground() throws Exception { try { ArrayList list = new ArrayList(); @@ -134,7 +162,7 @@ protected Object doInBackground() throws Exception { } else { err("Resend packet is wrong!"); - return null; + return; } list.stream().filter(o -> o.isDirectSend()).forEach(sendData -> { @@ -161,13 +189,12 @@ protected Object doInBackground() throws Exception { err("Resend Connection is timeout!"); err("All resend packets are dropped."); errWithStackTrace(e); - return null; + return; } catch (Exception e) { errWithStackTrace(e); throw e; } - return null; } private class DataToBeSend { diff --git a/src/main/java/core/packetproxy/controller/ResendProgressHandler.java b/src/main/java/core/packetproxy/controller/ResendProgressHandler.java new file mode 100644 index 00000000..54c87a7a --- /dev/null +++ b/src/main/java/core/packetproxy/controller/ResendProgressHandler.java @@ -0,0 +1,25 @@ +/* + * Copyright 2026 DeNA Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package packetproxy.controller; + +import java.util.List; +import packetproxy.model.OneShotPacket; + +/** Resend 進捗を EDT 等へ渡す(通常は GUI が SwingUtilities で登録)。 */ +public interface ResendProgressHandler { + + void onProgress(ResendController.ResendWorker worker, List packets); +} diff --git a/src/main/java/core/packetproxy/common/FilterTextParser.java b/src/main/java/core/packetproxy/gui/FilterTextParser.java similarity index 99% rename from src/main/java/core/packetproxy/common/FilterTextParser.java rename to src/main/java/core/packetproxy/gui/FilterTextParser.java index 132d3901..08b52ff0 100644 --- a/src/main/java/core/packetproxy/common/FilterTextParser.java +++ b/src/main/java/core/packetproxy/gui/FilterTextParser.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package packetproxy.common; +package packetproxy.gui; import static packetproxy.util.Logging.errWithStackTrace; @@ -29,7 +29,6 @@ import javax.swing.RowFilter.ComparisonType; import javax.swing.table.DefaultTableModel; import org.apache.commons.collections4.map.HashedMap; -import packetproxy.gui.GUIHistory; import packetproxy.model.Packet; import packetproxy.model.Packets; diff --git a/src/main/java/core/packetproxy/gui/GUIBulkSender.java b/src/main/java/core/packetproxy/gui/GUIBulkSender.java index 928595df..ebc3d623 100644 --- a/src/main/java/core/packetproxy/gui/GUIBulkSender.java +++ b/src/main/java/core/packetproxy/gui/GUIBulkSender.java @@ -118,7 +118,7 @@ public void actionPerformed(ActionEvent e) { ResendController.getInstance().resend(new ResendWorker(oneshots) { @Override - protected void process(List oneshots) { + public void process(List oneshots) { try { for (OneShotPacket oneshot : oneshots) { @@ -159,7 +159,7 @@ public void run() { ResendController.getInstance().resend(new ResendWorker(sendOneshot, 1) { @Override - protected void process(List oneshots) { + public void process(List oneshots) { try { for (OneShotPacket oneshot : oneshots) { diff --git a/src/main/java/core/packetproxy/gui/GUIBulkSenderTable.java b/src/main/java/core/packetproxy/gui/GUIBulkSenderTable.java index 9bf3c295..1ba72f89 100644 --- a/src/main/java/core/packetproxy/gui/GUIBulkSenderTable.java +++ b/src/main/java/core/packetproxy/gui/GUIBulkSenderTable.java @@ -40,7 +40,6 @@ import javax.swing.table.TableCellRenderer; import packetproxy.common.Utils; import packetproxy.model.OneShotPacket; -import packetproxy.model.OptionTableModel; import packetproxy.model.RegexParam; public class GUIBulkSenderTable { diff --git a/src/main/java/core/packetproxy/gui/GUIHistory.java b/src/main/java/core/packetproxy/gui/GUIHistory.java index a1d13131..e7e8978a 100644 --- a/src/main/java/core/packetproxy/gui/GUIHistory.java +++ b/src/main/java/core/packetproxy/gui/GUIHistory.java @@ -69,14 +69,12 @@ import javax.swing.table.DefaultTableModel; import javax.swing.table.TableCellRenderer; import javax.swing.table.TableRowSorter; -import packetproxy.common.FilterTextParser; import packetproxy.common.FontManager; import packetproxy.common.I18nString; import packetproxy.common.Utils; import packetproxy.model.Database; import packetproxy.model.Database.DatabaseMessage; import packetproxy.model.Filters; -import packetproxy.model.OptionTableModel; import packetproxy.model.Packet; import packetproxy.model.Packets; import packetproxy.model.ResenderPackets; diff --git a/src/main/java/core/packetproxy/gui/GUILogSink.java b/src/main/java/core/packetproxy/gui/GUILogSink.java new file mode 100644 index 00000000..aca92415 --- /dev/null +++ b/src/main/java/core/packetproxy/gui/GUILogSink.java @@ -0,0 +1,37 @@ +/* + * Copyright 2026 DeNA Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package packetproxy.gui; + +import packetproxy.platform.LogSink; + +public class GUILogSink implements LogSink { + + private final GUILog guiLog; + + public GUILogSink(GUILog guiLog) { + this.guiLog = guiLog; + } + + @Override + public void append(String message) { + guiLog.append(message); + } + + @Override + public void appendErr(String message) { + guiLog.appendErr(message); + } +} diff --git a/src/main/java/core/packetproxy/gui/GUIMain.java b/src/main/java/core/packetproxy/gui/GUIMain.java index f91cac5e..b2b7e7ab 100644 --- a/src/main/java/core/packetproxy/gui/GUIMain.java +++ b/src/main/java/core/packetproxy/gui/GUIMain.java @@ -33,6 +33,10 @@ import packetproxy.common.FontManager; import packetproxy.common.I18nString; import packetproxy.model.InterceptModel; +import packetproxy.controller.ResendController; +import packetproxy.platform.LogSinks; +import packetproxy.platform.MainWindows; +import packetproxy.platform.UserPrompts; import packetproxy.util.PacketProxyUtility; public class GUIMain extends JFrame implements PropertyChangeListener { @@ -100,6 +104,10 @@ private String getPaneString(Panes num) { private GUIMain(String title) { try { + UserPrompts.set(new SwingUserPrompt()); + MainWindows.set(new GUIMainWindowAccess(this)); + ResendController.setProgressHandler( + (worker, packets) -> SwingUtilities.invokeLater(() -> worker.process(packets))); setIcon(); gui_history = initProjectAndHistory(); setLookandFeel(); @@ -117,6 +125,7 @@ private GUIMain(String title) { gui_extensions = GUIExtensions.getInstance(); gui_vulcheckhelper = GUIVulCheckHelper.getInstance(); gui_log = GUILog.getInstance(); + LogSinks.set(new GUILogSink(gui_log)); tabbedpane = new JTabbedPane(); diff --git a/src/main/java/core/packetproxy/gui/GUIMainWindowAccess.java b/src/main/java/core/packetproxy/gui/GUIMainWindowAccess.java new file mode 100644 index 00000000..cae32360 --- /dev/null +++ b/src/main/java/core/packetproxy/gui/GUIMainWindowAccess.java @@ -0,0 +1,49 @@ +/* + * Copyright 2026 DeNA Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package packetproxy.gui; + +import java.awt.Frame; +import java.awt.Rectangle; +import packetproxy.platform.MainWindowAccess; + +public class GUIMainWindowAccess implements MainWindowAccess { + + private final GUIMain main; + + public GUIMainWindowAccess(GUIMain main) { + this.main = main; + } + + @Override + public Frame getFrame() { + return main; + } + + @Override + public Rectangle getBounds() { + return main.getBounds(); + } + + @Override + public void setAlwaysOnTop(boolean onTop) { + main.setAlwaysOnTop(onTop); + } + + @Override + public void setVisible(boolean visible) { + main.setVisible(visible); + } +} diff --git a/src/main/java/core/packetproxy/gui/GUIOptionComponentBase.java b/src/main/java/core/packetproxy/gui/GUIOptionComponentBase.java index a75c18a9..f3036c33 100644 --- a/src/main/java/core/packetproxy/gui/GUIOptionComponentBase.java +++ b/src/main/java/core/packetproxy/gui/GUIOptionComponentBase.java @@ -35,7 +35,6 @@ import javax.swing.table.TableRowSorter; import packetproxy.common.FontManager; import packetproxy.common.I18nString; -import packetproxy.model.OptionTableModel; public abstract class GUIOptionComponentBase implements PropertyChangeListener { diff --git a/src/main/java/core/packetproxy/gui/GUIOptionHubServer.java b/src/main/java/core/packetproxy/gui/GUIOptionHubServer.java index eee56367..6e19510f 100644 --- a/src/main/java/core/packetproxy/gui/GUIOptionHubServer.java +++ b/src/main/java/core/packetproxy/gui/GUIOptionHubServer.java @@ -104,7 +104,7 @@ private void stopServer() { private void startServer() throws Exception { String accessToken = new ConfigString("SharingConfigsAccessToken").getString(); - this.server = new ConfigHttpServer("localhost", 32349, accessToken); + this.server = new ConfigHttpServer("localhost", 32349, accessToken, new SwingConfigHttpUiActions()); this.server.start(); } diff --git a/src/main/java/core/packetproxy/gui/GUIOptionPrivateDNS.java b/src/main/java/core/packetproxy/gui/GUIOptionPrivateDNS.java index 7bd21618..ad39c15f 100644 --- a/src/main/java/core/packetproxy/gui/GUIOptionPrivateDNS.java +++ b/src/main/java/core/packetproxy/gui/GUIOptionPrivateDNS.java @@ -285,7 +285,7 @@ private JButton createDnsPortSetButton() { if (port == null) { return; } - privateDNS.setPort(port, new DNSSpoofingIPGetter(this)); + privateDNS.setPort(port, new DNSSpoofingIPGetter(new GUIOptionPrivateDNSSpoofingSource(this))); }); return button; } @@ -390,7 +390,7 @@ private JCheckBox createCheckBox() { return; } - if (!privateDNS.start(new DNSSpoofingIPGetter(this))) { + if (!privateDNS.start(new DNSSpoofingIPGetter(new GUIOptionPrivateDNSSpoofingSource(this)))) { checkBox.setSelected(false); showPrivateDnsStartErrorDialog(); } @@ -437,7 +437,7 @@ public void updateState() { if (!checkBox.isSelected()) { return; } - if (!privateDNS.start(new DNSSpoofingIPGetter(this))) { + if (!privateDNS.start(new DNSSpoofingIPGetter(new GUIOptionPrivateDNSSpoofingSource(this)))) { checkBox.setSelected(false); showPrivateDnsStartErrorDialog(); } @@ -504,7 +504,7 @@ private void restartPrivateDnsForBindingInterfaceChange() { if (!privateDNS.isRunning()) { return; } - if (!privateDNS.restart(new DNSSpoofingIPGetter(this))) { + if (!privateDNS.restart(new DNSSpoofingIPGetter(new GUIOptionPrivateDNSSpoofingSource(this)))) { checkBox.setSelected(false); showPrivateDnsStartErrorDialog(); } diff --git a/src/main/java/core/packetproxy/gui/GUIOptionPrivateDNSSpoofingSource.java b/src/main/java/core/packetproxy/gui/GUIOptionPrivateDNSSpoofingSource.java new file mode 100644 index 00000000..b3840884 --- /dev/null +++ b/src/main/java/core/packetproxy/gui/GUIOptionPrivateDNSSpoofingSource.java @@ -0,0 +1,47 @@ +/* + * Copyright 2026 DeNA Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package packetproxy.gui; + +import packetproxy.platform.SpoofingIPSource; + +public class GUIOptionPrivateDNSSpoofingSource implements SpoofingIPSource { + + private final GUIOptionPrivateDNS gui; + + public GUIOptionPrivateDNSSpoofingSource(GUIOptionPrivateDNS gui) { + this.gui = gui; + } + + @Override + public boolean isAuto() { + return gui.isAutoSpoofing(); + } + + @Override + public String get() { + return gui.getSpoofingIP(); + } + + @Override + public String get6() { + return gui.getSpoofingIP6(); + } + + @Override + public String getInt() { + return gui.getBindInterface(); + } +} diff --git a/src/main/java/core/packetproxy/gui/GUIRegexParamsTableDialog.java b/src/main/java/core/packetproxy/gui/GUIRegexParamsTableDialog.java index ee45d6e0..0673bfd7 100644 --- a/src/main/java/core/packetproxy/gui/GUIRegexParamsTableDialog.java +++ b/src/main/java/core/packetproxy/gui/GUIRegexParamsTableDialog.java @@ -23,7 +23,6 @@ import javax.swing.JTable; import javax.swing.table.TableRowSorter; import packetproxy.common.FontManager; -import packetproxy.model.OptionTableModel; import packetproxy.model.RegexParam; public class GUIRegexParamsTableDialog extends JDialog { diff --git a/src/main/java/core/packetproxy/gui/GUIResender.java b/src/main/java/core/packetproxy/gui/GUIResender.java index c43aa7cb..34915cc2 100644 --- a/src/main/java/core/packetproxy/gui/GUIResender.java +++ b/src/main/java/core/packetproxy/gui/GUIResender.java @@ -249,7 +249,7 @@ public void actionPerformed(ActionEvent e) { ResendController.getInstance().resend(new ResendWorker(sendPacket, 1) { @Override - protected void process(List packets) { + public void process(List packets) { try { OneShotPacket recvPacket = packets.get(0); diff --git a/src/main/java/core/packetproxy/gui/GUIVulCheckRecvTable.java b/src/main/java/core/packetproxy/gui/GUIVulCheckRecvTable.java index 64430951..560cb60f 100644 --- a/src/main/java/core/packetproxy/gui/GUIVulCheckRecvTable.java +++ b/src/main/java/core/packetproxy/gui/GUIVulCheckRecvTable.java @@ -25,7 +25,6 @@ import javax.swing.event.ListSelectionEvent; import javax.swing.table.TableCellRenderer; import packetproxy.model.OneShotPacket; -import packetproxy.model.OptionTableModel; public class GUIVulCheckRecvTable { diff --git a/src/main/java/core/packetproxy/gui/GUIVulCheckSendTable.java b/src/main/java/core/packetproxy/gui/GUIVulCheckSendTable.java index 595855ab..5b9ee779 100644 --- a/src/main/java/core/packetproxy/gui/GUIVulCheckSendTable.java +++ b/src/main/java/core/packetproxy/gui/GUIVulCheckSendTable.java @@ -28,7 +28,6 @@ import javax.swing.event.ListSelectionEvent; import javax.swing.table.TableCellRenderer; import packetproxy.model.OneShotPacket; -import packetproxy.model.OptionTableModel; public class GUIVulCheckSendTable { diff --git a/src/main/java/core/packetproxy/gui/GUIVulCheckTab.java b/src/main/java/core/packetproxy/gui/GUIVulCheckTab.java index d80a51df..04c59fec 100644 --- a/src/main/java/core/packetproxy/gui/GUIVulCheckTab.java +++ b/src/main/java/core/packetproxy/gui/GUIVulCheckTab.java @@ -172,7 +172,7 @@ public void actionPerformed(ActionEvent e) { ResendController.getInstance().resend(new ResendWorker(packet, 1) { @Override - protected void process(List oneshots) { + public void process(List oneshots) { Date recvTime = new Date(); try { @@ -216,7 +216,7 @@ public void actionPerformed(ActionEvent e) { ResendController.getInstance().resend(new ResendWorker(packet, 1) { @Override - protected void process(List oneshots) { + public void process(List oneshots) { Date recvTime = new Date(); try { diff --git a/src/main/java/core/packetproxy/model/OptionTableModel.java b/src/main/java/core/packetproxy/gui/OptionTableModel.java similarity index 97% rename from src/main/java/core/packetproxy/model/OptionTableModel.java rename to src/main/java/core/packetproxy/gui/OptionTableModel.java index 8dc665cb..53fb5112 100644 --- a/src/main/java/core/packetproxy/model/OptionTableModel.java +++ b/src/main/java/core/packetproxy/gui/OptionTableModel.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package packetproxy.model; +package packetproxy.gui; import javax.swing.table.DefaultTableModel; import org.apache.commons.lang3.ObjectUtils.Null; diff --git a/src/main/java/core/packetproxy/gui/SwingConfigHttpUiActions.java b/src/main/java/core/packetproxy/gui/SwingConfigHttpUiActions.java new file mode 100644 index 00000000..fadcf844 --- /dev/null +++ b/src/main/java/core/packetproxy/gui/SwingConfigHttpUiActions.java @@ -0,0 +1,64 @@ +/* + * Copyright 2026 DeNA Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package packetproxy.gui; + +import javax.swing.JOptionPane; +import packetproxy.common.I18nString; +import packetproxy.platform.ConfigHttpUiActions; + +public class SwingConfigHttpUiActions implements ConfigHttpUiActions { + + @Override + public void showOptionsTab() { + try { + showOptionsTabInternal(); + } catch (Exception e) { + throw new IllegalStateException(e); + } + } + + private void showOptionsTabInternal() throws Exception { + var main = GUIMain.getInstance(); + main.setAlwaysOnTop(true); + main.setVisible(true); + main.getTabbedPane().setSelectedIndex(GUIMain.Panes.OPTIONS.ordinal()); + main.setAlwaysOnTop(false); + } + + @Override + public boolean confirmOverwriteConfig() { + try { + return confirmOverwriteConfigInternal(); + } catch (Exception e) { + throw new IllegalStateException(e); + } + } + + private boolean confirmOverwriteConfigInternal() throws Exception { + var main = GUIMain.getInstance(); + main.setAlwaysOnTop(true); + main.setVisible(true); + int option = + JOptionPane.showConfirmDialog( + main, + I18nString.get("Do you want to overwrite config?"), + I18nString.get("Loading config"), + JOptionPane.YES_NO_OPTION, + JOptionPane.WARNING_MESSAGE); + main.setAlwaysOnTop(false); + return option == JOptionPane.YES_OPTION; + } +} diff --git a/src/main/java/core/packetproxy/gui/SwingUserPrompt.java b/src/main/java/core/packetproxy/gui/SwingUserPrompt.java new file mode 100644 index 00000000..fafe3376 --- /dev/null +++ b/src/main/java/core/packetproxy/gui/SwingUserPrompt.java @@ -0,0 +1,29 @@ +/* + * Copyright 2026 DeNA Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package packetproxy.gui; + +import javax.swing.JOptionPane; +import packetproxy.platform.UserPrompt; + +public class SwingUserPrompt implements UserPrompt { + + @Override + public boolean confirmTableRecreate(String tableName, String message) { + int option = JOptionPane.showConfirmDialog(null, message, "テーブルの更新", JOptionPane.YES_NO_OPTION, + JOptionPane.WARNING_MESSAGE); + return option == JOptionPane.YES_OPTION; + } +} diff --git a/src/main/java/core/packetproxy/extensions/randomness/RandomnessExtension.java b/src/main/java/core/packetproxy/gui/extensions/randomness/RandomnessExtension.java similarity index 99% rename from src/main/java/core/packetproxy/extensions/randomness/RandomnessExtension.java rename to src/main/java/core/packetproxy/gui/extensions/randomness/RandomnessExtension.java index 2864af7a..8e35eab4 100644 --- a/src/main/java/core/packetproxy/extensions/randomness/RandomnessExtension.java +++ b/src/main/java/core/packetproxy/gui/extensions/randomness/RandomnessExtension.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package packetproxy.extensions.randomness; +package packetproxy.gui.extensions.randomness; import static packetproxy.util.Logging.errWithStackTrace; import static packetproxy.util.Logging.log; @@ -186,7 +186,7 @@ public void actionPerformed(ActionEvent event) { ResendController.getInstance().resend(new ResendController.ResendWorker(sendPacket, 1) { @Override - protected void process(List oneshots) { + public void process(List oneshots) { int id = requestProgressBar.getValue(); for (OneShotPacket oneshot : oneshots) { diff --git a/src/main/java/core/packetproxy/ppcontextmenu/PPContextMenu.java b/src/main/java/core/packetproxy/gui/ppcontextmenu/PPContextMenu.java similarity index 97% rename from src/main/java/core/packetproxy/ppcontextmenu/PPContextMenu.java rename to src/main/java/core/packetproxy/gui/ppcontextmenu/PPContextMenu.java index 91669671..d51450d3 100644 --- a/src/main/java/core/packetproxy/ppcontextmenu/PPContextMenu.java +++ b/src/main/java/core/packetproxy/gui/ppcontextmenu/PPContextMenu.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package packetproxy.ppcontextmenu; +package packetproxy.gui.ppcontextmenu; import static packetproxy.util.Logging.errWithStackTrace; import static packetproxy.util.Logging.log; diff --git a/src/main/java/core/packetproxy/ppcontextmenu/SampleItem.java b/src/main/java/core/packetproxy/gui/ppcontextmenu/SampleItem.java similarity index 97% rename from src/main/java/core/packetproxy/ppcontextmenu/SampleItem.java rename to src/main/java/core/packetproxy/gui/ppcontextmenu/SampleItem.java index 2ab25f8a..5e4631e9 100644 --- a/src/main/java/core/packetproxy/ppcontextmenu/SampleItem.java +++ b/src/main/java/core/packetproxy/gui/ppcontextmenu/SampleItem.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package packetproxy.ppcontextmenu; +package packetproxy.gui.ppcontextmenu; import java.io.File; import javax.swing.JFrame; diff --git a/src/main/java/core/packetproxy/model/ClientCertificates.java b/src/main/java/core/packetproxy/model/ClientCertificates.java index 948ce6e3..c77fd3f7 100644 --- a/src/main/java/core/packetproxy/model/ClientCertificates.java +++ b/src/main/java/core/packetproxy/model/ClientCertificates.java @@ -24,8 +24,8 @@ import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; import java.util.List; -import javax.swing.JOptionPane; import packetproxy.common.ClientKeyManager; +import packetproxy.platform.UserPrompts; import packetproxy.model.Database.DatabaseMessage; /** DAO for ClientCertificate */ @@ -168,10 +168,8 @@ private boolean isLatestVersion() throws Exception { } private void RecreateTable() throws Exception { - int option = JOptionPane.showConfirmDialog(null, - "client_certificatesテーブルの形式が更新されているため\n現在のテーブルを削除して再起動しても良いですか?", "テーブルの更新", JOptionPane.YES_NO_OPTION, - JOptionPane.WARNING_MESSAGE); - if (option == JOptionPane.YES_OPTION) { + if (UserPrompts.get().confirmTableRecreate("client_certificates", + "client_certificatesテーブルの形式が更新されているため\n現在のテーブルを削除して再起動しても良いですか?")) { database.dropTable(ClientCertificate.class); dao = database.createTable(ClientCertificate.class, this); diff --git a/src/main/java/core/packetproxy/model/Extensions.java b/src/main/java/core/packetproxy/model/Extensions.java index ef221d45..67ca0804 100644 --- a/src/main/java/core/packetproxy/model/Extensions.java +++ b/src/main/java/core/packetproxy/model/Extensions.java @@ -33,8 +33,8 @@ import java.util.Map; import java.util.jar.JarEntry; import java.util.jar.JarFile; -import javax.swing.JOptionPane; -import packetproxy.extensions.randomness.RandomnessExtension; +import packetproxy.gui.extensions.randomness.RandomnessExtension; +import packetproxy.platform.UserPrompts; import packetproxy.extensions.samplehttp.SampleEncoders; import packetproxy.extensions.securityheaders.SecurityHeadersExtension; import packetproxy.model.Database.DatabaseMessage; @@ -313,9 +313,8 @@ private boolean isLatestVersion() throws Exception { } private void RecreateTable() throws Exception { - int option = JOptionPane.showConfirmDialog(null, "Extensionsテーブルの形式が更新されているため\n現在のテーブルを削除して再起動しても良いですか?", - "テーブルの更新", JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE); - if (option == JOptionPane.YES_OPTION) { + if (UserPrompts.get().confirmTableRecreate("Extensions", + "Extensionsテーブルの形式が更新されているため\n現在のテーブルを削除して再起動しても良いですか?")) { database.dropTable(Extension.class); dao = database.createTable(Extension.class, this); diff --git a/src/main/java/core/packetproxy/model/Filters.java b/src/main/java/core/packetproxy/model/Filters.java index 04cf19a3..cff8f48b 100644 --- a/src/main/java/core/packetproxy/model/Filters.java +++ b/src/main/java/core/packetproxy/model/Filters.java @@ -24,8 +24,8 @@ import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; import java.util.List; -import javax.swing.JOptionPane; import packetproxy.model.Database.DatabaseMessage; +import packetproxy.platform.UserPrompts; public class Filters implements PropertyChangeListener { @@ -145,9 +145,7 @@ private boolean isLatestVersion() throws Exception { } private void RecreateTable() throws Exception { - int option = JOptionPane.showConfirmDialog(null, "filtersテーブルの形式が更新されているため\n現在のテーブルを削除して再起動しても良いですか?", - "テーブルの更新", JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE); - if (option == JOptionPane.YES_OPTION) { + if (UserPrompts.get().confirmTableRecreate("filters", "filtersテーブルの形式が更新されているため\n現在のテーブルを削除して再起動しても良いですか?")) { database.dropTable(Filter.class); dao = database.createTable(Filter.class, this); diff --git a/src/main/java/core/packetproxy/model/InterceptOptions.java b/src/main/java/core/packetproxy/model/InterceptOptions.java index 283e3395..719183d2 100644 --- a/src/main/java/core/packetproxy/model/InterceptOptions.java +++ b/src/main/java/core/packetproxy/model/InterceptOptions.java @@ -25,8 +25,8 @@ import java.beans.PropertyChangeSupport; import java.util.ArrayList; import java.util.List; -import javax.swing.JOptionPane; import packetproxy.model.Database.DatabaseMessage; +import packetproxy.platform.UserPrompts; import packetproxy.model.InterceptOption.Direction; public class InterceptOptions implements PropertyChangeListener { @@ -349,9 +349,8 @@ public boolean isEnabled() throws Exception { } private void RecreateTable() throws Exception { - int option = JOptionPane.showConfirmDialog(null, "InterceptOptionsテーブルの形式が更新されているため\n現在のテーブルを削除して再起動しても良いですか?", - "テーブルの更新", JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE); - if (option == JOptionPane.YES_OPTION) { + if (UserPrompts.get().confirmTableRecreate("InterceptOptions", + "InterceptOptionsテーブルの形式が更新されているため\n現在のテーブルを削除して再起動しても良いですか?")) { database.dropTable(InterceptOption.class); dao = database.createTable(InterceptOption.class, this); diff --git a/src/main/java/core/packetproxy/model/Modifications.java b/src/main/java/core/packetproxy/model/Modifications.java index 7faa7a26..9f5f1d50 100644 --- a/src/main/java/core/packetproxy/model/Modifications.java +++ b/src/main/java/core/packetproxy/model/Modifications.java @@ -23,8 +23,8 @@ import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; import java.util.List; -import javax.swing.JOptionPane; import packetproxy.model.Database.DatabaseMessage; +import packetproxy.platform.UserPrompts; public class Modifications implements PropertyChangeListener { @@ -210,9 +210,8 @@ private boolean isLatestVersion() throws Exception { } private void RecreateTable() throws Exception { - int option = JOptionPane.showConfirmDialog(null, "Modificationsテーブルの形式が更新されているため\n現在のテーブルを削除して再起動しても良いですか?", - "テーブルの更新", JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE); - if (option == JOptionPane.YES_OPTION) { + if (UserPrompts.get().confirmTableRecreate("Modifications", + "Modificationsテーブルの形式が更新されているため\n現在のテーブルを削除して再起動しても良いですか?")) { database.dropTable(Modification.class); dao = database.createTable(Modification.class, this); diff --git a/src/main/java/core/packetproxy/model/OpenVPNForwardPorts.java b/src/main/java/core/packetproxy/model/OpenVPNForwardPorts.java index b20ecd23..9697f183 100644 --- a/src/main/java/core/packetproxy/model/OpenVPNForwardPorts.java +++ b/src/main/java/core/packetproxy/model/OpenVPNForwardPorts.java @@ -24,8 +24,8 @@ import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; import java.util.List; -import javax.swing.JOptionPane; import packetproxy.model.Database.DatabaseMessage; +import packetproxy.platform.UserPrompts; public class OpenVPNForwardPorts implements PropertyChangeListener { @@ -176,10 +176,8 @@ private boolean isLatestVersion() throws Exception { } private void RecreateTable() throws Exception { - int option = JOptionPane.showConfirmDialog(null, - "OpenVPNForwardPortsテーブルの形式が更新されているため\n現在のテーブルを削除して再起動しても良いですか?", "テーブルの更新", JOptionPane.YES_NO_OPTION, - JOptionPane.WARNING_MESSAGE); - if (option == JOptionPane.YES_OPTION) { + if (UserPrompts.get().confirmTableRecreate("OpenVPNForwardPorts", + "OpenVPNForwardPortsテーブルの形式が更新されているため\n現在のテーブルを削除して再起動しても良いですか?")) { database.dropTable(OpenVPNForwardPort.class); dao = database.createTable(OpenVPNForwardPort.class, this); diff --git a/src/main/java/core/packetproxy/model/Packets.java b/src/main/java/core/packetproxy/model/Packets.java index f00e96da..849b52cd 100644 --- a/src/main/java/core/packetproxy/model/Packets.java +++ b/src/main/java/core/packetproxy/model/Packets.java @@ -27,8 +27,8 @@ import java.util.List; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; -import javax.swing.JOptionPane; import packetproxy.common.Logger; +import packetproxy.platform.UserPrompts; import packetproxy.model.Database.DatabaseMessage; public class Packets implements PropertyChangeListener { @@ -251,9 +251,7 @@ private boolean isLatestVersion() throws Exception { } private void RecreateTable() throws Exception { - int option = JOptionPane.showConfirmDialog(null, "packetsテーブルの形式が更新されているため\n現在のテーブルを削除して再起動しても良いですか?", - "テーブルの更新", JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE); - if (option == JOptionPane.YES_OPTION) { + if (UserPrompts.get().confirmTableRecreate("packets", "packetsテーブルの形式が更新されているため\n現在のテーブルを削除して再起動しても良いですか?")) { database.dropTable(Packet.class); dao = database.createTable(Packet.class); diff --git a/src/main/java/core/packetproxy/model/ResenderPackets.java b/src/main/java/core/packetproxy/model/ResenderPackets.java index a8269019..3be892e9 100644 --- a/src/main/java/core/packetproxy/model/ResenderPackets.java +++ b/src/main/java/core/packetproxy/model/ResenderPackets.java @@ -10,8 +10,8 @@ import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; import java.util.List; -import javax.swing.JOptionPane; import packetproxy.model.Database.DatabaseMessage; +import packetproxy.platform.UserPrompts; public class ResenderPackets implements PropertyChangeListener { @@ -124,9 +124,8 @@ private boolean isLatestVersion() throws Exception { } private void RecreateTable() throws Exception { - int option = JOptionPane.showConfirmDialog(null, "resender_packetsテーブルの形式が更新されているため\n現在のテーブルを削除して再起動しても良いですか?", - "テーブルの更新", JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE); - if (option == JOptionPane.YES_OPTION) { + if (UserPrompts.get().confirmTableRecreate("resender_packets", + "resender_packetsテーブルの形式が更新されているため\n現在のテーブルを削除して再起動しても良いですか?")) { database.dropTable(ResenderPacket.class); dao = database.createTable(ResenderPacket.class, this); diff --git a/src/main/java/core/packetproxy/model/SSLPassThroughs.java b/src/main/java/core/packetproxy/model/SSLPassThroughs.java index 80073b80..a0940fca 100644 --- a/src/main/java/core/packetproxy/model/SSLPassThroughs.java +++ b/src/main/java/core/packetproxy/model/SSLPassThroughs.java @@ -24,8 +24,8 @@ import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; import java.util.List; -import javax.swing.JOptionPane; import packetproxy.ListenPortManager; +import packetproxy.platform.UserPrompts; import packetproxy.model.Database.DatabaseMessage; public class SSLPassThroughs implements PropertyChangeListener { @@ -235,9 +235,8 @@ private boolean isLatestVersion() throws Exception { } private void RecreateTable() throws Exception { - int option = JOptionPane.showConfirmDialog(null, "SSLPassThroughsテーブルの形式が更新されているため\n現在のテーブルを削除して再起動しても良いですか?", - "テーブルの更新", JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE); - if (option == JOptionPane.YES_OPTION) { + if (UserPrompts.get().confirmTableRecreate("SSLPassThroughs", + "SSLPassThroughsテーブルの形式が更新されているため\n現在のテーブルを削除して再起動しても良いですか?")) { database.dropTable(SSLPassThrough.class); dao = database.createTable(SSLPassThrough.class, this); diff --git a/src/main/java/core/packetproxy/platform/ConfigHttpUiActions.java b/src/main/java/core/packetproxy/platform/ConfigHttpUiActions.java new file mode 100644 index 00000000..a9f63de0 --- /dev/null +++ b/src/main/java/core/packetproxy/platform/ConfigHttpUiActions.java @@ -0,0 +1,25 @@ +/* + * Copyright 2026 DeNA Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package packetproxy.platform; + +/** リモート設定 HTTP API から必要な GUI 操作。 */ +public interface ConfigHttpUiActions { + + void showOptionsTab(); + + /** @return 設定上書きを許可する場合 true */ + boolean confirmOverwriteConfig(); +} diff --git a/src/main/java/core/packetproxy/platform/ConsoleUserPrompt.java b/src/main/java/core/packetproxy/platform/ConsoleUserPrompt.java new file mode 100644 index 00000000..d4faf02c --- /dev/null +++ b/src/main/java/core/packetproxy/platform/ConsoleUserPrompt.java @@ -0,0 +1,28 @@ +/* + * Copyright 2026 DeNA Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package packetproxy.platform; + +import static packetproxy.util.Logging.err; + +/** 非対話環境(Gulp 等)向け。確認は行わず、テーブル再作成は拒否する。 */ +public class ConsoleUserPrompt implements UserPrompt { + + @Override + public boolean confirmTableRecreate(String tableName, String message) { + err("Table schema mismatch for %s (recreate declined in non-interactive mode): %s", tableName, message); + return false; + } +} diff --git a/src/main/java/core/packetproxy/platform/LogSink.java b/src/main/java/core/packetproxy/platform/LogSink.java new file mode 100644 index 00000000..9e4153f7 --- /dev/null +++ b/src/main/java/core/packetproxy/platform/LogSink.java @@ -0,0 +1,24 @@ +/* + * Copyright 2026 DeNA Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package packetproxy.platform; + +/** GUI 等が実装する、アプリケーション内ログ表示の抽象化。 */ +public interface LogSink { + + void append(String message); + + void appendErr(String message); +} diff --git a/src/main/java/core/packetproxy/platform/LogSinks.java b/src/main/java/core/packetproxy/platform/LogSinks.java new file mode 100644 index 00000000..3c4f7e8c --- /dev/null +++ b/src/main/java/core/packetproxy/platform/LogSinks.java @@ -0,0 +1,46 @@ +/* + * Copyright 2026 DeNA Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package packetproxy.platform; + +/** {@link LogSink} の実行時登録先。 */ +public final class LogSinks { + + private static volatile LogSink sink; + + private LogSinks() {} + + public static void set(LogSink logSink) { + sink = logSink; + } + + public static void clear() { + sink = null; + } + + public static void append(String message) { + var current = sink; + if (current != null) { + current.append(message); + } + } + + public static void appendErr(String message) { + var current = sink; + if (current != null) { + current.appendErr(message); + } + } +} diff --git a/src/main/java/core/packetproxy/platform/MainWindowAccess.java b/src/main/java/core/packetproxy/platform/MainWindowAccess.java new file mode 100644 index 00000000..da008fbc --- /dev/null +++ b/src/main/java/core/packetproxy/platform/MainWindowAccess.java @@ -0,0 +1,31 @@ +/* + * Copyright 2026 DeNA Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package packetproxy.platform; + +import java.awt.Frame; +import java.awt.Rectangle; + +/** メインウィンドウへの参照(vulchecker 等が利用)。 */ +public interface MainWindowAccess { + + Frame getFrame(); + + Rectangle getBounds(); + + void setAlwaysOnTop(boolean onTop); + + void setVisible(boolean visible); +} diff --git a/src/main/java/core/packetproxy/platform/MainWindows.java b/src/main/java/core/packetproxy/platform/MainWindows.java new file mode 100644 index 00000000..6ee5f6dd --- /dev/null +++ b/src/main/java/core/packetproxy/platform/MainWindows.java @@ -0,0 +1,34 @@ +/* + * Copyright 2026 DeNA Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package packetproxy.platform; + +public final class MainWindows { + + private static volatile MainWindowAccess instance; + + private MainWindows() {} + + public static MainWindowAccess get() { + if (instance == null) { + throw new IllegalStateException("MainWindowAccess is not registered"); + } + return instance; + } + + public static void set(MainWindowAccess access) { + instance = access; + } +} diff --git a/src/main/java/core/packetproxy/platform/PacketPredicate.java b/src/main/java/core/packetproxy/platform/PacketPredicate.java new file mode 100644 index 00000000..5afc4866 --- /dev/null +++ b/src/main/java/core/packetproxy/platform/PacketPredicate.java @@ -0,0 +1,25 @@ +/* + * Copyright 2026 DeNA Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package packetproxy.platform; + +import packetproxy.model.Packet; + +/** パケット履歴フィルタの評価(Swing 非依存)。 */ +@FunctionalInterface +public interface PacketPredicate { + + boolean test(Packet packet); +} diff --git a/src/main/java/core/packetproxy/platform/SpoofingIPSource.java b/src/main/java/core/packetproxy/platform/SpoofingIPSource.java new file mode 100644 index 00000000..084586c7 --- /dev/null +++ b/src/main/java/core/packetproxy/platform/SpoofingIPSource.java @@ -0,0 +1,28 @@ +/* + * Copyright 2026 DeNA Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package packetproxy.platform; + +/** Private DNS のスプーフ IP 設定(GUI 実装が提供する)。 */ +public interface SpoofingIPSource { + + boolean isAuto(); + + String get(); + + String get6(); + + String getInt(); +} diff --git a/src/main/java/core/packetproxy/platform/UserPrompt.java b/src/main/java/core/packetproxy/platform/UserPrompt.java new file mode 100644 index 00000000..370a4d66 --- /dev/null +++ b/src/main/java/core/packetproxy/platform/UserPrompt.java @@ -0,0 +1,27 @@ +/* + * Copyright 2026 DeNA Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package packetproxy.platform; + +/** UI や CLI が実装する、ユーザー確認ダイアログの抽象化。 */ +public interface UserPrompt { + + /** + * スキーマ不一致時にテーブル再作成の可否をユーザーに尋ねる。 + * + * @return 再作成してよい場合は true + */ + boolean confirmTableRecreate(String tableName, String message); +} diff --git a/src/main/java/core/packetproxy/platform/UserPrompts.java b/src/main/java/core/packetproxy/platform/UserPrompts.java new file mode 100644 index 00000000..ff846064 --- /dev/null +++ b/src/main/java/core/packetproxy/platform/UserPrompts.java @@ -0,0 +1,33 @@ +/* + * Copyright 2026 DeNA Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package packetproxy.platform; + +/** {@link UserPrompt} の実行時登録先。起動モード(GUI / Gulp)に応じて実装を差し替える。 */ +public final class UserPrompts { + + private static volatile UserPrompt instance = new ConsoleUserPrompt(); + + private UserPrompts() { + } + + public static UserPrompt get() { + return instance; + } + + public static void set(UserPrompt prompt) { + instance = prompt; + } +} diff --git a/src/main/java/core/packetproxy/vulchecker/generator/JWTHeaderAddSpecifiedJKUGenerator.java b/src/main/java/core/packetproxy/vulchecker/generator/JWTHeaderAddSpecifiedJKUGenerator.java index 0fa21bb4..6fb67081 100644 --- a/src/main/java/core/packetproxy/vulchecker/generator/JWTHeaderAddSpecifiedJKUGenerator.java +++ b/src/main/java/core/packetproxy/vulchecker/generator/JWTHeaderAddSpecifiedJKUGenerator.java @@ -21,7 +21,7 @@ import java.awt.event.MouseEvent; import javax.swing.*; import org.apache.commons.codec.binary.Hex; -import packetproxy.gui.GUIMain; +import packetproxy.platform.MainWindows; public class JWTHeaderAddSpecifiedJKUGenerator extends Generator { @@ -41,9 +41,10 @@ public boolean generateOnStart() { public String generate(String inputData) throws Exception { cancelClicked = false; - JDialog dlg = new JDialog(GUIMain.getInstance()); + var mainWindow = MainWindows.get(); + JDialog dlg = new JDialog(mainWindow.getFrame()); - Rectangle rect = GUIMain.getInstance().getBounds(); + Rectangle rect = mainWindow.getBounds(); int width = 400; int height = 300; dlg.setBounds(rect.x + rect.width / 2 - width / 2, rect.y + rect.height / 2 - height / 2, width, @@ -82,12 +83,12 @@ public void mouseClicked(MouseEvent e) { }); buttons.add(cancel); - JPanel main = new JPanel(); - main.setLayout(new BoxLayout(main, BoxLayout.Y_AXIS)); - main.add(labels); - main.add(scrollpane); - main.add(buttons); - dlg.getContentPane().add(main); + JPanel content = new JPanel(); + content.setLayout(new BoxLayout(content, BoxLayout.Y_AXIS)); + content.add(labels); + content.add(scrollpane); + content.add(buttons); + dlg.getContentPane().add(content); dlg.setModal(true); dlg.setVisible(true); diff --git a/src/main/java/core/packetproxy/vulchecker/generator/JWTHeaderRS256toHS256Generator.java b/src/main/java/core/packetproxy/vulchecker/generator/JWTHeaderRS256toHS256Generator.java index 559885f0..de4cd84a 100644 --- a/src/main/java/core/packetproxy/vulchecker/generator/JWTHeaderRS256toHS256Generator.java +++ b/src/main/java/core/packetproxy/vulchecker/generator/JWTHeaderRS256toHS256Generator.java @@ -20,7 +20,7 @@ import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import javax.swing.*; -import packetproxy.gui.GUIMain; +import packetproxy.platform.MainWindows; public class JWTHeaderRS256toHS256Generator extends Generator { @@ -40,9 +40,10 @@ public boolean generateOnStart() { public String generate(String inputData) throws Exception { cancelClicked = false; - JDialog dlg = new JDialog(GUIMain.getInstance()); + var mainWindow = MainWindows.get(); + JDialog dlg = new JDialog(mainWindow.getFrame()); - Rectangle rect = GUIMain.getInstance().getBounds(); + Rectangle rect = mainWindow.getBounds(); int width = 400; int height = 300; dlg.setBounds(rect.x + rect.width / 2 - width / 2, rect.y + rect.height / 2 - height / 2, width, @@ -81,12 +82,12 @@ public void mouseClicked(MouseEvent e) { }); buttons.add(cancel); - JPanel main = new JPanel(); - main.setLayout(new BoxLayout(main, BoxLayout.Y_AXIS)); - main.add(labels); - main.add(scrollpane); - main.add(buttons); - dlg.getContentPane().add(main); + JPanel content = new JPanel(); + content.setLayout(new BoxLayout(content, BoxLayout.Y_AXIS)); + content.add(labels); + content.add(scrollpane); + content.add(buttons); + dlg.getContentPane().add(content); dlg.setModal(true); dlg.setVisible(true); diff --git a/src/main/java/core/packetproxy/vulchecker/generator/JWTSignWithAppleOtherTokenGenerator.java b/src/main/java/core/packetproxy/vulchecker/generator/JWTSignWithAppleOtherTokenGenerator.java index ab99b0e4..c8263260 100644 --- a/src/main/java/core/packetproxy/vulchecker/generator/JWTSignWithAppleOtherTokenGenerator.java +++ b/src/main/java/core/packetproxy/vulchecker/generator/JWTSignWithAppleOtherTokenGenerator.java @@ -24,7 +24,7 @@ import java.net.URI; import javax.swing.*; import packetproxy.common.TokenHttpServer; -import packetproxy.gui.GUIMain; +import packetproxy.platform.MainWindows; public class JWTSignWithAppleOtherTokenGenerator extends Generator { @@ -49,9 +49,10 @@ public String generate(String inputData) throws Exception { this.tokenFromBrowser = ""; this.cancelClicked = false; - JDialog dlg = new JDialog(GUIMain.getInstance()); + var mainWindow = MainWindows.get(); + JDialog dlg = new JDialog(mainWindow.getFrame()); - Rectangle rect = GUIMain.getInstance().getBounds(); + Rectangle rect = mainWindow.getBounds(); int width = 300; int height = 150; dlg.setBounds(rect.x + rect.width / 2 - width / 2, rect.y + rect.height / 2 - height / 2, width, @@ -105,11 +106,11 @@ public void mouseClicked(MouseEvent e) { } server.start(); - JPanel main = new JPanel(); - main.setLayout(new BoxLayout(main, BoxLayout.Y_AXIS)); - main.add(labels); - main.add(buttons); - dlg.getContentPane().add(main); + JPanel content = new JPanel(); + content.setLayout(new BoxLayout(content, BoxLayout.Y_AXIS)); + content.add(labels); + content.add(buttons); + dlg.getContentPane().add(content); dlg.setModal(true); dlg.setVisible(true); @@ -118,15 +119,15 @@ public void mouseClicked(MouseEvent e) { throw new Exception("cancel"); } - GUIMain.getInstance().setAlwaysOnTop(true); - GUIMain.getInstance().setVisible(true); + mainWindow.setAlwaysOnTop(true); + mainWindow.setVisible(true); // Need to wait for the server to finish sending the response data before // exiting Thread.sleep(100); server.stop(); - GUIMain.getInstance().setAlwaysOnTop(false); + mainWindow.setAlwaysOnTop(false); return tokenFromBrowser; } diff --git a/src/main/kotlin/core/packetproxy/AppInitializer.kt b/src/main/kotlin/core/packetproxy/AppInitializer.kt index d08aceed..6e57d11d 100644 --- a/src/main/kotlin/core/packetproxy/AppInitializer.kt +++ b/src/main/kotlin/core/packetproxy/AppInitializer.kt @@ -9,6 +9,8 @@ import packetproxy.common.ConfigIO import packetproxy.common.Utils import packetproxy.model.Database import packetproxy.model.Packets +import packetproxy.platform.ConsoleUserPrompt +import packetproxy.platform.UserPrompts import packetproxy.util.Logging object AppInitializer { @@ -30,6 +32,10 @@ object AppInitializer { fun initCore() { check(isCoreNotReady) { "initCore() has already been done !" } + if (isGulp) { + UserPrompts.set(ConsoleUserPrompt()) + } + // ログ機能のエラーについては標準エラー出力への出力を行い終了する try { Logging.init(isGulp) diff --git a/src/main/kotlin/core/packetproxy/util/Logging.kt b/src/main/kotlin/core/packetproxy/util/Logging.kt index 9b9ce0b2..4f8418c3 100644 --- a/src/main/kotlin/core/packetproxy/util/Logging.kt +++ b/src/main/kotlin/core/packetproxy/util/Logging.kt @@ -33,11 +33,10 @@ import kotlinx.coroutines.yield import org.jline.jansi.Ansi import org.jline.jansi.Ansi.Color.RED import org.slf4j.LoggerFactory -import packetproxy.gui.GUILog +import packetproxy.platform.LogSinks object Logging { private val dtf: DateTimeFormatter = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss") - private val guiLog: GUILog = GUILog.getInstance() private val logger = LoggerFactory.getLogger("") private var isGulp: Boolean = false @@ -101,8 +100,9 @@ object Logging { // WARN未満は出力されないためwarnで出力する logger.warn(fs) - if (isGulp) return - guiLog.append(fs) + if (!isGulp) { + LogSinks.append(fs) + } } @JvmStatic @@ -111,8 +111,9 @@ object Logging { val fs = formatString(format, *args) logger.error(Ansi.ansi().fg(RED).a(fs).reset().toString()) - if (isGulp) return - guiLog.appendErr(fs) + if (!isGulp) { + LogSinks.appendErr(fs) + } } /** 別のログが挟まらないように一塊にした上で1度に出力する */