/* ==================================================================================
   SECONDA PROVA INFO2
   Nome Programma: lab2Server.c
   Cognome: Nicosia Gaetano
   Matricola: 706996
   Classe Virtuale: CV3
   Si chiede di realizzare un server in grado di memorizzare l'insieme 
   di parole inviate dai client che si collegano, e di contare 
   il numero di occorrenze di una certa parola nell'insieme. 
   Il comportamento del server sia il seguente:
   1) una volta lanciato, si ponga in attesa di connessioni sulla porta 
      specificata dal primo argomento passato sulla linea di comando
   2) una volta accettata una connessione, invii un messaggio di benvenuto 
      e si ponga in attesa della trasmissione di una parola. 
      Per parola si intende una linea formata da soli caratteri e/o cifre
   3) dopo aver accettato la parola, il server conti quante volte ha già 
      ricevuto quella stessa parola, in passato, e lo comunichi al client
   4) il server chiuda la connessione con il client

   Caratteristiche:
   a) il server deve essere in grado di gestire connessioni contemporanee
   b) il server non può utilizzare file per memorizzare l'insieme di parole ricevute 

   Esempio di funzionamento (terminale lato client1 e client2).
   Nota: il server non ha mai ricevuto la parola "eccomi"

   <server accetta connessione client1>
   <server invia client1>Benvenuto nel server conta-occorrenze. Invia la parola
   <server accetta connessione client2>
   <server invia client2>Benvenuto nel server conta-occorrenze. Invia la parola
   <client1 invia>eccomi
   <server invia client1> occorrenze: 0
   <server chiude connessione con client1>
   <client2 invia>eccomi
   <server invia client2>occorrenze: 1
   <server chiude connessione client2>

   Si suppone che il client sia telnet, e che l'utente lato client 
   termini ogni linea premendo enter.
   ================================================================================*/

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdio.h>
#include <ctype.h>
#include <signal.h>
#include <errno.h>
#include <malloc.h>

#define MAXCONN 5
#define BUFLEN 100
#define BENVENUTO  "Benvenuto nel server conta occorrenze \r\n"
#define RICHIESTA "Invia la parola\r\n"

struct Tag_Nodo {
		  char *Parola;
      int Ripetizioni;
		  struct Tag_Nodo *Next;
		 };

typedef struct Tag_Nodo Tipo_Nodo;
typedef Tipo_Nodo *Tipo_Lista;

// Prototipi
void addressInit(struct sockaddr_in *indirizzo, int port, long IPaddr);
void parolaRicevuta(int sockDesc,char *s);
Tipo_Lista Insert(Tipo_Lista l, char *parola, int ripetizioni);
Tipo_Nodo  *Search(Tipo_Lista l, char *parola);
void Is_Empty(Tipo_Lista l);
Tipo_Lista Inizializza(Tipo_Lista l);

int main (unsigned argc, char **argv) {
int SERVER_PORT, sock, client_len, fd;
struct sockaddr_in server, client;
char risposta[40];
Tipo_Lista listaParole = NULL;
Tipo_Nodo *NodoTrovato;
int ripetizioni;
char *parola;
listaParole            = Inizializza(listaParole);

 if (argc != 2) {
   fprintf(stderr, "Sintassi: %s <porta>\n", argv[0]);
   exit(4);
 }

SERVER_PORT = atoi(argv[1]);

/* impostazione del transport end point */
if((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
	perror("chiamata alla system call socket fallita");
	exit(1);
}

/* inizializzo la struttura dell'indirizzo internet del server 
   la funzione utilizzata copia 0 nei primi strlen(myServer) byte dell'oggetto puntato
   da &server, operando in pratica un azzeramento della struttura. */
memset ( &server, 0, sizeof(server) );
addressInit(&server, SERVER_PORT, INADDR_ANY);

/* binding dell'indirizzo al transport end point */
if (bind(sock, (struct sockaddr *)&server, sizeof server) == -1) {
	perror("chiamata alla system call bind fallita");
	exit(2);
}

/* imposto il server in modo che possa gestire 5 richieste
   contemporaneamente */
listen(sock, MAXCONN);

/* inserisco questa chiamata per evitare che il figlio resti 'zombie'
   dovendo restituire il codice di ritorno al padre */
signal(SIGCHLD,SIG_IGN);

/* gestione delle connessioni dei client */
while (1) {
	client_len = sizeof(client);
	if ((fd = accept(sock, (struct sockaddr *)&client, &client_len)) < 0) {
		perror("Errore di accept della connessione");
		exit(3);
	}

	/* ogni volta che il server accetta una nuova connessione,
     quest'ultima viene gestita da un nuovo processo figlio */
	switch(fork()) {
		case -1:
			perror("Errore nella chiamata alla fork");
			exit(4);
		case 0:
			fprintf(stderr, "Aperta connessione (PID %d).\n",getpid());
			send(fd, BENVENUTO, strlen(BENVENUTO), 0);
			send(fd, RICHIESTA, strlen(RICHIESTA), 0);
      char parola[BUFLEN] = " ";
      parolaRicevuta(fd,parola);
      /* Se la parola non esiste la inserisce nella lista con
         ripetizione = 0
         Se la parola esiste incrementa di 1 il contatore */
      NodoTrovato = Search(listaParole, parola);
//      ripetizioni = NodoTrovato;
      if (NodoTrovato != NULL){
      	ripetizioni = 0;
      	listaParole = Insert(listaParole, parola, ripetizioni);
      	}
      else {
      	ripetizioni = listaParole->Ripetizioni;	
      }	
     sprintf(risposta,"Il numero di occorrenze e' %d\n",ripetizioni);
     send(fd,risposta,strlen(risposta),0);

			close(fd);
			fprintf(stderr, "Chiusa connessione (PID %d).\n",getpid());
}
}
}

/**************************************************************************************************************************/
void addressInit(struct sockaddr_in *indirizzo, int port, long IPaddr)
{
  // Creo l'indirizzo Internet locale
   indirizzo->sin_family = AF_INET;
   indirizzo->sin_port = htons((u_short) port); // ordinamento dei Byte da Host a Rete

  /* indicando INADDR_ANY viene collegato il socket all'indirizzo locale IP
  *  dell'interaccia di rete che verrà utilizzata per inoltrare il datagram IP */
   indirizzo->sin_addr.s_addr = IPaddr;
}

void parolaRicevuta(int sockDesc,char *s){
	char c;
	int i=0;
	int n;
          do {
                 n = recv(sockDesc, &c, 1, 0);
                 if (c!='\n'){
                    s[i]=c;
                 i++;}
           } while( c!='\n' );
          s[i]='\0';     
}    

Tipo_Lista Inizializza(Tipo_Lista l)
 {
   return (l = NULL);
 }
 
Tipo_Lista Insert(Tipo_Lista l, char *parola, int ripetizioni)
 {
   Tipo_Lista Prec = NULL;
   Tipo_Lista Succ = l;
   Tipo_Lista Temp;

   while ( (Succ != NULL) && (strcmp(parola,Succ->Parola) > 0) )
     {
   	Prec = Succ;
	  Succ = Succ->Next;
     }
   
   Temp = (Tipo_Lista) malloc(sizeof(Tipo_Nodo));
   if (Temp == NULL)
     {
      	printf("\nErrore nell'allocazione.\n");
        exit(1);
     }
   
   if (Prec == NULL)
     { 
	      Temp->Parola = parola;
        Temp->Ripetizioni = 0;
	      Temp->Next  = Succ;
	      return Temp;
     }
   else
     {  
        Temp->Parola = parola;
        Temp->Ripetizioni = ripetizioni;
      	Temp->Next = Succ;
	      Prec->Next = Temp;
        return l;
     }
 }

Tipo_Nodo *Search(Tipo_Lista l, char *parola)
 { 
   while ( (l != NULL) && (strcmp(parola,l->Parola) > 0) )
     l = l->Next;
   if ( (l != NULL) && (strcmp(l->Parola,parola) == 0) ){
   	  l->Ripetizioni++;
      return l;}
   else
      return NULL; 
 }

