/* * Copyright 1999-2006 University of Chicago * * 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 org.dcache.ftp.client.vanilla; import org.dcache.ftp.client.exception.FTPReplyParseException; import java.io.Serializable; import java.io.EOFException; import java.io.IOException; import java.io.BufferedReader; import org.slf4j.Logger; import org.slf4j.LoggerFactory; // FTPReplyParseException - move line to exception // internationalize exceptions /** * <p> * Represents the FTP reply. * </p> */ public class Reply implements Serializable { private static final long serialVersionUID = 3736894002484160637L; private static final Logger logger = LoggerFactory.getLogger(Reply.class); public static final int POSITIVE_PRELIMINARY = 1; public static final int POSITIVE_COMPLETION = 2; public static final int POSITIVE_INTERMEDIATE = 3; public static final int TRANSIENT_NEGATIVE_COMPLETION = 4; public static final int PERMANENT_NEGATIVE_COMPLETION = 5; //minimum length of 1st line: //message is defined as 3 chars + <sp> + <text> + <CRLF> //so if text is empty, minimum 1st line length = 4 private static final int MIN_FIRST_LEN = 4; // instance members protected String message; protected int code; protected int category; protected boolean isMultiline; // for subclassing protected Reply() { } /** * @throws EOFException on end of stream * @throws IOException on I/O problem * @throws FTPReplyParseException if cannot parse **/ public Reply(BufferedReader input) throws FTPReplyParseException, EOFException, IOException { logger.debug("read 1st line"); String line = input.readLine(); if (logger.isDebugEnabled()) { logger.debug("1st line: " + line); } //end of stream if (line == null) { throw new EOFException(); } //for compatibility with GT2.0 wuftp server which is incorrectly inserting \0 between lines line = ignoreLeading0(line); if (line.length() < MIN_FIRST_LEN) { throw new FTPReplyParseException( FTPReplyParseException.STRING_TOO_SHORT, "Minimum 1st line length = " + MIN_FIRST_LEN + ". Here's the incorrect 1st line ->" + line + "<-"); } // code String codeString = line.substring(0, 3); try { code = Integer.parseInt(codeString); } catch (NumberFormatException e) { throw new FTPReplyParseException( FTPReplyParseException.FIRST_3_CHARS, "Here's the incorrect line ->" + line + "<-" + "and the first 3 chars ->" + codeString + "<-" ); } // category category = line.charAt(0) - '0'; // message char char4 = line.charAt(3); //do not include 4th char in message message = line.substring(4, line.length()); if (char4 == ' ') { //single line reply isMultiline = false; } else if (char4 == '-') { //multi - line reply isMultiline = true; String lastLineStarts = codeString + ' '; //platform dependent line separator String lineSeparator = System.getProperty("line.separator"); if (logger.isDebugEnabled()) { logger.debug( "multiline reply; last line should start with ->" + lastLineStarts + "<-"); logger.debug("lenght of line.separator on this OS: " + lineSeparator.length()); } StringBuilder buf = new StringBuilder(message); for (; ; ) { logger.debug("read line"); line = input.readLine(); //end of stream if (line == null) { throw new EOFException(); } //for compatibility with GT2.0 wuftp server //which is incorrectly inserting \0 between lines line = ignoreLeading0(line); if (logger.isDebugEnabled()) { logger.debug("line : ->" + line + "<-"); } buf.append(lineSeparator).append(line); if (line.startsWith(lastLineStarts)) { logger.debug("end reached"); break; } } message = buf.toString(); } else { throw new FTPReplyParseException( FTPReplyParseException.UNEXPECTED_4TH_CHAR, "Here's the incorrect line ->" + line + "<-"); } } /** * @return the first digit of the reply code. */ public int getCategory() { return category; } /** * @return the reply code */ public int getCode() { return code; } public boolean isMultiline() { return isMultiline; } /** * <p> * Returns the text that came with the reply, between the leading space and * terminating CRLF, excluding the mentioned space and CRLF. * </p> * <p> * If the reply is multi-line, this returns the text between the leading * dash "-" and the CRLF following the last line, excluding the mentioned * dash and CRLF. Note that lines are separated by the local line separator * [as returned by System.getProperty("line.separator")] rather than CRLF. * <p> * </p> * <p> * <p> * </p> */ public String getMessage() { return message; } public static boolean isPositivePreliminary(Reply reply) { return (reply.getCategory() == POSITIVE_PRELIMINARY); } public static boolean isPositiveCompletion(Reply reply) { return (reply.getCategory() == POSITIVE_COMPLETION); } public static boolean isPositiveIntermediate(Reply reply) { return (reply.getCategory() == POSITIVE_INTERMEDIATE); } public static boolean isTransientNegativeCompletion(Reply reply) { return (reply.getCategory() == TRANSIENT_NEGATIVE_COMPLETION); } public static boolean isPermanentNegativeCompletion(Reply reply) { return (reply.getCategory() == PERMANENT_NEGATIVE_COMPLETION); } public String toString() { String mult = isMultiline ? "-" : " "; return code + mult + message; } /** * GT2.0 wuftp server incorrectly inserts \0 between lines. We have to deal with that. **/ protected static String ignoreLeading0(String line) { if (line.length() > 0 && line.charAt(0) == 0) { logger.debug("WARNING: The first character of the reply is 0. Ignoring the character."); /* logger.debug( "\n\nWARNING:\n In the reply received from the server, the first character's code is 0! I will ignore it but this means the server is not following the protocol. Here's the details: \n first line of the reply ->" + line + "<-"); logger.debug( "First 3 chars of reply->" +line.substring(0,3)+"<-"); logger.debug( "char 0 ->" + line.charAt(0) + "<- code = " + (int)line.charAt(0)); logger.debug( "char 1 ->" + line.charAt(1) + "<- code = " + (int)line.charAt(1)); logger.debug( "char 2 ->" + line.charAt(2) + "<- code = " + (int)line.charAt(2)); logger.debug( "char 3 ->" + line.charAt(3) + "<- code = " + (int)line.charAt(3)); */ return line.substring(1, line.length()); } return line; } } // end Reply