/* 11s.c */
#ifdef __cplusplus
#error Be sure you are using a C compiler...
#endif

#if defined (WIN32)

#include <winsock2.h>

#elif defined (linux) || defined (_POSIX_VERSION) || defined (_POSIX2_C_VERSION)\
 || defined (_XOPEN_VERSION)

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>             /* close */

#define INVALID_SOCKET -1
#define SOCKET_ERROR -1

#define closesocket(s) close (s)
typedef int SOCKET;
typedef struct sockaddr_in SOCKADDR_IN;
typedef struct sockaddr SOCKADDR;

#else
#error not defined for this platform
#endif

#include <stdio.h>
#include <stdlib.h>

#include <pthread.h>

/* macros ============================================================== */

#define TELNET 23
#define IMAP 143

#define PORT TELNET

#define ESC 27

/* constants =========================================================== */
/* types =============================================================== */
/* structures ========================================================== */

struct cli
{
   pthread_t thread;
   SOCKADDR_IN sin;
   int recsize;
   SOCKET sock;
   int err;
};

/* private data ======================================================== */
/* private functions =================================================== */

/* thread client function */
static void *client (void *p_data)
{
   struct cli *p_cli = p_data;
   if (p_cli != NULL)
   {
      int end = 0;
      do
      {
         /* wait for the receive of a data block */
         unsigned char data[128];
         int sock_err = recv (p_cli->sock, data, (sizeof data - 1), 0);

         if (sock_err != SOCKET_ERROR)
         {
            size_t nb_rec = sock_err;
            if (nb_rec > 0)
            {
               /* convert to string */
               data[nb_rec] = 0;
               printf ("%u byte%s received:\n'%s'\n",
                       (unsigned) nb_rec, nb_rec > 1 ? "s" : "", data);
               fflush (stdout);

               if (strcmp (data, "quit\n") == 0)
               {
                  puts ("Disconnection request");
                  end = 1;
               }
               else if (strcmp (data, "now\n") == 0)
               {
                  time_t now = time(NULL);
                  char *response = strdup (ctime(&now));

                  send (p_cli->sock, response, strlen (response), 0);
                  free (response);
               }
               else
               {
                  char const response[] = "ERR\n";
                  send (p_cli->sock, response, strlen (response), 0);
               }
            }
            else
            {
               puts ("client is disconnected");
               end = 1;
            }
         }
         else
         {
            perror ("socket.recv");
            p_cli->err = 1;
            end = 1;
         }
      }
      while (!end);
      shutdown (p_cli->sock, 2);
      printf ("closing client socket %d...\n", p_cli->sock);
      closesocket (p_cli->sock), p_cli->sock = INVALID_SOCKET;

      /* the memory is now under the control of the thread */
      free (p_cli), p_cli = NULL;

   }

   return NULL;
}

static int clients (SOCKET sock)
{
   int err = 0;
   int end = 0;
   do
   {
      /* wait for a client connection */
      printf ("waiting for a client connection on port %d...\n", PORT);

      {
         /* create a new client context */
         struct cli *p_cli = malloc (sizeof *p_cli);
         if (p_cli != NULL)
         {
            p_cli->recsize = (int) sizeof p_cli->sin;
            p_cli->sock =
               accept (sock, (SOCKADDR *) & p_cli->sin, &p_cli->recsize);

            if (p_cli->sock != INVALID_SOCKET)
            {
               printf
                  ("client connected with socket %d from %s:%d\n",
                   p_cli->sock, inet_ntoa (p_cli->sin.sin_addr),
                   htons (p_cli->sin.sin_port));

/* send ...*/
               pthread_create (&p_cli->thread, NULL, client, p_cli);
               /* ... and forget */

            }
            else
            {
               perror ("socket.accept");
               err = 1;
            }
         }
         else
         {
            fprintf (stderr, "client creation failed : memory error\n");
         }
      }
   }
   while (!end);
   return err;
}

/* ---------------------------------------------------------------------
   --------------------------------------------------------------------- */
static int app (void)
{
   int err = 0;

   /* open a socket in TCP/IP mode. */
   SOCKET sock = socket (AF_INET, SOCK_STREAM, 0);

   if (sock != INVALID_SOCKET)
   {
      printf ("socket %d is now opened in TCP/IP mode\n", sock);

      /* we want to listen on the TELNET port */
      {
         int sock_err;
         /* assign the listening port */
         SOCKADDR_IN sin = { 0 };

         /* automatic IP address */
         sin.sin_addr.s_addr = htonl (INADDR_ANY);

         /* protocol family (IP) */
         sin.sin_family = AF_INET;

         /* listening port */
         sin.sin_port = htons (PORT);

         /* bind */
         sock_err = bind (sock, (SOCKADDR *) & sin, sizeof sin);

         if (sock_err != SOCKET_ERROR)
         {
            /* start listening (server mode) */
            sock_err = listen (sock, 5);

            printf ("listening on port %d...\n", PORT);

            if (sock_err != SOCKET_ERROR)
            {
               err = clients (sock);
            }
            else
            {
               perror ("socket.listen");
               err = 1;
            }
         }
         else
         {
            perror ("socket.bind");
            err = 1;
         }

         printf ("closing socket %d...\n", sock);

         /* close the socket. */
         sock_err = closesocket (sock), sock = INVALID_SOCKET;

         printf ("the socket is now closed\n");

         if (sock_err)
         {
            perror ("socket.close");
            err = 1;
         }
      }
   }
   else
   {
      perror ("socket.open");
      err = 1;
   }

   return err;
}

/* entry point ========================================================= */

/* ---------------------------------------------------------------------
   --------------------------------------------------------------------- */
int main (void)
{
   int ret;
#if defined (WIN32)
   WSADATA wsa_data;
   int err = WSAStartup (MAKEWORD (2, 2), &wsa_data);

   if (!err)
   {
      puts ("WIN: winsock2: OK");
#else
   int err;
#endif

   err = app ();

#if defined (WIN32)
   WSACleanup ();
}
#endif

if (err)
{
   ret = EXIT_FAILURE;
}
else
{
   ret = EXIT_SUCCESS;
}

system ("pause");

return ret;
}
