protected function _readMsg($function = "_readMsg") {
		if(!$this->connected) {
			$this->pushError($function, 'Connect first');
			return false;
		}
		$result = true;
		$this->message = '';
		$this->code = 0;
		$go = true;
		do {
			$tmp = @socket_read($this->ftp_control_sock, 4096, PHP_BINARY_READ);
			if($tmp === false) {
				$go = false;
				$result = false;
				$socketErr = socket_strerror(socket_last_error($this->ftp_control_sock));
				$this->pushError($function, 'Read failed', $socketErr);
				$regs = array(0, 0);
			}
			else {
				$this->message .= $tmp;
				$go = !preg_match(
					"/^([0-9]{3})(-.+\\1)? [^".self::CRLF."]+".self::CRLF."$/Us",
					$this->message,
					$regs
				);
			}
		} while($go);
		if($this->LocalEcho) {
			echo "GET < ".Strings::trimLineBreaks($this->message).self::CRLF;
		}
		$this->code = (int) $regs[1];
		return $result;
	}
	protected function _readMsg($function = "_readmsg") {
		if(!$this->connected) {
			$this->pushError($function, 'Connect first');
			return false;
		}
		if (!is_array($this->_ftp_data_sock)) {
			$this->pushError($function, 'No data retrieved');
			return false;
		}
		$result = true;
		$this->message = implode(self::CRLF, $this->ftp_data_sock).self::CRLF;
		$this->code = 0;
		$m = preg_match(
			"/^([0-9]{3})(-(.*[".self::CRLF."]{1,2})+\\1)? [^".self::CRLF."]+[".self::CRLF."]{1,2}$/m",
			$this->_message,
			$regs
		);
		if(!$m) {
			$this->pushError($function, 'Invalid response from FTP');
			return false;
		}
		if($this->LocalEcho) {
			echo "GET < ".Strings::trimLineBreaks($this->message).self::CRLF;
		}
		$this->code = (int) $regs[1];
		return $result;
	}
	/**
	 * Reads a file (in three different ways).
	 *
	 * 1. If the first parameter is 0 (File::READ_STRING, default) the complete file will be
	 * returned at once as string. The function returns the content of the file as string, if the
	 * file does not exist or another error occured, boolean false will be returned. This is similar
	 * to PHP's file_get_contents() function.
	 *
	 * 2. If the first parameter is -1 (File::READ_LINES) or -2 (File::READ_LINES_FILLED), the
	 * function returns the file as an array. Each element of the array corresponds to a line in the
	 * file. The newline and/or carriage return chars will be removed from the line endings. If you
	 * use the option -2 (File::READ_LINES_FILLED) only lines with content will be returned, all
	 * empty lines will be skipped. The function returns the content of the file as array, if the
	 * file does not exist or another error occured, boolean false will be returned. Both options
	 * are similar to PHP's file() function.
	 *
	 * 3. If the first parameter is greater than zero (> 0), the function reads up to the specified
	 * amount of bytes from the file pointer opened before. When the end of file (EOF) is reached
	 * the function returns null, if the (partial) reading process was successful the function
	 * returns true, not the content. On failure false will be returned. The content is "returned"
	 * as the third parameter of this function.
	 *
	 * The third parameter of this fucntion is only used when the first parameter is greater than
	 * zero (see the third way to read a file).
	 *
	 * Warning: This function may return Boolean FALSE, but may also return a non-Boolean value
	 * which evaluates to FALSE, such as 0 or "". Use the === operator for testing the return value
	 * of this function.
	 *
	 * The second parameter is the mode of reading (Binary or Unicode). You have to use one of the
	 * class constants File::BINARY or File::UNICODE (default).
	 *
	 * Examples:
	 * <code>
	 *	$f = new File('file.txt');
	 *	while ($f->read(1024, File::UNICODE, $data) === true) {
	 *		echo $data;
	 *	}
	 * </code>
	 * <code>
	 *	$f = new File('blank.gif');
	 *	$content = $f->read(File::READ_STRING, File::BINARY);
	 *  if ($content !== false) {
	 *    echo $content;
	 *  }
	 * </code>
	 * <code>
	 *	$f = new File('core.log');
	 *	$content = $f->read(File::READ_LINES_FILLED);
	 *  if ($content !== false) {
	 *    print_r($content);
	 *  }
	 * </code>
	 *
	 * @param int Type to read the file, default: 0 (File::READ_STRING)
	 * @param int Mode to read a file: Unicode (default) or Binary
	 * @param string Only used when first parameter is > 0, contains partial content of the file.
	 * @return mixed Requested Content or false (or only the bool state when first param is > 0)
	 * @todo Check whether $fopenMode is really correct for php 6 binary/unicode handling
	 */
	public function read($type = File::READ_STRING, $mode = self::UNICODE, &$data = null) {
		if ($this->readable() == false) {
			return false;
		}

		if ($mode == self::BINARY) {
			$mode = FILE_BINARY;
			$fopenMode = 'rb';
		}
		else {
			$mode = FILE_TEXT;
			$fopenMode = 'r';
		}

		if ($type == self::READ_STRING) {
			$contents = file_get_contents($this->path, $mode);
			if ($contents !== false) {
				return $contents;
			}
			else {
				return false;
			}
		}
		elseif ($type == self::READ_LINES || $type == self::READ_LINES_FILLED) {
			// When filesize is > 8 MB we use another method to read the file into an array.
			if ($this->size() <= 8*1024*1024) {
				if ($type == self::READ_LINES_FILLED) {
					$flags = FILE_SKIP_EMPTY_LINES | $mode;
				}
				else {
					$flags = $mode;
				}
				$array = file($this->path, $flags);
			}
			else {
				$array = array();
				$this->handle = fopen($this->path, $fopenMode);
				if (is_resource($this->handle) == false) {
					return false;
				}
				while (feof($this->handle) == false) {
					$line = fgets($this->handle);
					$line = Strings::trimLineBreaks($line);
					if ($type == self::READ_LINES || !empty($line)) {
						$array[] = $line;
					}
				}
				fclose($this->handle);
			}
			// Remove line breaks
			$array = array_map(array('Strings', 'trimLineBreaks'), $array);
		}
		elseif ($type > 0) {
			if (is_resource($this->handle) == false) {
				$this->handle = fopen($this->path, $fopenMode);
				if (is_resource($this->handle) == false) {
					return false;
				}
			}
			if (feof($this->handle) == false) {
				$data = fread($this->handle, $type);
				if ($data !== false) {
					return true;
				}
				else {
					fclose($this->handle);
					return false;
				}
			}
			else {
				// Reached end of file (EOF)
				fclose($this->handle);
				return null;
			}
		}
		else {
			FileSystem::getDebug()->addText(
				"The specified type ({$type}) to read the file '{$this->path}' is not supported."
			);
			return false;
		}
	}
	/**
	 * @dataProvider providerTrimLineBreaks
	 */
	public function testTrimLineBreaks($param1, $param2, $expected) {
		$result = Strings::trimLineBreaks($param1, $param2);
		$function = ($param2 == true) ? 'rtrim' : 'trim';
		$this->assertEquals($expected, $result, "Failed to {$function}: ".var_export($param1, true));
	}
	protected function _readMsg($function = "_readmsg") {
		if(!$this->connected) {
			$this->pushError($function, 'Connect first');
			return false;
		}
		$result = true;
		$this->message = '';
		$this->code = 0;
		$go = true;
		do {
			$tmp = @fgets($this->ftp_control_sock, 512);
			if($tmp === false) {
				$go = false;
				$result = false;
				$this->pushError($function, 'Read failed');
			}
			else {
				$this->message .= $tmp;
				$m = preg_match(
					"/^([0-9]{3})(-(.*[".self::CRLF."]{1,2})+\\1)? [^".self::CRLF."]+[".self::CRLF."]{1,2}$/",
					$this->message,
					$regs
				);
				if($m) {
					$go = false;
				}
			}
		} while($go);
		if($this->LocalEcho) {
			echo "GET < ".Strings::trimLineBreaks($this->message).self::CRLF;
		}
		$this->code = (int) $regs[1];
		return $result;
	}