/* 
 * Copyright (C) 1999-2004 Joachim Wieland <joe@mcknight.de>
 * 
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2 of the License, or (at your
 * option) any later version.
 * 
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 59 Temple Place - Suite 330, Boston, MA 02111, USA.
 */

#include "jftpgw.h"
#include <signal.h>
#include <ctype.h>

/* reads a single line from fd and returns a pointer to a malloc()ed 
 * char array.
 *
 * Parameters: fd: The file descriptor to read from
 *
 * Return values: (char*) 0 on error, a pointer to a malloc()ed char array
 *                that contains the read data on success
 *
 * Called by: various functions
 */

extern int timeout;
static char *readline_check(int, int);

char* readline(int fd) {
	/* do not check for valid FTP responses */
	return readline_check(fd, 0);
}

char* ftp_readline(int fd) {
	/* check for valid FTP responses */
	return readline_check(fd, 1);
}

static char *readline_check(int fd, int check_ftp_format) {
	const int MAXSIZE = 3;
	int n, ret, length = 0;
	int linecnt;
	int data_read = 0;
	char *temp, *buffer;
	fd_set readset;
	struct timeval rtimeout;
	sigset_t sigset, oldset;
	rtimeout.tv_sec = config_get_ioption("commandtimeout", 300);
	rtimeout.tv_usec = 0;

	FD_ZERO(&readset);
	FD_SET(fd, &readset);

	timeout = 0;

	sigemptyset(&sigset);
	sigemptyset(&oldset);
	sigaddset(&sigset, SIGCHLD);
	ret = sigprocmask(SIG_BLOCK, &sigset, &oldset);
	if (ret < 0) {
		jlog(3, "sigprocmask() error: %s", strerror(errno));
	}
	if ((ret = select(fd + 1, &readset, NULL, NULL, &rtimeout)) <= 0) {
		if (ret == 0) {
			timeout = 1;
		}
		/* save the errno value from sigprocmask() */
		n = errno;
		ret = sigprocmask(SIG_UNBLOCK, &sigset, &oldset);
		if (ret < 0) {
			jlog(3, "sigprocmask() error releasing the blocked"
				" signals: %s", strerror(errno));
		}
		errno = n;
		return 0;
	}
	ret = sigprocmask(SIG_UNBLOCK, &sigset, &oldset);
	if (ret < 0) {
		jlog(3, "sigprocmask() error releasing the blocked"
			" signals: %s", strerror(errno));
	}
	
	if (fd < 0) {
		jlog(1, "readline_check: Not connected");
		return 0;
	}

	buffer = (char *)malloc(MAXSIZE);
	enough_mem(buffer);

	temp = buffer;

	linecnt = 0;
	while ((n = read(fd, temp, 1)) > 0) {
		data_read = 1;
		if (*temp == '\r') { linecnt++; continue; }
		if (*temp == '\n') { linecnt++; break; }
		if (*temp == '\0') break;
		/* check for a valid FTP response. It must start with either
		 * xxx <text> (mind the space)
		 * or
		 * xxx-<text> (with a dash)
		 */
		length++;
		if (check_ftp_format && linecnt == 0
			&& ((length <= 3 && !isdigit((int) *temp))
				|| (length == 4 && *temp != ' '
						&& *temp != '-'))) {

				buffer[length] = 0;
				jlog(4, "malformed FTP response: %s",
					buffer);
				jlog(9, "in line: %d", linecnt);
				set_errstr("malformed FTP response");
				return 0;
		}
		if ((length+1) % MAXSIZE == 0) {
			buffer = (char*) realloc(buffer, length + 1 + MAXSIZE);
			enough_mem(buffer);
			temp = buffer + length - 1;
		}
		temp++;
	}
	if (n < 0 || (n == 0 && data_read == 0)) {
		if (n < 0) {
			set_errstr(strerror(errno));
			jlog(2, "Error reading: %s", strerror(errno));
		}
		free(buffer);
		return 0;
	}
	buffer[length] = '\0';
	if (my_strcasestr(buffer, "PASS ") == (char*) 0) {
		jlog(9, "Read (%d): %s", fd, buffer);
	} else {
		jlog(9, "Read (%d): ***hidden***", fd);
	}
	return buffer;
}


/* reads from fd until it has a "xxx data" response and returns the
 * response code xxx.
 * If **data != 0, *data is set to the address of the malloc()ed array that
 * contains the read data.
 *
 * Parameters: fd: The file descriptor to read from
 *             data: A pointer to a pointer that should be set to the address
 *                   of the read data.
 *
 * Return value: 0 on error
 *               the response code on success
 * Called by: checkforabort()
 *
 */

int ftp_getrc(int fd, char **data) {
	char *line;
	char *tmp =0;
	int response;

	while ((line = ftp_readline(fd))) {
		if (line[0] < '0' || line[0] > '9') {
			free(line);
			continue;
		}
		if (line[3] == ' ') {
			tmp = strdup(line);
			enough_mem(tmp);
			break;
		}
		/* free the line if we continue in the loop */
		free(line);
	}
	if (!line || !*line) return 0;
	sscanf(line, "%d ", &response);
	if (data) {
		*data = tmp;
	} else {
		free(tmp);
	}
	/* free the line that was not freed within the whlie loop because of
	 * a break statement */
	free(line);
	tmp =0;
	if (response >= 100)
		return response;
	return 0;
}

/* readall() reads from an fd and returnes the whole data in a structure
 * message.
 * 
 * Parameters: sourcefd: The file descriptor to read from
 * 
 * Return values: A struct message with
 *                  - both char* arrays in it set to NULL on error
 *                  - one char* array pointing to the whole message and the
 *                    other one pointing to the last line of it, starting with
 *                    the response code.
 * 
 * Called by: login() to get the whole welcome message and to analyze
 *            the authentication message
 *
 */

struct message readall(int sourcefd) {
	char* line =0, *buf =0, *tmp =0, *last =0, *linestart =0;
	struct message ret;
	size_t tmpsize;

	while ((line = readline(sourcefd))) {
		if (buf) {
			tmpsize = strlen(buf) + strlen(line) + 4;
			tmp = (char*) malloc(tmpsize);
			enough_mem(tmp);
			strncpy(tmp, buf, tmpsize);
			last = tmp + strlen(buf);
			free(buf);
			buf = 0;
		} else {
			tmpsize = strlen(line) + 4;
			tmp = (char*) malloc(tmpsize);
			enough_mem(tmp);
			tmp[0] = '\0';
			last = tmp;
		}
		linestart = tmp + strlen(tmp);
		scnprintf(tmp, tmpsize, "%s\r\n", line);
		buf = tmp;
		tmp =0;
		if (line[0] < '0' || line[0] > '9') {
			free(line);
			continue;
		}
		if (line[3] == ' ') {
			break;
		}
		free(line);
		line = (char*) 0;
	}
	if (!line) {
		free(buf);
		ret.fullmsg = (char*) 0;
		ret.lastmsg = (char*) 0;
		return ret;
	} else {
		free(line);
	}
	ret.fullmsg = buf;
	jlog(9, "Readall (%d): %s", sourcefd, buf);
	ret.lastmsg = last;
	return ret;
}

/* passall() reads from sourcefd until there is no data left to read and writes
 * everything to targetfd
 *
 * Parameters: sourcefd: The file descriptor to read from
 *             targetfd: The file descriptor to write to
 *
 * Return value: 0 on error
 *               a pointer that contains the malloc()ed char array with the
 *               passed data on success
 *
 * Called by: handlecmds() to pass the message sent after a QUIT
 *            passcmd() to pass the control connection when passing a command
 *            passcmd() to pass the control connection after having transmit
 *                      the file 
 */

char* passall(int sourcefd, int targetfd) {
	char* line =0, *sendbuf =0;
	size_t sendbufsize;

	while ((line = readline(sourcefd))) {
		sendbufsize = strlen(line) + 3;
		sendbuf = (char*) malloc(sendbufsize);
		enough_mem(sendbuf);
		snprintf(sendbuf, sendbufsize, "%s\r\n", line);
		free(line);
		say(targetfd, sendbuf);

		if (strlen(sendbuf) > 4
			&& isdigit((int)sendbuf[0])
			&& isdigit((int)sendbuf[1])
			&& isdigit((int)sendbuf[2])
			&& sendbuf[3] == ' ') {

			/* the loop is left here */
			break;
		} else {
			free(sendbuf);
			sendbuf = 0;
		}
	}
	return sendbuf;
}



syntax highlighted by Code2HTML, v. 0.9.1