retrieveFile method

Future<void> retrieveFile(
  1. String filename
)

Implementation

Future<void> retrieveFile(String filename) async {
  if (filename.isEmpty) {
    sendResponse('501 Syntax error in parameters');
    return;
  }

  // Check file existence before opening data connection to avoid
  // sending 550 after 150 (RFC 959: validate before preliminary reply)
  if (!fileOperations.exists(filename)) {
    sendResponse('550 File not found');
    return;
  }
  String fullPath = fileOperations.resolvePath(filename);
  File file = File(fullPath);
  if (!await file.exists()) {
    sendResponse('550 File not found');
    return;
  }

  if (!checkDataProtection()) return;

  if (!await openDataConnection()) {
    return;
  }

  try {
    transferInProgress = true;

    if (await file.exists()) {
      Stream<List<int>> fileStream = file.openRead();

      StreamSubscription<List<int>>? subscription;
      subscription = fileStream.listen(
        (data) {
          if (transferInProgress && dataSocket != null) {
            try {
              dataSocket!.add(data);
            } catch (e) {
              logger.generalLog('Error writing to data socket: $e');
              transferInProgress = false;
              subscription?.cancel();
              _closeDataSocket();
            }
          }
        },
        onDone: () async {
          if (transferInProgress) {
            transferInProgress = false;
            await _closeDataSocket();
            sendResponse('226 Transfer complete');
          }
        },
        onError: (error) async {
          logger.generalLog('Error reading from file: $error');
          if (transferInProgress) {
            sendResponse('426 Connection closed; transfer aborted');
            transferInProgress = false;
            await _closeDataSocket();
          }
        },
        cancelOnError: true,
      );
    }
  } catch (e) {
    logger.generalLog('Exception in retrieveFile: $e');
    sendResponse('550 File transfer failed');
    transferInProgress = false;
    await _closeDataSocket();
  }
}