/*      Get and Resume Elite EDition source code


Get and Resume Elite EDition (GREED)
Copyright (C) 1999  Anoakie Turner

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., 675 Mass Ave, Cambridge, MA 02139, USA.

For more information on the GPL, please go to:
http://www.gnu.org/copyleft/gpl.html


        Contact:  Anoakie Turner
                  Anoakie.Turner@asu.edu

                  13240 N. 94th Pl.
                  Scottsdale, AZ 85260
*/

#include "main.h"
#ifdef __riscos__
#include "riscos.h"
#endif /* __riscos__ */


void RecurseFTP(char*, char*, int);

int ReadSock (int sockfd, char* buffer, char* stopstr)
/*************************************
**	ReadSock(int, char*, char*)
**
** PRE:	 Assigned(sockfd, buffer, stopstr)
** POST: reads data from socket(sockfd) until either the
**	 stopstr is hit or sockfd reaches the EOF
*************************************/
{       int len = 1;
        int result = 0;
	struct timeval timeout; /* = { TIMEOUT_SEC, 0 }; .8+S patch */
	timeout.tv_sec = TIMEOUT_SEC;
	timeout.tv_usec = 0;

        do
        {	switch(select(FD_SETSIZE, &testfds, (fd_set *)0, (fd_set *)0, &timeout))
		{	case 0:
				if (OUTPUT_LEVEL > 1)
					printf("\ntimeout!\n");
				return (0);
				break;
			case -1:
				printf("Big socket error or something!\n");
				return (0);
				break;
			default:
				result = read(sockfd, buffer + len - 1, sizeof(buffer) + len - 1);
		                len += result;
				buffer[len] = '\0';
				break;
		}
		timeout.tv_sec = TIMEOUT_SEC;
        } while (result > 0 && strstr(buffer, stopstr) == NULL);
	if (OUTPUT_LEVEL > 8)
        	printf("%s\n\n", buffer);

	return(1);
}


int ReadPASVPort(char* buffer)
/*************************************
**	ReadPASVPort(char*)
**
** PRE:  Assigned(buffer)
** POST: Returns the first specified valid passive port in (buffer)
*************************************/
{	char *rbuffer = strstr(buffer, "Passive Mode (");
	char lhost[6][4];
	int i = 0, j = 0, k = 0;


	/* If Passive Mode was not found in the buffer,
		we have to assume the ftp server doesn't use
		PASV mode, therefore it fails
		NOTE: Server could aso be busy! */
	if (rbuffer == NULL)
	{	if (OUTPUT_LEVEL > 0)
			printf("PASV mode failed");
		return(0);
	} else
	{	for (i = 14; (unsigned)i < strlen(rbuffer) && rbuffer[i] != ')'; i++)
			if (rbuffer[i] == ',')
			{	lhost[j][k] = '\0';
				j++;
				k = 0;
			} else
		lhost[j][k++] = rbuffer[i];

		return (atoi(lhost[4])*256 + atoi(lhost[5]));
	}
}


void GetFileListing (URLp URL)
{
}


bool ReadFTPListing (URLp URL)
/*************************************
**	bool ReadFTPListing
**
** PRE:  URL has been Parse()'d and Connect()'ed
** POST: Sets URL->sockfd up for reading and determines the files size.
*************************************/
{	int i = 0, j = 0, k = 0, listfd = 0, datafd = 0, noretry = 0;
	unsigned int tempport = 0;
	int whitespace = 0;
	char *buffer;
	struct sockaddr_in datastream, liststream;
#ifdef __riscos__
        char *temp, *ext;
#endif /* __riscos__ */


	URL->size = malloc(15);
	NOSIZE = 0;
	buffer = malloc(64000);


	/* Reads from the FTP socket until 200 (ok) is reached	*/
	i = ReadSock(URL->sockfd, buffer, "\n200");

	/* If a 501 is found, then the FTP site DOESN'T support resuming */
	if (strstr(buffer, "\n501"))
		noretry = 1;
	listfd = socket(AF_INET, SOCK_STREAM, 0);
	tempport = ReadPASVPort(buffer);

	/* If we can't find a valid port to connect to, or can't open
		a socket to read the file listing, then an error occurs */
	if (tempport == 0 || listfd == -1 || !i)
	{	URL->retry = 1;

		/* Server returns 530 if busy */
		if (strstr(buffer, "\n530") != NULL || strstr(buffer, "\n421") != NULL)
		{	if (OUTPUT_LEVEL > 0)
#ifdef USE_GTK
				fprintf(out, "%d:mb:Error#Server is busy\n", URL->pid);
#else
				printf(" because server is busy");
#endif
		}
		else
			URL->retry = 0;
		write(URL->sockfd, "\r\nQUIT\r\n", 8);
		close(URL->sockfd);
		close(listfd);
#ifndef USE_GTK
		if (OUTPUT_LEVEL > 0)
			printf("!\n");
#endif

		if (OUTPUT_LEVEL > 0 && listfd == -1)
#ifdef USE_GTK
			fprintf(out, "%d:mb:Error#Error opening socket to FTP server for LIST\n", URL->pid);
#else
			printf("Error opening socket to FTP server for LIST!\n\n");
#endif

		return(0);
	}

	liststream.sin_family = AF_INET;
	liststream.sin_port = htons(tempport);
	memcpy(&liststream.sin_addr, URL->host->h_addr, URL->host->h_length);

        if (OUTPUT_LEVEL > 0)
#ifdef USE_GTK
                fprintf(out, "%d:lb:Connecting to port %d for file listing.\n",
			URL->pid, tempport);
#else
                printf ("[ CONNECTING TO PORT %d FOR FILE LISTING ]\n\n", tempport);
#endif

	/* Attempts to connect to socket for list of files */
	if (connect(listfd, (struct sockaddr *)&liststream, sizeof(liststream)) == -1)
	{	if (OUTPUT_LEVEL > 0)
#ifdef USE_GTK
			fprintf(out, "%d:mb:Error#Error connecting to FTP socket for LIST!\n",
				URL->pid);
#else
			printf("Error connecting to FTP socket for LIST!\n");
#endif
		write(URL->sockfd, "\r\nQUIT\r\n", 8);
		close(listfd);
		return(0);
	}

	/* Reads the socket for the file listing */
	if(!ReadSock(listfd, buffer, "\r\n\r\n"))
	{	URL->retry = 1;
		write(URL->sockfd, "\r\nQUIT\r\n", 8);
		close(listfd);
		return(0);
	}

	close(listfd);

	if (strlen(buffer) < 7 || strstr(buffer, "No such file or directory") != NULL)
		/* file not found */
	{	if (OUTPUT_LEVEL > 0)
#ifdef USE_GTK
			fprintf(out, "%d:mb:Error#No file or files found!\n", URL->pid);
#else
			printf("Error:  No file or files found!\n");
#endif
		write(URL->sockfd, "\r\nQUIT\r\n", 8);
		close(listfd);
		URL->retry = 0;
		return(0);
	}


	if (buffer[0] != '-' && buffer[0] != 'd' && buffer[0] != 'l')
		buffer = strchr(buffer, '\n') + 1;

	if (((URL->filename == NULL || strstr(buffer, "\nd") || buffer[0] == 'd')
		&& URL->recurse >= 0) || buffer[0] == 'l')
	{	RecurseFTP (URL->name, buffer, URL->recurse);
		URL->done = 1;
		URL->retry = 0;
		sleep(3);
		return (0);
	} else if ((strstr(buffer, "\nd") != NULL || buffer[0] == 'd') && URL->recurse < 0)
	{	if (OUTPUT_LEVEL > 0)
#ifdef USE_GTK
			fprintf(out, "%d:lb:Directory hit... Ignoring!!\n", URL->pid);
#else
                        printf("Directory hit... Ignoring!\n");
#endif
                write(URL->sockfd, "\r\nQUIT\r\n", 8);
                close(listfd);
                URL->retry = 0;
                return(0);
        }



	/* queues the variable 'j' to the start of the filesize */
	/* Sample: "-rwx-wx--x 1 root wheel 1024 /etc/passwd" */
	while ((unsigned)j < strlen(buffer) && whitespace < 4)
	{	if (buffer[j] == ' ' && buffer[j + 1] != ' ')
			whitespace ++;
		j++;
	}


	k = j;
	/* Calculates the size of the string being read in */
	for (i = 0; (unsigned)j < strlen(buffer); j++)
		if (buffer[j] == ' ' || (buffer[j] > ('0' - 1) && buffer[j] < ('9' + 1)))
			i++;
		else
			j = strlen(buffer);

	/* Allocates space for the string */
	URL->total = malloc(i + 1);

	j = k;
	k = 0;
	/* reads the first set of numbers it finds into the string URL->total */
	for (i = 0; (unsigned)j < strlen(buffer); j++)
		if (buffer[j] == ' ' || (buffer[j] > ('0' - 1) && buffer[j] < ('9' + 1)))
		{	URL->total[i++] = buffer[j];
			if (buffer[j] != ' ')
				k = 1;
		}
		else
			j = strlen(buffer);


	URL->total[j - 4] = '\0';
	URL->ltotal = atol(URL->total);

	if (OUTPUT_LEVEL > 1)
	{
#ifndef USE_GTK
		printf("# File: %s\n",URL->filename);
#endif
		if (!isdigit(URL->total[0]))
#ifdef USE_GTK
			fprintf(out, "%d:lb:Unknown file size\n", URL->pid);
#else
			printf("# File size is unknown!\n");
#endif
		else
#ifdef USE_GTK
			fprintf(out, "%d:lb:File size: %ld kb (%ld bytes)\n",
				URL->pid, (long int)((URL->ltotal)/1024+.5), URL->ltotal);
#else
			printf("# File size: %ld kbytes (%ld bytes)\n", (long int)((URL->ltotal)/1024+.5), URL->ltotal);
#endif
	}



	usleep(FTP_WAIT_TIME);
	if (!noretry)
		write(URL->sockfd, "REST 0\r\n", 8);
	usleep(FTP_WAIT_TIME);
	write(URL->sockfd, "PASV\r\n", 6);
	usleep(FTP_WAIT_TIME);
	write(URL->sockfd, "NOOP\r\n", 6);

	/* Note, it looks for a 200, which NOOP returns */
	if(!ReadSock(URL->sockfd, buffer, "\n200"))
	{	URL->retry = 1;
		write(URL->sockfd, "\r\nQUIT\r\n", 8);
		close(listfd);
		return(0);
	}

	/* If a 350 is returned, then you cannot resume the file! */
	if (strstr(buffer, "\n350") == NULL)
		URL->retry = 0;

	tempport = ReadPASVPort(buffer);
	if (!tempport)
	{	URL->retry = 1;
#ifdef USE_GTK
		fprintf(out, "%d:mb:Error#for file retrieval\n", URL->pid);
#else
		printf(" for file retrieval!\n");
#endif
		write(URL->sockfd, "\r\nQUIT\r\n", 8);
		close(URL->sockfd);
		return (0);
	}

	/* Opens up the file for appending, seeks to the end of the file and
		determines teh files size from its current location */

	if (strchr(URL->filename, ' ') != NULL)
		for (i = 0; i < strlen(URL->filename); i++)
			if (URL->filename[i] == ' ')
				URL->filename[i] = '_';

#ifdef __riscos__
        if (!riscos_style)
            temp = __riscosify(URL->filename, 0, __RISCOSIFY_DONT_TRUNCATE,
                               buffer, sizeof(buffer), NULL);
        else
            temp = gs_trans(URL->filename, buffer, sizeof(buffer));
        if (!temp) {
            printf("Error during filename conversion!\n");
            exit (100);
        }
        free(URL->filename);
        URL->filename = malloc(strlen(buffer)+1);
        strcpy(URL->filename, buffer);
        if (!riscos_style)
	    ext = strrchr(URL->filename, '/');
	else
            ext = strrchr(URL->name, '.');
        if (ext)
            filetype = mimemap_translate_ext2type(ext+1);
        else
            filetype = 0xfff;
#endif /* __riscos__ */
	URL->fp = fopen (URL->filename, "a+b");
	if (URL->fp == NULL)
	{	printf("Error opening file!\n");
		exit(69);
	}
	fseek (URL->fp,0,SEEK_END);
	URL->lsize = ftell(URL->fp);

	/* If the size is of the file (according to the FTP server)
		is 0 bytes, or it doesn't return the file size,
		the file is set to 2 gigs (doesn't effect transfer)
		and the NOSIZE flag is set */
	if (k == 0)
	{	URL->ltotal = 2000000000;
		NOSIZE = 1;
	}


	/* If the size of the file is greater than or equal to the size
		the ftp server reports, then the file is already finished!
		If URL->fp is NULL, then an error has occured when opening
		the file */
	if ((URL->lsize >= URL->ltotal && URL->ltotal != 0) || URL->fp == NULL)
	{	if (OUTPUT_LEVEL > 0)
#ifdef USE_GTK
			fprintf(out, "%d:mb:Error#It seems this file has already been\n"
				"downloaded OR it's currently in use!\n", URL->pid);
#else
			printf("Error... It seems this file has already been downloaded OR it's "
				"currently in use!\n");
#endif
		write(URL->sockfd, "\r\nQUIT\r\n", 8);
		close(datafd);
		URL->retry = 0;
		return(0);
	}

	/* If the size of the file is smaller then the set ROLLBACK size,
		then the file size is reset to 0, otherwise it rolls
		the file size back ROLLBACK bytes */
	if (URL->lsize < ROLLBACK || !URL->retry)
		URL->lsize = 0;
	else
		URL->lsize -= ROLLBACK;

	sprintf(URL->size, "%ld", URL->lsize);
	if (OUTPUT_LEVEL > 1)
#ifdef USE_GTK
		fprintf(out, "%d:lb:Found %ld kb: resuming from byte %ld\n",
				URL->pid, URL->lsize/1024, URL->lsize);
#else
		printf("# Found %ld kbytes : resuming from byte %ld.\n\n", URL->lsize/1024, URL->lsize);
#endif

        if (OUTPUT_LEVEL > 0)
#ifdef USE_GTK
		fprintf(out, "%d:lb:Requesting file transfer\n", URL->pid);
#else
                printf ("[ REQUESTING FILE TRANSFER ]\n");
#endif

	/* Tells the FTP server that you want to resume a file at
		URL->size bytes */
	usleep(FTP_WAIT_TIME);
	write(URL->sockfd, "TYPE I\r\n", 8);
	usleep(FTP_WAIT_TIME);
	if (!noretry)
	{	write(URL->sockfd, "REST ", 5);
		write(URL->sockfd, URL->size, strlen(URL->size));
		write(URL->sockfd, "\r\n", 2);
	}
	usleep(FTP_WAIT_TIME);
	write(URL->sockfd, "RETR ", 5);
	usleep(FTP_WAIT_TIME);
	write(URL->sockfd, URL->file, strlen(URL->file));
	usleep(FTP_WAIT_TIME);
	write(URL->sockfd, "\r\n", 2);
	usleep(FTP_WAIT_TIME);

	/* Sets the dldots equal to the amount that is already downloaded */
	dldots = URL->lsize / ROLLBACK;

	datafd = socket(AF_INET, SOCK_STREAM, 0);
	if (datafd == -1)
	{	if (OUTPUT_LEVEL > 0)
#ifdef USE_GTK
			fprintf(out, "%d:mb:Error#Error opening sockect to FTP server for RETR\n",
				URL->pid);
#else
			printf("Error opening socket to FTP server for RETR!\n");
#endif
		write(URL->sockfd, "\r\nABOR\r\nQUIT\r\n", 14);
		close(datafd);
		close(URL->sockfd);
		URL->retry = 1;
		return(0);
	}

	datastream.sin_family = AF_INET;
	datastream.sin_port = htons(tempport);
	memcpy(&datastream.sin_addr, URL->host->h_addr, URL->host->h_length);

	if (connect(datafd, (struct sockaddr *)&datastream, sizeof(datastream)) == -1)
	{	if (OUTPUT_LEVEL > 1)
#ifdef USE_GTK
			fprintf(out, "%d:mb:Error#Error opening sockect to FTP server for RETR\n",
				URL->pid);
#else
			printf("Error connecting to FTP socket for RETR!\n");
#endif
		write(URL->sockfd, "\r\nABOR\r\nQUIT\r\n", 14);
		close(datafd);
		URL->retry = 1;
		return(0);
	}

	if(!ReadSock(URL->sockfd, buffer, "\n150"))
	{	URL->retry = 1;
		write(URL->sockfd, "\r\nQUIT\r\n", 8);
		close(listfd);
		return(0);
	}


	write(URL->sockfd, "\r\nQUIT\r\n", 8);

	listfd = URL->sockfd;
	URL->sockfd = datafd;

	close (listfd);

	URL->lleft = URL->ltotal - URL->lsize;

	if (OUTPUT_LEVEL > 0)
#ifdef USE_GTK
		fprintf(out, "%d:lb:Connection established, transferring...\n",
				URL->pid);
#else
	{	printf ("[ REQUEST FOR FILE %s SUCCESSFUL! ]\n", URL->filename);
		printf ("[ CONNECTION ESTABLISHED, TRANSFERRING FILE ]\n\n\n");
	}
	if (OUTPUT_LEVEL > 1 && STDOUT == 0)
		printf("[.");
#endif

	/* Shrinks the file size for the rollback */
	ftruncate(fileno(URL->fp), URL->lsize);
	usleep(FTP_WAIT_TIME);

	return (1);
}

void RecurseFTP (char* URLname, char *buff, int rec)
{	char *tname;
	char *tfilename;
	char *buffer;
	char tempch;
	int i, j, ahead;
	char type;

	buffer = buff;

	if (strchr (buffer, '\r') == NULL)
		tempch = '\n';
	else
		tempch = '\r';


	while (buffer != NULL && buffer[0] != '\n' && strlen (buffer) > 7)
	{	tname = malloc (4096);
		tfilename = malloc (1024);
		tname = strcpy(tname, URLname);
		type = buffer[0];
		i = 1; j = 0;

		while ((unsigned)i < strlen(buffer) && j < 8)
		{	if (buffer[i] == ' ' && buffer[i + 1] != ' ')
				j ++;
			i++;
		}


/*		for (i = 1; i < strlen(buffer) && buffer[i] != tempch; i++);
		for (; i > 0 && buffer[i - 1] != ' '; i--);
*/
		j = 0;
		while (buffer != NULL && buffer[i] != tempch)
		{	tfilename[j] = buffer[i];
			j++; i++;
		}
		tfilename[j] = '\0';
		ahead = 0;

		if (type == 'l')
		{	if (strstr(tfilename, " -> ") != NULL)
				ahead = strlen(tfilename) - strlen(strstr(tfilename, " -> ")) + 4;
			*strrchr(tname, '/') = '\0';
		}
		tfilename += ahead;

		if (tname[strlen(tname) - 1] != '/')
			strcat(tname, "/");
		strcat(tname, tfilename);

		if (buffer != NULL)
		{   if (tfilename[strlen(tfilename) - 1] != '.' &&
			(strstr(tfilename, "/.") == NULL || strlen(strstr(tfilename, "/.")) > 3))
			switch (type)
			{	case '-':
					if (OUTPUT_LEVEL > 0)
						printf("+Added file: %s\n", tname);
					InsertURL(tname, 0);
					break;
				case 'l':
					if (rec >= 0)
					{	if (OUTPUT_LEVEL > 0)
							printf("+Added");
						InsertURL(tname, rec - 1);
					} else
						if (OUTPUT_LEVEL > 0)
							printf("-IGNORED");
					if (OUTPUT_LEVEL > 0)
						printf(" link: %s\n", tname);
					break;
				case 'd':
					if (rec > 0)
					{	if (OUTPUT_LEVEL > 0)
							printf("+Added");
						InsertURL(strcat(tname, "/"), rec - 1);
					} else
						if (OUTPUT_LEVEL > 0)
							printf("-IGNORED");
					if (OUTPUT_LEVEL > 0)
						printf(" directory: %s\n", tname);
					break;
				default:
					if (OUTPUT_LEVEL > 0)
						printf("Unrecognized file type for: %s\n", tname);
					break;
			}

			buffer = strchr(buffer, '\n');
			if (buffer != NULL)
				buffer++;
		}
		tfilename -= ahead;
		free (tfilename);
	}
}
