/*
 * Autor: Carles Pina i Estany <carles arroba pinux.info>
 * Data: Juliol 2002
 * Descripció: mini-servidor FTP. No funcional, prova de concepte :-)
 * Mës informació: http://pinux.info/utils/index.html#miniftp
 * Llicència: GPL 2.0 o posterior
 */


#include <fcntl.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <linux/in.h>
#include <linux/net.h>

#define ESCRIBIR "1"
#define NOESCRIBIR "0"

/****************Inicio de la configuración*******************/

/* Dirección IP donde el servidor contestará. P. ej. ponemos la IP
 * de la Interfaz externa, así desde la interfaz interna no contestará.
 * Es importante ponerlo NO VALE poner 0.0.0.0 para TODAS */
#define IP "192.168.1.3"

/*Puerto donde ponemos el servidor*/
#define PORT 4447

/*Número máximo de usuarios que se podran conectar*/
#define MAX_USERS 10

/*Directorio donde pondremos los ficheros temporales
 * que pueda necesitar el servidor*/
#define TEMP "/tmp"

/*Nombre del proceso, si dejamos "" usará el del ejecutable*/
#define NOMBRE ""

/*Definimos los logins/contraseñas del servidor.*/
char *usuaris [][4] =
	{"carles","prova","/tmp/carles",ESCRIBIR,
	"joan","verbatim","/tmp/joan",NOESCRIBIR,
	"","",""};				/*Dejar esa línia así!!!*/


/*como saluda el servidor -dejar el 220!!!*/
#define WELCOME "220 Benvingut al servidor FTP miniserver-Pinux\r\n"

/****************Fin de la configuración*******************/

void talla_salt (char *buffer);
int autentifica (char *user, char *passwd, char *puede);
void inici_retr (int newfd, char * buffer_l);
void numport (int port, char *retorna);
void inici_stor (int newfd, char * buffer_l);
int nou_port (int *sockfd2, int *addrlen2, struct sockaddr_in * servaddr2, struct sockaddr_in * client_info2);
void fill (int newfd);

int main (int argc, char **argv) {
	int sockfd,newfd,addrlen;
	int temp;
	struct sockaddr_in servaddr;
	struct sockaddr_in client_info;

	if (strlen(NOMBRE)!=0) {
		strcpy(argv[0],NOMBRE);
	}
	
	printf("Escoltant al port: %d\n",PORT);
	printf("Contestant només a la IP: %s\n",IP);
	
	sockfd=socket (AF_INET, SOCK_STREAM, 0);
	if (sockfd<=0) {
		printf("Problemes creant socket\n");
		exit(1);
	}

	servaddr.sin_family=AF_INET;
	servaddr.sin_addr.s_addr=inet_addr(IP);
	servaddr.sin_port=htons(PORT);

	temp=bind (sockfd, (struct sockaddr*)&servaddr, sizeof(struct sockaddr_in));
	if (temp!=0) {
		printf("No puc fer el bind amb exit\n");
		exit(2);
	}
	listen (sockfd,MAX_USERS);
	addrlen=sizeof(struct sockaddr_in);
	for (;;) {
		newfd=accept(sockfd,(struct sockaddr*)&client_info, &addrlen);
		printf("IP: %s\n",inet_ntoa(client_info.sin_addr.s_addr));
		if (!fork ()) {
			close(sockfd);
			srand(time(NULL));
			printf("Faig fill\n");
			fill(newfd);
		}
	}
}

void fill (int newfd) {
        int sockfd,sockfd2,addrlen2;
        int port_dades;
	char normalitza[20];
        int auten,i;
        int newfd2;
        char puede[2];
        int conegut;
        char retorna[20];
        char *buffer_e;         /*escrivim cap al client*/
        char user[20];
        char passwd[20];
        char buffer_l[1024];    /*llegim DEL client*/
        char comanda[1024];
        char cadena[1024];
        struct sockaddr_in servaddr2;
        struct sockaddr_in client_info2;

	/***Comença autenticacio***/
	/*COMPROVAR LOGIN I PASSWD AMB LA FUNCIONA
          AUTENTIFICA!!!*/
	printf("Abans Welcome\n");
	send (newfd,WELCOME,sizeof(WELCOME),0);
	printf("Despres Welcome\n");

	recv(newfd,user,20,0);
	recv(newfd,user,20,0);
	talla_salt(user);
	printf("USUARI: _%s_\n",user);

	buffer_e="331 Password necessari\r\n";
	send (newfd,buffer_e,strlen(buffer_e),0);
	recv(newfd,passwd,20,0);
	talla_salt(passwd);
	printf("PASSWORD: _%s_\n",passwd);
	auten=autentifica(user,passwd,puede);
	printf("Escriu: %s\n",puede);
	if (auten==0) {
		buffer_e="530 Login incorrect.\r\n";
		send (newfd,buffer_e,strlen(buffer_e),0);
		close(sockfd);
		close(newfd);
		exit(2);
	}
	if (auten==3) {
		buffer_e="530 No existe tu directorio HOME.\r\n";
		send (newfd,buffer_e,strlen(buffer_e),0);
		close(sockfd);
		close(newfd);
		exit(2);
	}
	
	buffer_e="230 Ok!\r\n";
	send (newfd,buffer_e,strlen(buffer_e),0);

	/***************TROS PRINCIPAL DE GESTIO DE COMANDES**************************/
	for (;;) {
		conegut=0;
		recv(newfd,comanda,1024,0);
		talla_salt(comanda);
		printf("Comanda rebuda: _%s_\r\n",comanda);

		if (strncmp(comanda,"LIST",4)==0) {
			sprintf(buffer_l,"ls -la > %s/miniftp_temp\n",TEMP);
			printf("faig system de: %s\r\n",buffer_l);
			system (buffer_l);
			printf("Després de system\n");
			/*TODO: comprovar que ha anat OK!*/
			sprintf(comanda,"RETR %s/miniftp_temp",TEMP);
			/*Comprovar que ha anat OK!*/
			conegut=1;
		}

		if (strncmp(comanda,"NLST",4)==0) {
			printf("Nem dins\n");
			newfd2=accept(sockfd2,(struct sockaddr*)&client_info2,&addrlen2);
			printf("Connectat!\n");
			buffer_e="150 Obrint en mode binari\r\n";
			send(newfd,buffer_e,strlen(buffer_e),0);
			buffer_e=comanda;
			buffer_e+=5;

			sprintf(buffer_l,"%s\r\n",buffer_e);
			printf("Enviaré: _%s_",buffer_l);
		        send(newfd2,buffer_l,strlen(buffer_l),0);
			buffer_e="226 Transferència feta!\r\n";
			send(newfd,buffer_e,strlen(buffer_e),0);
			close(newfd2);
			conegut=1;
		}

		if (strcmp(comanda,"SYST")==0) {
			printf("Executant SYST!!\n");
			buffer_e="215 UNIX Type: L8\r\n";
			send (newfd,buffer_e,strlen(buffer_e),0);
			conegut=1;
		}
		if (strcmp(comanda,"PASV")==0) {
			if ((port_dades=nou_port (&sockfd2,&addrlen2,&servaddr2,&client_info2))!=0) {
			numport(port_dades,retorna);

			strcpy(normalitza,IP);
			for (i=0;i<strlen(normalitza);i++) {
				if (normalitza[i]=='.')
					normalitza[i]=',';
			}
			normalitza[strlen(normalitza)-2]='\0';
			
			sprintf(buffer_l,"227 Entering Passive Mode (%s,%s)\r\n",normalitza,retorna);
			send (newfd,buffer_l,strlen(buffer_l),0);
			}
			else {
			printf("Problemes buscant un nou port??\n");
			}
			conegut=1;
		}

		if (strcmp(comanda,"TYPE I")==0) {
			printf("Rebut TYPE I, no fem res ¿?\n");
			buffer_e="200 oki oki\r\n";
			send (newfd,buffer_e,strlen(buffer_e),0);
			conegut=1;
		}

		if (strncmp(comanda,"STOR",4)==0 && strncmp(puede,ESCRIBIR,1)==0) {
			printf("Entro a STOR!!\n");
			newfd2=accept(sockfd2,(struct sockaddr*)&client_info2, &addrlen2);
			printf("Despres accept\n");
			buffer_e="150 Obrint mode Binari\r\n";
			send(newfd,buffer_e,strlen(buffer_e),0);
			inici_stor (newfd2,comanda);

			buffer_e="226 Transferencia feta!\r\n";
			send(newfd,buffer_e,strlen(buffer_e),0);
			conegut=1;
			
		}

		if (strncmp(comanda,"STOR",4)==0 && strncmp(puede,NOESCRIBIR,1)==0) {
			printf("Intento de escribir sin permiso!\n");
			buffer_e="500 No tienes permisos de escritura!\r\n";
			send(newfd,buffer_e,strlen(buffer_e),0);
			conegut=1;
		}

		if (strncmp(comanda,"RETR",4)==0) {
			printf("Entro a RETR\n");
			newfd2=accept(sockfd2,(struct sockaddr*)&client_info2,&addrlen2);
			printf("Despres accept\n");
			buffer_e="150 Obrint mode Binari\r\n";
			send(newfd,buffer_e,strlen(buffer_e),0);
			inici_retr (newfd2,comanda);
			buffer_e="226 Transferència feta!\r\n";
			send(newfd,buffer_e,strlen(buffer_e),0);
			close (newfd2);
			conegut=1;
		}

		if (strcmp(comanda,"QUIT")==0) {
			buffer_e="221 Adeu!\n";
			send(newfd,buffer_e,strlen(buffer_e),0);
			printf("Sortim!\n");
			conegut=1;
			break;
		}
	
		if (!conegut) {
			sprintf(cadena,"500 has usado %s , comando no implementado. Es posible que lo haga, si pagas la hago más rápido :-)\r\n",comanda);
			send(newfd,cadena,strlen(cadena),0);
		}
		sprintf(cadena,"%s/miniftp_temp",TEMP);
		unlink(cadena);
	}
	printf("Final\n");
	close (newfd);
}

int nou_port (int *sockfd2, int *addrlen2, struct sockaddr_in * servaddr2, struct sockaddr_in * client_info2) {
	int temp;
	int port;
	

	*sockfd2=socket (AF_INET, SOCK_STREAM, 0);
	if (sockfd2<=0) {
		printf("No puc fer el socket per la segona connexio\n");
		return (0);
	}
	do {
        port=1025+(int) (65535.0*rand()/(RAND_MAX+1.0));
	servaddr2->sin_family=AF_INET;
	servaddr2->sin_addr.s_addr=htonl(INADDR_ANY);
	servaddr2->sin_port=htons(port);
	temp=bind (*sockfd2, (struct sockaddr*)servaddr2, sizeof(struct sockaddr_in));
	} while (bind (*sockfd2, (struct sockaddr*)servaddr2, sizeof(struct sockaddr_in))==0);
	
	listen (*sockfd2,1);
	*addrlen2=sizeof(struct sockaddr_in);
	//newfd=accept(sockfd,(struct sockaddr*)&client_info, &addrlen);
	printf("Port escullit: %hi\n",port);
	return (port);
}

void inici_stor (int newfd, char * buffer_l) {
	char *p;
	int f;
	/*char temp;*/
	char temp[4096];	/*paquet maxim 4096...*/
	int conta;

	p=buffer_l;

	p+=5;
	umask(666);	
	f=creat(p,S_IRWXU);	/*TODO: comprovar que s' hagi
				  pogut obrir bé!!*/

	while ((conta=recv(newfd,temp,4096,0))!=0) {
		write(f,temp,conta);
	}
	close(f);
	close (newfd);
}

void inici_retr (int newfd, char * buffer_l) {
	char *p;
	int f;
	char temp[4096];
	int conta;

	p=buffer_l;

	p+=5;
	f=open(p,O_RDONLY);
	printf("Obrire el fitxer: _%s_\n",p);
	
	while ((conta=read(f,temp,4096))!=0) {
		send(newfd,temp,conta,0);
	}
	close(f);
	close (newfd);
}
										

void talla_salt (char *buffer) {
	int i;

	for (i=0;buffer[i]!='\r';i++) {} ;

	buffer[i]='\0';
}


void numport (int port, char *retorna) {

        int baix,alt;
        baix=port&0xff;
        alt=alt>>8;
	sprintf(retorna,"%d,%d",alt,baix);
}

int autentifica (char *user, char *passwd, char *puede) {
	char *u=user+5;
	char *p=passwd+5;
        int trobat=0;
        int i=0;
	int ok=0;
	
	strcpy(puede,"0");
        while (strcmp (usuaris[i][0],"")!=0 && !trobat) {
                printf ("%s %s\n",usuaris[0][0], usuaris[0][1]);

                if (strcmp(usuaris[i][0],u)==0 &&
                    strcmp(usuaris[i][1],p)==0) {
			printf("Usuari i contrasenya trobats!\n");
			printf("Directori: %s\n",usuaris[i][2]);
			if (chdir(usuaris[i][2])==-1)
				ok=2;
			/*Si chdir retorna -1 no ha anat bé
			 * podriem mirar errno per veure en qué ha fallat*/
                        trobat=1;
			strncpy(puede,usuaris[i][3],2);
                }
		printf("Itera\n");
                i++;
        }
        return (trobat+ok);
}
