/*

                                   _JNJ`                        
                               .JNMH`                           
                              JMMF`       `;.                   
                            .NMM)          `MN.                 
                            MMM)            (MML                
                           (MMM`             MMML               
                     M  I  D  N  I  G  H  T    C  o  D  E       
                           (NMMF             MHNH               
                            NMML            .MMM                
                             NMML          .NMH                 
                              4MMNL       .#F                   
                               `4HNNL_   `                      
                                   `"""`                        

                          Copyright (C) 2004-2007
       "Ian (Larry) Latter" <ian dot latter at midnightcode dot org>

    Midnight Code is a registered trademark of Ian Latter.

    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, and mirrored at the Midnight Code web
    site; as at version 2 of the License only.
  
    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
    (version 2) along with this program; if not, write to the Free
    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
    02111-1307 USA, or see http://midnightcode.org/gplv2.txt

*/

/* netclnt.c :: Midnight Code network client example program		*/


#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <arpa/inet.h>
#include <errno.h>
#include <time.h>


#include <libmidnightcode.h>


int
main(int argc, char **argv) {
  int cfg_control;
  int cfg_deadpeer;
  int cfg_protocol;
  char cfg_input_file_name[128] = "\0";
  char cfg_output_file_name[128] = "\0";
  struct in_addr cfg_to_host;
  int cfg_port;
  int cfg_help = 0;
  char to_host_s[21];
  char end_block[11] = "=-_DONE_-=\0";
  void * tmp_void_p;
  struct midnightcode_socket_head_t * sockets;
  struct midnightcode_socket_element_t * socket;
  struct midnightcode_socket_element_t * socket_client;
  struct stat stat_buffer;
  FILE * ifp;
  FILE * ofp;
  char * buffer;
  char * buffer_old;
  int buffer_len;
  int buffer_total;
  int found_client;
  int bytes_read;
  time_t time_start;
  time_t time_stop;
  int time_taken;
  int got_size_bundle;
  int size_bundle_gap;
  char size_bundle[32];
  int size;
  int ret_i;

 
  // Prepare switches
  struct midnightcode_switch_t switches[] = {
    { "s", "server", "Server to connect to",
      MIDNIGHTCODE_PARSE_TYPE_HOST, 0, 0, &cfg_to_host.s_addr },
    { "p", "port", "Port to listen on / connect to (80)",
      MIDNIGHTCODE_PARSE_TYPE_INT, 0, 0, &cfg_port },
    { "u", "udp", "Use UDP instead (TCP)",
      MIDNIGHTCODE_PARSE_TYPE_NONE, 0, 0, &cfg_protocol },
    { "if", "input_file", "Source file, to copy from",
      MIDNIGHTCODE_PARSE_TYPE_STRING, 128, 0, &cfg_input_file_name },
    { "of", "output_file", "Target file, to copy to",
      MIDNIGHTCODE_PARSE_TYPE_STRING, 128, 0, &cfg_output_file_name },
    { "r", "raw_socket", "Use CONTROL_RAW -v- CONTROL_MIDNIGHTCODE",
      MIDNIGHTCODE_PARSE_TYPE_NONE, 0, 0, &cfg_control },
    { "dp", "deadpeer", "Enable DPD (CONTROL_MIDNIGHTCODE only)",
      MIDNIGHTCODE_PARSE_TYPE_NONE, 0, 0, &cfg_deadpeer },
    { "h", "help", "Display this help/usage screen",
      MIDNIGHTCODE_PARSE_TYPE_NONE, 0, 0, &cfg_help },
    { 0 }
  };



  // Set-up initial values;
  memset(&cfg_input_file_name, 0, sizeof(cfg_input_file_name));
  memset(&cfg_output_file_name, 0, sizeof(cfg_output_file_name));
  memset(&cfg_to_host, 0, sizeof(struct in_addr));
  cfg_control = 0;
  cfg_deadpeer = 0;
  cfg_port = 80;
  cfg_protocol = 0;

  got_size_bundle = 0;
  memset(&size_bundle, 0, sizeof(size_bundle));



  // Process switches
  if((ret_i = midnightcode_switch_process(argc, argv, switches)) != 0) {
    fprintf(stdout, "\n");
    midnightcode_logo(0, MIDNIGHTCODE_COLOUR_STYLE_BLOOD_MOON);
    midnightcode_license(0, MIDNIGHTCODE_COLOUR_STYLE_BLOOD_MOON, 
      MIDNIGHTCODE_LICENSE_TEXT_COPYRIGHT);
    fprintf(stdout, "    Error in switches, switch \"%s\" is unkown"
      " at parameter number %d.\n\n", argv[ret_i], ret_i);
    midnightcode_switch_usage(MIDNIGHTCODE_SWITCH_STYLE_SOFT, argv[0],
      switches);
    exit(1);
  }
  if(cfg_help) {
    midnightcode_logo(0, MIDNIGHTCODE_COLOUR_STYLE_BLOOD_MOON);
    midnightcode_license(0, MIDNIGHTCODE_COLOUR_STYLE_BLOOD_MOON, 
      MIDNIGHTCODE_LICENSE_TEXT_COPYRIGHT);
    midnightcode_switch_usage(MIDNIGHTCODE_SWITCH_STYLE_SOFT, argv[0],
      switches);
    exit(1);
  }
  fflush(stdout);



  // Process license and logo
  midnightcode_logo(0, MIDNIGHTCODE_COLOUR_STYLE_BLOOD_MOON);
  midnightcode_license(0, MIDNIGHTCODE_COLOUR_STYLE_BLOOD_MOON, 
    MIDNIGHTCODE_LICENSE_TEXT_ALL);



  // Program title
  midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_H1,
    MIDNIGHTCODE_STATUS_STYLE_NORMAL, "Network copy example: %s", argv[0]);

  fprintf(stdout, "\n");
  fflush(stdout);




  // Validate user params
  midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_TEXT,
                           MIDNIGHTCODE_STATUS_STYLE_NORMAL,
                           "Validating user parameters");
  if(!strlen(cfg_input_file_name)) {
    if(!strlen(cfg_output_file_name)) {
      midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_FAIL);
      fprintf(stdout, "\n");
      exit(1);
    }
  } else {
    if(midnightcode_ip_isspecial(MIDNIGHTCODE_IP_SPECIAL_RFC3171|
      MIDNIGHTCODE_IP_SPECIAL_RFC3171, cfg_to_host.s_addr) ||
      strlen(cfg_output_file_name)) {
      midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_FAIL);
      fprintf(stdout, "\n");
      exit(1);
    } else {
      memset(&to_host_s, 0, 21);
      snprintf(to_host_s, 21, "%s", inet_ntoa(cfg_to_host));
    }
  }
  midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_OKAY);



  // Resolve user settings
  if(cfg_control == 0) {
    cfg_control = MIDNIGHTCODE_SOCKET_CONTROL_MIDNIGHTCODE;
  } else {
    cfg_control = MIDNIGHTCODE_SOCKET_CONTROL_RAW;
  }
  if(cfg_deadpeer == 0) {
    cfg_deadpeer = MIDNIGHTCODE_SOCKET_DPD_DISABLE;
  } else {
    cfg_deadpeer = MIDNIGHTCODE_SOCKET_DPD_DISABLE;
    if(cfg_control == MIDNIGHTCODE_SOCKET_CONTROL_MIDNIGHTCODE)
      cfg_deadpeer = MIDNIGHTCODE_SOCKET_DPD_ENABLE;
  }
  if(cfg_protocol == 0) {
    cfg_protocol = MIDNIGHTCODE_SOCKET_PROTOCOL_TCP;
  } else {
    cfg_protocol = MIDNIGHTCODE_SOCKET_PROTOCOL_UDP;
  }





  // Socket Initialisation
  midnightcode_list_init(socket_struct_head, midnightcode_socket_element_t);
  tmp_void_p = (void *)&socket_struct_head;
  sockets = tmp_void_p;

  midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_TEXT,
                           MIDNIGHTCODE_STATUS_STYLE_NORMAL,
                           "Initialising socket library");
  if(midnightcode_socket_initialise(sockets) < 0) {
    midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_FAIL);
    fprintf(stdout, "\n");
    exit(1);
  } else {
    midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_OKAY);
  }

  time_taken = 0;



  // Go Server
  if(strlen(cfg_output_file_name)) {



    // Initialise listener socket
    midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_TEXT,
                             MIDNIGHTCODE_STATUS_STYLE_NORMAL,
                             "Installing listener socket");
    if(midnightcode_socket_open(&socket,
      cfg_control,
      cfg_deadpeer,
      1, MIDNIGHTCODE_SOCKET_DIR_LISTEN,
      cfg_protocol, "0.0.0.0", cfg_port) < 0) {
      midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_FAIL);
      fprintf(stdout, "\n");
      exit(1);
    } else {
      midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_OKAY);
    }



    // Opening output file
    midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_TEXT,
                             MIDNIGHTCODE_STATUS_STYLE_NORMAL,
                             "Opening output file");
    if((ofp = fopen(cfg_output_file_name, "w")) == NULL) {
      midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_FAIL);
      fprintf(stdout, "\n");
      exit(1);
    } else {
      midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_OKAY);
    }



    // find an client connection
    socket_client = NULL;
    found_client = 0;
    while(!found_client) {

      if(cfg_protocol == MIDNIGHTCODE_SOCKET_PROTOCOL_UDP) {

          if(socket->direction != MIDNIGHTCODE_SOCKET_DIR_RECEIVE)
            continue;

          socket_client = socket;
          found_client = 1;
          continue;

      }

      if(cfg_protocol == MIDNIGHTCODE_SOCKET_PROTOCOL_TCP) {

        midnightcode_list_for_each(socket_client, sockets) {

          if(socket_client->state != MIDNIGHTCODE_SOCKET_STATE_OPERATIONAL)
            continue;

          if(socket_client->ipv4_remote.s_addr != 0 &&
            socket_client->port_remote != 0) {
            found_client = 1;
            break;
          }
        }
        usleep(MIDNIGHTCODE_SOCKET_POLL_TIME);
      }
    }


    if(cfg_protocol == MIDNIGHTCODE_SOCKET_PROTOCOL_TCP) {

      // Closing listener socket
      midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_TEXT,
                               MIDNIGHTCODE_STATUS_STYLE_NORMAL,
                               "Closing listener socket");
      if(midnightcode_socket_close(socket)) {
        midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_FAIL);
        fprintf(stdout, "\n");
        exit(1);
      } else {
        midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_OKAY);
      }

    }



    // Have client connection
    midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_TEXT,
                             MIDNIGHTCODE_STATUS_STYLE_NORMAL,
                             "Have client connection; %s:%d -> %s:%d",
                             socket_client->ipv4_remote_s,
                             socket_client->port_remote,
                             socket_client->ipv4_local_s,
                             socket_client->port_local);
    midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_OKAY);






    // Start timer
    buffer_total = 0;
    time(&time_start);
    


    // Read socket data
    buffer = NULL;
    while(socket_client->state == MIDNIGHTCODE_SOCKET_STATE_OPERATIONAL) {



      // Reset
      usleep(MIDNIGHTCODE_SOCKET_POLL_TIME);
      if(buffer != NULL)
        free(buffer);
      buffer = NULL;
      buffer_len = 0;



      // Read socket data
      ret_i = midnightcode_socket_read_block(socket_client,
                &buffer, &buffer_len, 1, MIDNIGHTCODE_SOCKET_IO_NONBLOCKING);
      if(ret_i <= 0)
        continue;



      // Check for terminate
      if(got_size_bundle != sizeof(size_bundle)) {
        size_bundle_gap = sizeof(size_bundle) - got_size_bundle;
        if(buffer_len < size_bundle_gap) {
          memcpy(&(size_bundle[got_size_bundle]), buffer, buffer_len);
          got_size_bundle += buffer_len;
          continue;
        } else {
          memcpy(&(size_bundle[got_size_bundle]), buffer, size_bundle_gap);
          got_size_bundle += size_bundle_gap;
          if(midnightcode_parse_strtovar(&size, (char *)&size_bundle,
            sizeof(size_bundle), 0, MIDNIGHTCODE_PARSE_TYPE_INT))
            exit(1);


          // Expecting ...
          midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_TEXT,
                                   MIDNIGHTCODE_STATUS_STYLE_NORMAL,
                                   "Expecting a total of %d bytes", size);
          midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_OKAY);



          // Receive data
          midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_BLURB,
                                   MIDNIGHTCODE_STATUS_STYLE_UNKNOWN,
                                   "Receiving Data ...");



          if(buffer_len > size_bundle_gap) {
            buffer_old = buffer;
            buffer_len -= size_bundle_gap;
            buffer = malloc(buffer_len);
            if(!buffer)
              exit(1);
            memcpy(buffer, &(buffer_old[size_bundle_gap]), buffer_len);
            free(buffer_old);
          } else {
            continue;
          }
        }
      }



      // Writing output file
      if((ret_i = fwrite(buffer, sizeof(char), buffer_len, ofp)) < 1) {
        midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_TEXT,
                                 MIDNIGHTCODE_STATUS_STYLE_NORMAL,
                                 "Writing output file; %d bytes", buffer_len);
        midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_FAIL);
        printf("Error was [%d]\n", ret_i);
        fprintf(stdout, "\n");
        exit(1);
      }



      // Keep track of totals
      buffer_total += buffer_len;

      // printf("File of [%d] bytes, received\n", buffer_total);



      // Check for terminate
      if(buffer_total == size) {



        // Received terminate count
        midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_TEXT,
                                 MIDNIGHTCODE_STATUS_STYLE_NORMAL,
                                 "Received total file count of %d bytes",
                                 size);
        midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_OKAY);



        // Send socket terminate
        midnightcode_socket_write_block(socket_client, end_block,
          strlen(end_block), MIDNIGHTCODE_SOCKET_IO_BLOCKING);
        midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_TEXT,
                                 MIDNIGHTCODE_STATUS_STYLE_NORMAL,
                                 "Sending terminate message");
        if(strlen(end_block)) {
          midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_OKAY);
        } else {
          midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_FAIL);
        }
        sleep(1);



        // Closing client socket
        midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_TEXT,
                                 MIDNIGHTCODE_STATUS_STYLE_NORMAL,
                                 "Closing client socket");
        if(midnightcode_socket_close(socket_client)) {
          midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_FAIL);
          fprintf(stdout, "\n");
          exit(1);
        } else {
          midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_OKAY);
        }


        continue;
      }
    }



    // Stop timer
    time(&time_stop);
    time_taken = (int)(time_stop - time_start);
    if(time_taken < 1)
      time_taken = 1;



    // Closing output file
    midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_TEXT,
                             MIDNIGHTCODE_STATUS_STYLE_NORMAL,
                             "Closing output file");
    if(fclose(ofp)) {
      midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_FAIL);
      fprintf(stdout, "\n");
      exit(1);
    } else {
      midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_OKAY);
    }



  // Go Client
  } else {



    // Initialise outbound socket
    midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_TEXT,
                             MIDNIGHTCODE_STATUS_STYLE_NORMAL,
                             "Opening new connection; %s:%d",
                             to_host_s, cfg_port);
    if(midnightcode_socket_open(&socket,
      cfg_control,
      cfg_deadpeer,
      0, MIDNIGHTCODE_SOCKET_DIR_INITIATE,
      cfg_protocol, to_host_s, cfg_port) < 0) {
      midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_FAIL);
      fprintf(stdout, "\n");
      exit(1);
    } else {
      midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_OKAY);
    }


    // Opening input file
    midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_TEXT,
                             MIDNIGHTCODE_STATUS_STYLE_NORMAL,
                             "Opening input file");
    if((ifp = fopen(cfg_input_file_name, "r")) == NULL) {
      midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_FAIL);
      fprintf(stdout, "\n");
      exit(1);
    } else {
      midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_OKAY);
    }



    // Calculate file size to send
    memset(&stat_buffer, 0, sizeof(struct stat));
    if(stat(cfg_input_file_name, &stat_buffer))
      exit(1);
    size = stat_buffer.st_size;
    midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_TEXT,
                             MIDNIGHTCODE_STATUS_STYLE_NORMAL,
                             "Input file size is %d bytes", size);
    midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_OKAY);



    // Send file size info
    if(midnightcode_parse_vartostr((char *)&size_bundle, &size,
      sizeof(size_bundle), 0, MIDNIGHTCODE_PARSE_TYPE_INT))
      exit(1);
    ret_i = midnightcode_socket_write_block(socket, size_bundle,
      sizeof(size_bundle), MIDNIGHTCODE_SOCKET_IO_BLOCKING);
    midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_TEXT,
                             MIDNIGHTCODE_STATUS_STYLE_NORMAL,
                             "Sending socket 32 byte file size");
    if(ret_i <= 0) {
      midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_FAIL);
    } else {
      midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_OKAY);
    }



    // Calculate a fair buffer size
    if(cfg_protocol == MIDNIGHTCODE_SOCKET_PROTOCOL_TCP) {
      buffer_len = midnightcode_socket_membrane_size_est(
        MIDNIGHTCODE_SOCKET_CODED_DECODE,
        MIDNIGHTCODE_SOCKET_PACKET_DATA,
        MIDNIGHTCODE_SOCKET_WIRE_BLOCK_MAX) - 100;
      if(buffer_len < 0)
        buffer_len = 65535;
    } else {
      /* NOTE: UDP, ip_conntrack kills packets greater than this */
      buffer_len = 4096 * 3;
    }
    buffer = malloc(buffer_len);
    buffer_total = 0;



    // Start timer
    time(&time_start);



    // Send data
    midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_BLURB,
                             MIDNIGHTCODE_STATUS_STYLE_UNKNOWN,
                             "Sending Data ...");



    // Send data
    while(!feof(ifp)) {


      // XX
      // sleep(2);

      usleep(MIDNIGHTCODE_SOCKET_POLL_TIME);
      memset(buffer, 0, buffer_len);



      // Reading input file
      if((ret_i = fread(buffer, sizeof(char), buffer_len, ifp)) < 1) {
        midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_TEXT,
                                 MIDNIGHTCODE_STATUS_STYLE_NORMAL,
                                 "Reading input file; %d bytes", buffer_len);
        midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_FAIL);
        perror("read error:");
        printf("Error was [%d]\n", ret_i);
        fprintf(stdout, "\n");
        exit(1);
      }



      // Retain totals
      bytes_read = ret_i;



      // Send socket data
      ret_i = midnightcode_socket_write_block(socket, buffer,
        bytes_read, MIDNIGHTCODE_SOCKET_IO_BLOCKING);
      if(ret_i <= 0) {
        midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_TEXT,
                                 MIDNIGHTCODE_STATUS_STYLE_NORMAL,
                                 "Sending socket data block; %d bytes",
                                 bytes_read);
        midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_FAIL);
        fprintf(stdout, "\n");
        break;
      } else {
        buffer_total += bytes_read;
      }

    }



    // Display byte summary
    midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_TEXT,
                             MIDNIGHTCODE_STATUS_STYLE_NORMAL,
                             "File of %d bytes sent", buffer_total);
    if(buffer_total != size) {
      midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_FAIL);
      fprintf(stdout, "\n");
      exit(1);
    } else {
      midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_OKAY);
    }



    // Closing input file
    midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_TEXT,
                             MIDNIGHTCODE_STATUS_STYLE_NORMAL,
                             "Closing input file");
    if(fclose(ifp)) {
      midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_FAIL);
      fprintf(stdout, "\n");
      exit(1);
    } else {
      midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_OKAY);
    }



    // Stop timer
    time(&time_stop);
    time_taken = (int)(time_stop - time_start);
    if(time_taken < 1)
      time_taken = 1;



    // Read socket data
    buffer = NULL;
    while(socket->state == MIDNIGHTCODE_SOCKET_STATE_OPERATIONAL) {



      // Reset
      usleep(MIDNIGHTCODE_SOCKET_POLL_TIME);
      if(buffer != NULL)
        free(buffer);
      buffer = NULL;
      buffer_len = 0;



      // Read socket data
      ret_i = midnightcode_socket_read_block(socket, &buffer,
        &buffer_len, 1, MIDNIGHTCODE_SOCKET_IO_NONBLOCKING);
      if(ret_i <= 0)
        continue;



      // Check for terminate
      if(strlen(end_block) == buffer_len) {

        if(memcmp(end_block, buffer, buffer_len) == 0) {



          // Received terminate message
          midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_TEXT,
                                   MIDNIGHTCODE_STATUS_STYLE_NORMAL,
                                   "Received terminate message");
          midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_OKAY);




          // Closing client socket
          midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_TEXT,
                                   MIDNIGHTCODE_STATUS_STYLE_NORMAL,
                                   "Closing socket");
          if(midnightcode_socket_close(socket)) {
            midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_FAIL);
            fprintf(stdout, "\n");
            exit(1);
          } else {
            time(&time_stop);
            time_taken = (int)(time_stop - time_start);
            if(time_taken < 1)
              time_taken = 1;
            midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_OKAY);
          }



          continue;
        }
      }
    }
  }



  // Socket shutdown
  midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_TEXT,
                           MIDNIGHTCODE_STATUS_STYLE_NORMAL,
                           "Shutdown socket library");
  if(midnightcode_socket_shutdown(sockets) < 0) {
    midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_FAIL);
    fprintf(stdout, "\n");
    exit(1);
  } else {
    midnightcode_status_flag(MIDNIGHTCODE_STATUS_FLAG_OKAY);
  }



  // Transfer rate
  fprintf(stdout, "\n");
  midnightcode_status_text(MIDNIGHTCODE_STATUS_TYPE_BLURB,
                           MIDNIGHTCODE_STATUS_STYLE_UNKNOWN,
    "%d bytes in %d seconds = %02.2fMbytes/sec",
    buffer_total, time_taken,
    (float)((float)buffer_total / (float)(time_taken)) / (float)1000000);
  fprintf(stdout, "\n");


  exit(0);
}
