/**
 * Group : Edison Chindrawaly
 *         Sehoon Jung
 *         Sangtae Park
 *
 * Project: #1 Crypto Server
 */
#include "crypt_server.h"

void main()
{
 
 int listenfd;
 MESSAGE message;
 listenfd = openConnection(askPort());
 beginProcess(listenfd);
 closeConnection(listenfd);
 
}


/**
 * openConnection's method is to establish connection
 * between the client and host.
 * @param  int PORT contains predefine port number
 * @return sockfd if flag == 1 else connfd is return
 */
int openConnection(int PORT)
{
 int servsize;
 int yes = 1;
 int sockfd,connfd,clientsize;
 struct sockaddr_in my_add;
 struct sockaddr_in other_add;

 sockfd = socket(AF_INET, SOCK_STREAM,IPPROTO_TCP);
 verify(0,sockfd,socket_flag);

 bzero(&(my_add.sin_zero),sizeof(my_add));
 my_add.sin_family = AF_INET;
 my_add.sin_port = htons(PORT);
 my_add.sin_addr.s_addr = INADDR_ANY;
        
 verify(sockfd,
 bind(sockfd,(struct sockaddr *)&my_add, sizeof(struct sockaddr)),
 bind_flag);       

 setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));
 verify(sockfd,listen(sockfd,BACKLOG),listen_flag);
 
 return sockfd;
}


/**
 * beginProcess's method is where the process is being
 * execute - send/receive messages. It receives a listen socket
 * descriptors. It accepts more than 2 connections from 2 or
 * more clients. Right now I specify the max number of client
 * to be 5 clients.
 * @param  int sockfd contain socket descriptors
 * @return none
 */
void beginProcess(int sockfd)
{
 int byteRecv = 0;
 int byteSend = 0;
 int encrypt = 1;
 int decrypt = 2;
 char* tmp;
 MESSAGE message;
 int new_sock = 0;
 int fd[2];

 struct sockaddr_in other_add;
 int clisize = sizeof(other_add);

 verify(sockfd,pipe(fd),pipe_flag);

 fd_set readfds, master;
 FD_ZERO(&readfds);
 FD_ZERO(&master);
 FD_SET(STDIN,&master);
 FD_SET(sockfd,&master);
 FD_SET(fd[READ],&master);

 printf("Press any key to terminate\n");
 while(true)
 {
  readfds = master;
  verify(sockfd,
         select(FD_SETSIZE,&readfds,NULL,NULL,NULL),
         select_flag);
  if(FD_ISSET(STDIN,&readfds))
  {
   printf("crypt_server terminates\n");
   break;
  } // end to detect the keyboard

  if(FD_ISSET(sockfd,&readfds))
  {
    new_sock = accept(sockfd,(struct sockaddr *)&other_add,
                     (socklen_t *)&clisize);
    printf("Receive connection from: %s\n",inet_ntoa(other_add.sin_addr));

    if(fork() == 0)
    {
     close(sockfd);
     do
     {
      byteRecv = receivedStruct(new_sock,message);
      verify(new_sock,byteRecv,receive_flag);
      printf("Receive msg %s: %s\n", inet_ntoa(other_add.sin_addr),
             message.msg);   
      if(message.option == encrypt)
        tmp = strdup(encryptDecrypt(message.msg,message.key,encrypt));
      else
        tmp = strdup(encryptDecrypt(message.msg,message.key,decrypt));

      if((strcasecmp(message.msg,"END") == 0) ||
        (strcasecmp(tmp,"END") == 0))
      {
       message.msg_length = strlen(tmp);
       strcpy(message.msg,tmp);
       byteSend = sendStruct(new_sock,message);
       verify(new_sock,byteSend,send_flag);
       printf("Terminate: %s\n",inet_ntoa(other_add.sin_addr));
       break;
      }     
      strcpy(message.msg,tmp);
      printf("Send msg to %s: %s\n",inet_ntoa(other_add.sin_addr),
             message.msg);
      byteSend = sendStruct(new_sock,message);
      verify(new_sock,byteSend,send_flag);

     }while(byteRecv > 0);
     close(new_sock);
     exit(0);
    } // end child
    FD_CLR(new_sock,&readfds);
    close(new_sock);
  } // end to detect the socket

 } // end of while - loop

}// end of beginProcess.

/**
 * verify's method is to verify whether the operation is successful
 * @param  int sock contains socket descriptor
 *         int byte contains the byte to check
 *         int flag contains the operation type
 * @return none
 */
void verify(int sockfd, int byte, int flag)
{
switch(flag)
 {
  case 0:if(byte<0)
         {
           perror("ERROR: socket has failed\n");
           exit(0);
         }
         break;
  case 1:if(byte<0)
         {
           perror("ERROR: bind has failed\n");
           closeConnection(sockfd);
           exit(0);
         }
         break;
  case 2:if(byte<0)
         {
           perror("ERROR: listen has failed\n");
           closeConnection(sockfd);
           exit(0);
         }
         break;
  case 3:if(byte<0)
         {
           perror("ERROR: accept has failed\n");
           closeConnection(sockfd);
           exit(0);
         }
         break;
  case 4:if(byte<0)
         {
           perror("ERROR: send has failed\n");
           closeConnection(sockfd);
           exit(0);
         }
         break;
  case 5:if(byte<0)
         {
           perror("ERROR: receive has failed\n");
           closeConnection(sockfd);
           exit(0);
         }
         break;
  case 6:if(byte<0)
         {
           perror("ERROR: pipe has failed\n");
           exit(0);
         }
         break;
  case 7:if(byte<0)
         {
           perror("ERROR: write has failed\n");
           exit(0);
         }
         break;
  case 8:if(byte<0)
         {
           perror("ERROR: read has failed\n");
           exit(0);
         }
         break;
  case 9:if(byte<0)
         {
           perror("ERROR: select has failed\n");
           exit(0);
         }
  default: break;
 }
}

/**
 * receivedStruct's method is to receive struct from
 * the given socket. It will return -1 if it fails
 * to receive otherwise it will return int bigger than
 * zero.
 * @param  int sockfd contains sock descriptor
 *         struct MSG contains the struct itself
 * @return int
 */
int receivedStruct(int sockfd, MESSAGE& receivedMsg)
{
  int byteRecv = 0;
  byteRecv = recv(sockfd,(char *)&receivedMsg,sizeof(MESSAGE),0);
  return byteRecv;
}

/**
 * sendStruct's method is to send MESSAGE struct to the client/
 * server that is address by the socket descriptor
 * @param  int sockfd contains socket descriptor
 *         MESSAGE msg contains the message struct
 * @return -1 if fail otherwiser return number of bytes send
 */
int sendStruct(int sockfd, MESSAGE msg)
{
 int numSend = 0;
 numSend = send(sockfd,(char *)&msg, sizeof(MESSAGE),0);
 return numSend;
}

/**
 * encryptDecrypt's method is to encrypt or decrypt
 * a message that is pass into the method.
 * @param  char* msg contains the message
 *         char key contains the key to encrypt/decrypt
 *         int flag contains
 *         1 - to encrypt the message
 *         2 - to decrypt the message
 * @return the encrypted/decrypted message
 */
char* encryptDecrypt(char* msg, char key, int flag)
{
 char buf[MAXSIZE];
 if((msg == NULL) || (strlen(msg)<1))
   return NULL;

 int length = strlen(msg);
 switch(flag)
 {
  case 1: // encrypt message
         for(int i = 0; i<length; i++)
           buf[i] = msg[i] + key;
         break;
  case 2: // decrypt message
         for(int i = 0; i<length; i++)
           buf[i] = msg[i] - key;
         break;
  default:
          return NULL;
 }
 buf[length] = '\0';
 char* buffer = strdup(buf);
 return buffer;  
}

/**
 * closeConnection's method is to close the socket
 * descriptor that contains the connection of
 * the client/server's model
 * @param  int sockfd contains socket descriptor
 * @return none
 */
void closeConnection(int sockfd)
{
 close(sockfd);
}

int askPort()
{
 int port;
 cout<<"enter port: ";
 cin>>port;
 cout<<flush;
 return port;
}
 

1