From 74bf67a9b2a0845af49b6df98f5e2acb519a1b67 Mon Sep 17 00:00:00 2001 From: Gyubin Han Date: Thu, 1 Jan 2026 02:55:28 +0900 Subject: [PATCH] =?UTF-8?q?Feat:=20=ED=8C=8C=EC=9D=BC=20=EC=A0=84=EC=86=A1?= =?UTF-8?q?=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84=20(=EC=97=85?= =?UTF-8?q?=EB=A1=9C=EB=93=9C/=EB=8B=A4=EC=9A=B4=EB=A1=9C=EB=93=9C)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - RETR 명령어 구현: 파일 다운로드 * 파일 존재 여부 확인 * 데이터 연결을 통한 파일 스트림 전송 * 전송 바이트 수 로깅 * 에러 처리 및 연결 자동 종료 - STOR 명령어 구현: 파일 업로드 * 현재 디렉토리에 파일 생성 * 데이터 연결을 통한 파일 스트림 수신 * 전송 완료 후 파일 크기 확인 * 에러 처리 및 연결 자동 종료 - 두 명령어 모두 PASV 선행 필수 - FileInputStream/FileOutputStream을 사용한 스트림 처리 - 전송 성공/실패에 따른 적절한 응답 코드 반환 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 --- .../be/gyu/android/server/ftp/FTPSession.java | 104 ++++++++++++++++++ 1 file changed, 104 insertions(+) diff --git a/app/src/main/java/be/gyu/android/server/ftp/FTPSession.java b/app/src/main/java/be/gyu/android/server/ftp/FTPSession.java index 5729f64..b725115 100644 --- a/app/src/main/java/be/gyu/android/server/ftp/FTPSession.java +++ b/app/src/main/java/be/gyu/android/server/ftp/FTPSession.java @@ -107,6 +107,12 @@ public class FTPSession implements Runnable { case "PASV": handlePasv(); break; + case "RETR": + handleRetr(argument); + break; + case "STOR": + handleStor(argument); + break; case "NOOP": handleNoop(); break; @@ -369,6 +375,104 @@ public class FTPSession implements Runnable { } } + private void handleRetr(String fileName) throws IOException { + if (!isAuthenticated) { + sendResponse(FTPResponse.NOT_LOGGED_IN, "Please login first"); + return; + } + + if (fileName.isEmpty()) { + sendResponse(FTPResponse.SYNTAX_ERROR_PARAMETERS, "No file name specified"); + return; + } + + if (dataConnection == null) { + sendResponse(FTPResponse.CANNOT_OPEN_DATA_CONNECTION, "Use PASV first"); + return; + } + + java.io.File file = fileSystem.getFile(fileName); + if (file == null || !file.exists() || !file.isFile()) { + sendResponse(FTPResponse.FILE_UNAVAILABLE, "File not found"); + dataConnection.close(); + dataConnection = null; + return; + } + + sendResponse(FTPResponse.FILE_STATUS_OK, "Opening data connection for " + fileName + " (" + file.length() + " bytes)"); + + if (dataConnection.acceptConnection()) { + try { + java.io.FileInputStream fis = new java.io.FileInputStream(file); + if (dataConnection.transferStream(fis)) { + fis.close(); + dataConnection.close(); + sendResponse(FTPResponse.CLOSING_DATA_CONNECTION, "Transfer complete"); + Log.i(TAG, "File sent: " + fileName + " (" + file.length() + " bytes)"); + } else { + fis.close(); + dataConnection.close(); + sendResponse(FTPResponse.CONNECTION_CLOSED, "Transfer failed"); + } + } catch (Exception e) { + Log.e(TAG, "Error sending file: " + e.getMessage()); + dataConnection.close(); + sendResponse(FTPResponse.CONNECTION_CLOSED, "Transfer error: " + e.getMessage()); + } + } else { + dataConnection.close(); + sendResponse(FTPResponse.CANNOT_OPEN_DATA_CONNECTION, "Cannot open data connection"); + } + + dataConnection = null; + } + + private void handleStor(String fileName) throws IOException { + if (!isAuthenticated) { + sendResponse(FTPResponse.NOT_LOGGED_IN, "Please login first"); + return; + } + + if (fileName.isEmpty()) { + sendResponse(FTPResponse.SYNTAX_ERROR_PARAMETERS, "No file name specified"); + return; + } + + if (dataConnection == null) { + sendResponse(FTPResponse.CANNOT_OPEN_DATA_CONNECTION, "Use PASV first"); + return; + } + + java.io.File file = new java.io.File(fileSystem.getCurrentDirectory(), fileName); + + sendResponse(FTPResponse.FILE_STATUS_OK, "Opening data connection for " + fileName); + + if (dataConnection.acceptConnection()) { + try { + java.io.FileOutputStream fos = new java.io.FileOutputStream(file); + if (dataConnection.receiveStream(fos)) { + fos.close(); + dataConnection.close(); + sendResponse(FTPResponse.CLOSING_DATA_CONNECTION, "Transfer complete"); + Log.i(TAG, "File received: " + fileName + " (" + file.length() + " bytes)"); + } else { + fos.close(); + dataConnection.close(); + sendResponse(FTPResponse.CONNECTION_CLOSED, "Transfer failed"); + } + } catch (Exception e) { + Log.e(TAG, "Error receiving file: " + e.getMessage()); + dataConnection.close(); + sendResponse(FTPResponse.CONNECTION_CLOSED, "Transfer error: " + e.getMessage()); + } + } else { + dataConnection.close(); + sendResponse(FTPResponse.CANNOT_OPEN_DATA_CONNECTION, "Cannot open data connection"); + } + + dataConnection = null; + } + private void handleNoop() throws IOException { sendResponse(FTPResponse.COMMAND_OK, "OK"); }