/*
prog: ProOS MUTS API
lang: C
prot: TCP/IP
plat: GNU/Linux (ifdef __WIN32__ Windows NT5)
requ: POSIX Threads, Networking

Copyright (c) 2007, Group 2 TI ProOS 2006/2007
                    Fontys University of Professional Education

    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; either version 2 of the License, or
    (at your option) any later version.

    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
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/
#include <stdio.h>
#ifdef __WIN32__
  #include <winsock.h>
  #include <stdlib.h>
  #define MSG_WAITALL 0;
#else
  #include <sys/socket.h>
  #include <arpa/inet.h>
  #include <stdlib.h>
  #include <string.h>
  #include <unistd.h>
#endif
#include "pthread.h"
#include "proos.h"

/**********************************************************************************
 *                             PRIVATE CODE                                       *
 **********************************************************************************/


/*
 * Global and threading variables.
 */
volatile int proos_socket = -1;
volatile unsigned char proos_client_number = 0;
volatile unsigned char proos_network_handler_halt = 0x00;
volatile proos_packet proos_answer;
volatile pthread_mutex_t proos_mutex_net = PTHREAD_MUTEX_INITIALIZER;
pthread_t proos_network_handler;
#ifdef __WIN32__
  WSADATA proos_wsa;
#endif

void debug_msg (char *msg) {
  #ifdef EBUG
  fprintf(stderr, msg);
  #endif
}

void debug_packet (const proos_packet *packet) {
  #ifdef EBUG
  char *data = "";
  if (packet->data_len > 0) {
    data = (char *) malloc(packet->data_len + 1);
    memcpy(data, packet->data, packet->data_len);
    data[packet->data_len] = '\0';
  }
  fprintf(stderr, "Packet:\n-Protocol\t\t\t%c%c%c\n-Version\t\t\t%d\n-Client Number\t\t\t%d\n-Command\t\t\t0x%02X\n-Data Length\t\t\t%d\n-Parity\t\t\t\t0x%02X\n-Data\t\t\t\t%s\n",
          packet->protocol[0],
          packet->protocol[1],
          packet->protocol[2],
          packet->version,
          packet->client_num,
          packet->command,
          packet->data_len,
          packet->parity,
          data
         );
  if (packet->data_len > 0)
    free(data);
  #endif
}

/*
 * We need to be able to send the packet without data.
 */
typedef struct {
  char protocol[3];
  unsigned char version;
  unsigned char client_num;
  unsigned char command;
  unsigned char data_len;
  unsigned char parity;
} proos_stripped_packet;

/*
 * Private function to wait for anwer of the network handler.
 */
void proos_netwait (void) {
  pthread_mutex_lock((pthread_mutex_t *) &proos_mutex_net);
  debug_msg("Networking Mutex Freed. Continue.\n");
  pthread_mutex_unlock((pthread_mutex_t *) &proos_mutex_net);
}

/*
 * Private function to send a packet.
 */
unsigned char proos_send (const proos_packet *packet) {
  /*
   * Debug It
   */
  debug_packet(packet);

  /*
   * Do we have a connection?
   */
  if (proos_socket == -1) {
    debug_msg("Socket not yet Initialized. Cannot Send Packet.\n");
    return 0x00;
  }

  /*
   * Create a proos_stripped_packet first.
   */
  proos_stripped_packet spacket;
  memcpy(spacket.protocol, packet->protocol, 3);
  spacket.version = packet->version;
  spacket.client_num = packet->client_num;
  spacket.command = packet->command;
  spacket.data_len = packet->data_len;
  spacket.parity = packet->parity;

  /*
   * Send the first parts of the packet.
   */
  if (send(proos_socket, (char *) &spacket, sizeof(proos_stripped_packet), 0) != sizeof(proos_stripped_packet)) {
    debug_msg("Send Stripped Packet Failed.\n");
    return 0x00;
  }

  /*
   * If we've got data send it too.
   */
  if (packet->data != NULL && packet->data_len != 0) {
    if (send(proos_socket, (char *) packet->data, packet->data_len, 0) != packet->data_len) {
      debug_msg("Send Data Packet Failed.\n");
      return 0x00;
    }
  }
  
  /*
   * Lock the networking mutex.
   * Only if it's not a primary non-nethandler command.
   */
  if (packet->command != PROOS_PACKET_CLIENTREG) {
    debug_msg("Locked Networking Mutex.\n");
    pthread_mutex_lock((pthread_mutex_t *) &proos_mutex_net);
  }

  /*
   * Great succes!
   */
  debug_msg("Packet Send Successful.\n");
  return 0x01;
}

/*
 * Private helper recursive function to aid proos_recv().
 * Old behaviour was sometimes problematic with Win32 BOSS.
 * It sent data in lots of small stages somehow.
 * This is designed to wait just as long until the buffer is full.
 */
unsigned int proos_recv_helper (char *buffer, unsigned int len) {
  unsigned int recv_len = 0, tmp_len = 0;
  char *tmpbuffer = buffer;
  
  if ((recv_len = recv(proos_socket, tmpbuffer, len, MSG_WAITALL)) < 0) {
    debug_msg("Socket Receive Error.\n");
    return 0;
  }
  
  tmp_len = recv_len;
  while (recv_len < len) {
    tmpbuffer += tmp_len;
    
    if ((tmp_len = recv(proos_socket, tmpbuffer, len, MSG_WAITALL)) < 0) {
      debug_msg("Socket Receive Error.\n");
      return 0;
    }
    
    recv_len += tmp_len;
  }
  
  return recv_len;
}

/*
 * Private function to receive a packet.
 * Be sure to free the data after using it.
 */
unsigned char proos_recv (proos_packet *packet) {
  /*
   * Do we have a connection?
   */
  if (proos_socket == -1) {
    debug_msg("Socket not yet Initialized. Cannot Receive Packet.\n");
    return 0x00;
  }
  
  /*
   * Receive the first part of the packet.
   */
  proos_stripped_packet spacket;
  if (proos_recv_helper((char *) &spacket, sizeof(proos_stripped_packet)) != sizeof(proos_stripped_packet)) {
    debug_msg("Packet Receive First Portion Failed.\n");
    return 0x00;
  }
  
  /*
   * Is this an alien message from an Alpha-class planet or a proos packet?
   */
  if (spacket.version != PROOS_VERSION || !(spacket.protocol[0] == 0x50 && spacket.protocol[1] == 0x4F && spacket.protocol[2] == 0x53)) {
    debug_msg("Packet Receive Failed: Protocol Mismatch.\n");
    return 0x00;
  }
  
  /*
   * If there's data to receive, get that too.
   */
  char *data = NULL;
  if (spacket.data_len > 0) {
    data = (char *) malloc(spacket.data_len);
    if (data == NULL) {
      debug_msg("Out Of Memory!\n");
      return 0x00;
    }
    if (proos_recv_helper((char *) data, spacket.data_len) != spacket.data_len) {
      debug_msg("Packet Receive Data Portion Failed.\n");
      free(data);
      return 0x00;
    }
  }
  
  /*
   * Reconstruct the packet.
   */
  memcpy(packet->protocol, spacket.protocol, 3);
  packet->version = spacket.version;
  packet->client_num = spacket.client_num;
  packet->command = spacket.command;
  packet->data_len = spacket.data_len;
  packet->parity = spacket.parity;
  packet->data = data;
  
  /*
   * Debug It
   */
  debug_packet(packet);
  
  /*
   * Great succes!
   */
  debug_msg("Packet Receive Successful.\n");
  return 0x01;
}

/*
 * Private function (will run as child process) to manage the network traffic.
 */
void *proos_nethandler (void *arg) {
  debug_msg("Nethandler Started.\n");

  /*
   * We can only operate when the net connection is active.
   */
  if (proos_socket == -1) {
    debug_msg("Socket Not Initialized. Nethandler Exiting.\n");
    proos_network_handler_halt = 0x01;
    goto after_parser;
  }

  proos_packet recv_packet, pong_packet;
  #ifdef EBUG
    char debug_msg_buf[40];
  #endif

  /*
   * Prebuild the pong packet.
   */
  memcpy(pong_packet.protocol, "POS", 3);
  pong_packet.version = PROOS_VERSION;
  pong_packet.client_num = proos_client_number;
  pong_packet.command = PROOS_PACKET_PONG;
  pong_packet.data_len = 0;
  pong_packet.parity = 0;
  pong_packet.data = NULL;
  
  while (!proos_network_handler_halt) {
    /*
     * Listen for incoming packets.
     */
    if (!proos_recv(&recv_packet)) {
      debug_msg("Receiver Failed. Stopping Nethandler.\n");
      proos_network_handler_halt = 0x01;
      goto after_parser;
    }
    
    /*
     * Got one. Check if it's for me.
     */
    if (recv_packet.client_num == proos_client_number || recv_packet.client_num == 255) {
      switch (recv_packet.command) {
        case PROOS_PACKET_DUMMY:
          debug_msg("Dummy Packet Received.\n");
        break;
        case PROOS_PACKET_PING:
          /*
           * _______________
           *< PING PONG MOO >
           * ---------------
           *     \   ^__^
           *      \  (oo)\_______
           *         (__)\       )\/\
           *             ||----w |
           *             ||     ||
           */
          debug_msg("Ping Received.\n");
          proos_send(&pong_packet);
          debug_msg("Pong Sent.\n");
        break;
        case PROOS_PACKET_CLIENTREG_ANSWER:
        case PROOS_PACKET_CLIENTUNREG_ANSWER:
        case PROOS_PACKET_REGSHARED_ANSWER:
        case PROOS_PACKET_REGSEM_ANSWER:
        case PROOS_PACKET_UNREGSEM_ANSWER:
        case PROOS_PACKET_SEMP_ANSWER:
        case PROOS_PACKET_SEMV_ANSWER:
        case PROOS_PACKET_MEMWONE_ANSWER:
        case PROOS_PACKET_MEMWTWO_ANSWER:
        case PROOS_PACKET_MEMR_ANSWER:
        case PROOS_PACKET_STDOUT_ANSWER:
        case PROOS_PACKET_UNREGSHARED_ANSWER:
          #ifdef EBUG
            sprintf(debug_msg_buf, "Command 0x%02X Received.\n", recv_packet.command);
            debug_msg(debug_msg_buf);
          #endif
          /*
           * Copy the packet to the volatile memory packet buffer for the master to play with.
           * Void typecast to fix gcc's volatile global problem.
           */
          memcpy((void *) &proos_answer, &recv_packet, sizeof(proos_packet));
          
          /*
           * Unlock the networking mutex.
           */
          debug_msg("Unlocked Networking Mutex.\n");
          pthread_mutex_unlock((pthread_mutex_t *) &proos_mutex_net);
        break;
        case PROOS_PACKET_HALTBROADCAST:
          /*
           * The BOSS is dead. Die miserably in his honour.
           */
          debug_msg("The BOSS told us to die. Dying...\n");
          exit(0);
        break;
      }
    } else
      debug_msg("Received Packet, but it was not for me. Check Network Hardware.\n");
    
    /*
     * Start listening for a new packet.
     */
  }
  
  after_parser:
  
  /*
   * proos_network_handler_halt set to 0x01.
   * Exit thread.
   */
  debug_msg("Nethandler Exited.\n");
  return NULL;
}


/**********************************************************************************
 *                             PUBLIC CODE                                        *
 **********************************************************************************/


unsigned char proos_connect (const char *address, const unsigned int port) {
  struct sockaddr_in sock_req;

  #ifdef __WIN32__
  /*
   * Load Winsock 2.0 lib
   */
  if (WSAStartup(MAKEWORD(2, 0), &proos_wsa) != 0) {
    debug_msg("Socket Create Failed: WSAStartup().\n");
    return 0x00;
  }
  #endif
   
  /*
   * Set up connection.
   */
  if ((proos_socket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
    debug_msg("Socket Create Failed.\n");
    return 0x00;
  }
  memset(&sock_req, 0, sizeof(sock_req));
  sock_req.sin_family = AF_INET;
  sock_req.sin_addr.s_addr = inet_addr(address);
  sock_req.sin_port = htons(port);
  if (connect(proos_socket, (struct sockaddr *) &sock_req, sizeof(sock_req)) < 0) {
    debug_msg("Socket Connect Failed.\n");
    return 0x00;
  }
  
  /*
   * Great succes!
   */
  debug_msg("Socket Connect Successful.\n");
  return 0x01;
}

unsigned char proos_disconnect (void) {
  /*
   * Try to close the socket.
   */
  #ifdef __WIN32__
    if (closesocket(proos_socket) < 0) {
  #else
    if (close(proos_socket) < 0) {
  #endif
    debug_msg("Socket Close Failed.\n");
    return 0x00;
  }

  #ifdef __WIN32__
  /*
   * Unload Winsock 2.0 lib
   */
  WSACleanup();
  #endif

  /*
   * Great succes!
   */
  debug_msg("Socket Close Succesful.\n");
  return 0x01;
}

unsigned char proos_register (void) {
  proos_packet register_packet, register_packet_ans;
  
  /*
   * Do we have a connection?
   */
  if (proos_socket == -1) {
    debug_msg("Socket not yet Initialized. Cannot Register.\n");
    return 0x00;
  }

  /*
   * Set up our packet for registration.
   * Be sure to define protocol first!
   */
  memcpy(register_packet.protocol, "POS", 3);
  register_packet.version = PROOS_VERSION;
  register_packet.client_num = proos_client_number;
  register_packet.command = PROOS_PACKET_CLIENTREG;
  register_packet.data_len = 0;
  register_packet.parity = 0x00;
  register_packet.data = NULL;
  
  /*
   * Send the packet.
   */
  if (!proos_send(&register_packet)) {
    debug_msg("Register Send Failed: Net Problems.\n");
    return 0x00;
  }
  
  /*
   * Receive the answer.
   */
  if (!proos_recv(&register_packet_ans)) {
    debug_msg("Register Ans Failed: Net Problems.\n");
    return 0x00;
  }
  
  /*
   * Is this the right anwer?
   */
  if (register_packet_ans.command != PROOS_PACKET_CLIENTREG_ANSWER) {
    debug_msg("Register Ans Failed: Wrong Answer.\n");
    if (register_packet_ans.data != NULL)
      free(register_packet_ans.data);
    return 0x00;
  }
  
  /*
   * Set the local client number accordingly.
   * Also free up used space.
   */
  proos_client_number = (unsigned char) *register_packet_ans.data;
  free(register_packet_ans.data);
  
  /*
   * Create child process to take care of network traffic.
   */
  proos_network_handler_halt = 0x00;
  pthread_create(&proos_network_handler, NULL, proos_nethandler, NULL);

  /*
   * Great succes!
   */
  debug_msg("Register Succesful.\n");
  return 0x01;
}

unsigned char proos_unregister (void) {
  /*
   * We can only operate when the net connection is active.
   */
  if (proos_socket == -1) {
    debug_msg("Socket Not Initialized. Cannot Unregister.\n");
    return 0x00;
  }

  /*
   * Are we registered in the first place?
   */
  if (proos_client_number == 0) {
    debug_msg("Not Registered. Cannot Unregister.\n");
  }
  
  proos_packet unreg_packet;
  
  /*
   * Build the unregister request.
   */
  memcpy(unreg_packet.protocol, "POS", 3);
  unreg_packet.version = PROOS_VERSION;
  unreg_packet.client_num = proos_client_number;
  unreg_packet.command = PROOS_PACKET_CLIENTUNREG;
  unreg_packet.data_len = 0;
  unreg_packet.parity = 0x00;
  unreg_packet.data = NULL;
  
  /*
   * Send unregister to BOSS.
   */
  if (!proos_send(&unreg_packet)) {
    debug_msg("Unregister Failed.\n");
    return 0x00;
  }

  /*
   * Is the answer what we expected?
   */
  proos_netwait();
  if (proos_answer.command != PROOS_PACKET_CLIENTUNREG_ANSWER) {
    debug_msg("Unregister Failed: Wrong Answer.\n");
    free(proos_answer.data);
    return 0x00;
  }
  if (!*proos_answer.data) {
    debug_msg("Unregister Failed: Returned False.\n");
    free(proos_answer.data);
    return 0x00;
  }

  /*
   * Stop the network handler thread.
   * And free received data.
   */
  free(proos_answer.data);
  proos_network_handler_halt = 0x01;

  /*
   * Great succes!
   */
  debug_msg("Unregister Succesful.\n");
  return 0x01;
}

proos_address proos_alloc (const proos_address id, const unsigned char len) {
  proos_address out;
  proos_packet alloc_packet;
  unsigned char data[5];
  
  /*
   * We can only operate when the net connection is active.
   */
  if (proos_socket == -1) {
    debug_msg("Socket Not Initialized. Cannot Alloc.\n");
    return (proos_address) 0;
  }
  
  /*
   * Data building.
   */
  data[0] = len;
  memcpy(&data[1], &id, 4);

  /*
   * Build registration packet.
   */
  memcpy(alloc_packet.protocol, "POS", 3);
  alloc_packet.version = PROOS_VERSION;
  alloc_packet.client_num = proos_client_number;
  alloc_packet.command = PROOS_PACKET_REGSHARED;
  alloc_packet.data_len = 5;
  alloc_packet.parity = 0x00;
  alloc_packet.data = (char *) data;
  
  /*
   * Send alloc request.
   */
  if (!proos_send(&alloc_packet)) {
    debug_msg("Alloc Failed: Net Problems.\n");
    return (proos_address) 0;
  }
  
  /*
   * Get the answer.
   */
  proos_netwait();
  if (proos_answer.command != PROOS_PACKET_REGSHARED_ANSWER) {
    debug_msg("Alloc Failed: Wrong Anwer.\n");
    free(proos_answer.data);
    return (proos_address) 0;
  }
  
  memcpy(&out, proos_answer.data, 4);
  free(proos_answer.data);
  
  /*
   * Great succes!
   */
  debug_msg("Alloc Succesful.\n");
  return out;
}

unsigned char proos_free (const proos_address id) {
  proos_packet free_packet;
  unsigned char free_data[4];
  
  /*
   * We can only operate when the net connection is active.
   */
  if (proos_socket == -1) {
    debug_msg("Socket Not Initialized. Cannot Free.\n");
    return 0x00;
  }
  
  /*
   * Write packet data block.
   */
  memcpy(free_data, &id, 4);
  
  /*
   * Build the free request packet.
   */
  memcpy(free_packet.protocol, "POS", 3);
  free_packet.version = PROOS_VERSION;
  free_packet.client_num = proos_client_number;
  free_packet.command = PROOS_PACKET_UNREGSHARED;
  free_packet.data_len = 4;
  free_packet.parity = 0x00;
  free_packet.data = (char *) free_data;
  
  /*
   * Send the packet.
   */
  if (!proos_send(&free_packet)) {
    debug_msg("Free Failed: Net Problems.\n");
    return 0x00;
  }
  
  /*
   * Get the answer.
   */
  proos_netwait();
  if (proos_answer.command != PROOS_PACKET_UNREGSHARED_ANSWER) {
    debug_msg("Free Failed: Wrong Answer.\n");
    free(proos_answer.data);
    return 0x00;
  }
  if (!*proos_answer.data) {
    debug_msg("Free Failed: Boss Returned False.\n");
    free(proos_answer.data);
    return 0x00;
  }
  
  /*
   * Great succes!
   */
  debug_msg("Free Succesful.\n");
  free(proos_answer.data);
  return 0x01;
}

unsigned char proos_write (const proos_address address, const char *data, const unsigned char len) {
  proos_packet write_request_packet, write_data_packet;
  unsigned char write_request_data[5];
  
  /*
   * We can only operate when the net connection is active.
   */
  if (proos_socket == -1) {
    debug_msg("Socket Not Initialized. Cannot Write.\n");
    return 0x00;
  }
  
  /*
   * Create data field for request.
   */
  write_request_data[0] = len;
  memcpy(&write_request_data[1], &address, 4);
  
  /*
   * Build the write request packet.
   */
  memcpy(write_request_packet.protocol, "POS", 3);
  write_request_packet.version = PROOS_VERSION;
  write_request_packet.client_num = proos_client_number;
  write_request_packet.command = PROOS_PACKET_MEMWONE;
  write_request_packet.data_len = 5;
  write_request_packet.parity = 0x00;
  write_request_packet.data = (char *) write_request_data;
  
  /*
   * Send the request for write.
   */
  if (!proos_send(&write_request_packet)) {
    debug_msg("Write Failed: Net Problems.\n");
    return 0x00;
  }
  
  /*
   * Get the answer.
   */
  proos_netwait();
  if (proos_answer.command != PROOS_PACKET_MEMWONE_ANSWER) {
    debug_msg("Write Failed: Wrong Answer To Request.\n");
    free(proos_answer.data);
    return 0x00;
  }
  if (!*proos_answer.data) {
    debug_msg("Write Failed: Request Returned False.\n");
    free(proos_answer.data);
    return 0x00;
  }
  free(proos_answer.data);
  
  /*
   * Build the data packet.
   */
  memcpy(write_data_packet.protocol, "POS", 3);
  write_data_packet.version = PROOS_VERSION;
  write_data_packet.client_num = proos_client_number;
  write_data_packet.command = PROOS_PACKET_MEMWTWO;
  write_data_packet.data_len = len;
  write_data_packet.parity = 0x00;
  write_data_packet.data = (char *) data;
  
  /*
   * Send the data packet.
   */
  if (!proos_send(&write_data_packet)) {
    debug_msg("Write Failed: Net Problems.\n");
    return 0x00;
  }
  
  /*
   * Get the answer.
   */
  proos_netwait();
  if (proos_answer.command != PROOS_PACKET_MEMWTWO_ANSWER) {
    debug_msg("Write Failed: Wrong Answer To Write.\n");
    free(proos_answer.data);
    return 0x00;
  }
  if (!*proos_answer.data) {
    debug_msg("Write Failed: Write Returned False.\n");
    free(proos_answer.data);
    return 0x00;
  }
  free(proos_answer.data);

  /*
   * Great succes!
   */
  debug_msg("Write Succesful.\n");
  return 0x01;
}

unsigned char proos_read (const proos_address address, const char *data, const unsigned char len) {
  proos_packet read_packet;
  unsigned char read_data[5];
  
  /*
   * We can only operate when the net connection is active.
   */
  if (proos_socket == -1) {
    debug_msg("Socket Not Initialized. Cannot Read.\n");
    return 0x00;
  }
  
  /*
   * Prepare request data.
   */
  read_data[0] = len;
  memcpy(&read_data[1], &address, 4);
  
  /*
   * Build the read packet.
   */
  memcpy(read_packet.protocol, "POS", 3);
  read_packet.version = PROOS_VERSION;
  read_packet.client_num = proos_client_number;
  read_packet.command = PROOS_PACKET_MEMR;
  read_packet.data_len = 5;
  read_packet.parity = 0x00;
  read_packet.data = (char *) read_data;
  
  /*
   * Send read request.
   */
  if (!proos_send(&read_packet)) {
    debug_msg("Read Failed: Net Problems.\n");
    return 0x00;
  }
  
  /*
   * Get answer.
   */
  proos_netwait();
  if (proos_answer.command != PROOS_PACKET_MEMR_ANSWER) {
    debug_msg("Read Failed: Wrong Answer.\n");
    free(proos_answer.data);
    return 0x00;
  }
  if (!*proos_answer.data) {
    debug_msg("Read Failed: BOSS Returned False.\n");
    free(proos_answer.data);
    return 0x00;
  }
  memcpy((char *) data, &proos_answer.data[1], len);
  free(proos_answer.data);

  /*
   * Great succes!
   */
  debug_msg("Read Succesful.\n");
  return 0x01;
}

unsigned char proos_sem_register (const proos_semid sem_id, const unsigned char sem_val) {
  proos_packet sem_reg_packet;
  unsigned char sem_reg_data[3];
  
  /*
   * We can only operate when the net connection is active.
   */
  if (proos_socket == -1) {
    debug_msg("Socket Not Initialized. Cannot Register Semaphore.\n");
    return 0x00;
  }
  
  /*
   * Prepare the register packet payload.
   */
  sem_reg_data[0] = sem_val;
  memcpy(&sem_reg_data[1], &sem_id, 2);
  
  /*
   * Prepare the register packet.
   */
  memcpy(sem_reg_packet.protocol, "POS", 3);
  sem_reg_packet.version = PROOS_VERSION;
  sem_reg_packet.client_num = proos_client_number;
  sem_reg_packet.command = PROOS_PACKET_REGSEM;
  sem_reg_packet.data_len = 3;
  sem_reg_packet.parity = 0x00;
  sem_reg_packet.data = (char *) sem_reg_data;
  
  /*
   * Send the register packet.
   */
  if (!proos_send(&sem_reg_packet)) {
    debug_msg("Register Semaphore Failed: Net Problems.\n");
    return 0x00;
  }
  
  /*
   * Get the answer.
   */
  proos_netwait();
  if (proos_answer.command != PROOS_PACKET_REGSEM_ANSWER) {
    debug_msg("Register Semaphore Failed: Wrong Answer.\n");
    free(proos_answer.data);
    return 0x00;
  }
  if (!*proos_answer.data) {
    debug_msg("Register Semaphore Failed: BOSS Returned False.\n");
    free(proos_answer.data);
    return 0x00;
  }
  free(proos_answer.data);
  
  /*
   * Great succes!
   */
  debug_msg("Register Semaphore Succesful.\n");
  return 0x01;
}

unsigned char proos_sem_unregister (const proos_semid sem_id) {
  proos_packet sem_unreg_packet;
  unsigned char sem_unreg_data[2];
  
  /*
   * We can only operate when the net connection is active.
   */
  if (proos_socket == -1) {
    debug_msg("Socket Not Initialized. Cannot Unregister Semaphore.\n");
    return 0x00;
  }
  
  /*
   * Prepare unregister payload.
   */
  memcpy(sem_unreg_data, &sem_id, 2);

  /*
   * Prepare unregister request packet.
   */
  memcpy(sem_unreg_packet.protocol, "POS", 3);
  sem_unreg_packet.version = PROOS_VERSION;
  sem_unreg_packet.client_num = proos_client_number;
  sem_unreg_packet.command = PROOS_PACKET_UNREGSEM;
  sem_unreg_packet.data_len = 2;
  sem_unreg_packet.parity = 0x00;
  sem_unreg_packet.data = (char *) sem_unreg_data;
  
  /*
   * Send the request.
   */
  if (!proos_send(&sem_unreg_packet)) {
    debug_msg("Unregister Semaphore Failed: Net Problems.\n");
    return 0x00;
  }
  
  /*
   * Get the answer.
   */
  proos_netwait();
  if (proos_answer.command != PROOS_PACKET_UNREGSEM_ANSWER) {
    debug_msg("Unregister Semaphore Failed: Wrong Answer.\n");
    free(proos_answer.data);
    return 0x00;
  }
  if (!*proos_answer.data) {
    debug_msg("Unregister Semaphore Failed: BOSS Returend False.\n");
    free(proos_answer.data);
    return 0x00;
  }
  free(proos_answer.data);

  /*
   * Great succes!
   */
  debug_msg("Unregister Semaphore Succesful.\n");
  return 0x01;
}

unsigned char proos_sem_p (const proos_semid sem_id) {
  proos_packet sem_p_packet;
  unsigned char sem_p_data[2];
    
  /*
   * We can only operate when the net connection is active.
   */
  if (proos_socket == -1) {
    debug_msg("Socket Not Initialized. Cannot Send Semaphore P.\n");
    return 0x00;
  }
  
  /*
   * Prepare the semaphore data chunk.
   */
  memcpy(sem_p_data, &sem_id, 2);
  
  /*
   * Prepare the semaphore packet.
   */
  memcpy(sem_p_packet.protocol, "POS", 3);
  sem_p_packet.version = PROOS_VERSION;
  sem_p_packet.client_num = proos_client_number;
  sem_p_packet.command = PROOS_PACKET_SEMP;
  sem_p_packet.data_len = 2;
  sem_p_packet.parity = 0x00;
  sem_p_packet.data = (char *) sem_p_data;
  
  /*
   * Send the semaphore packet.
   */
  if (!proos_send(&sem_p_packet)) {
    debug_msg("Semaphore P Failed: Net Problem.\n");
    return 0x00;
  }
  
  /*
   * Get the answer. The proos_netwait() function will do the work
   * for us. We hope the only packet the BOSS sends is the right one.
   */
  proos_netwait();
  if (proos_answer.command != PROOS_PACKET_SEMP_ANSWER) {
    debug_msg("Semaphore P Failed: Wrong Answer.\n");
    free(proos_answer.data);
    return 0x00;
  }
  if (!*proos_answer.data) {
    debug_msg("Semaphore P Failed: BOSS Returned False.\n");
    free(proos_answer.data);
    return 0x00;
  }
  free(proos_answer.data);
  
  /*
   * Great succes!
   */
  debug_msg("Semaphore P Succesful.\n");
  return 0x01;
}

unsigned char proos_sem_v (const proos_semid sem_id) {
  proos_packet sem_v_packet;
  unsigned char sem_v_data[2];
    
  /*
   * We can only operate when the net connection is active.
   */
  if (proos_socket == -1) {
    debug_msg("Socket Not Initialized. Cannot Send Semaphore V.\n");
    return 0x00;
  }
  
  /*
   * Prepare the semaphore data chunk.
   */
  memcpy(sem_v_data, &sem_id, 2);
  
  /*
   * Prepare the semaphore packet.
   */
  memcpy(sem_v_packet.protocol, "POS", 3);
  sem_v_packet.version = PROOS_VERSION;
  sem_v_packet.client_num = proos_client_number;
  sem_v_packet.command = PROOS_PACKET_SEMV;
  sem_v_packet.data_len = 2;
  sem_v_packet.parity = 0x00;
  sem_v_packet.data = (char *) sem_v_data;
  
  /*
   * Send the semaphore packet.
   */
  if (!proos_send(&sem_v_packet)) {
    debug_msg("Semaphore V Failed: Net Problem.\n");
    return 0x00;
  }
  
  /*
   * Get the answer.
   */
  proos_netwait();
  if (proos_answer.command != PROOS_PACKET_SEMV_ANSWER) {
    debug_msg("Semaphore V Failed: Wrong Answer.\n");
    free(proos_answer.data);
    return 0x00;
  }
  if (!*proos_answer.data) {
    debug_msg("Semaphore V Failed: BOSS Returned False.\n");
    free(proos_answer.data);
    return 0x00;
  }
  free(proos_answer.data);
  
  /*
   * Great succes!
   */
  debug_msg("Semaphore V Succesful.\n");
  return 0x01;
}

unsigned char proos_sendmsg (const char *msg) {
  proos_packet msg_packet;
  
  /*
   * We can only operate when the net connection is active.
   */
  if (proos_socket == -1) {
    debug_msg("Socket Not Initialized. Cannot Send Message.\n");
    return 0x00;
  }
  
  /*
   * Prepare the message.
   */
  memcpy(msg_packet.protocol, "POS", 3);
  msg_packet.version = PROOS_VERSION;
  msg_packet.client_num = proos_client_number;
  msg_packet.command = PROOS_PACKET_STDOUT;
  msg_packet.data_len = strlen(msg) + 1;
  msg_packet.parity = 0x00;
  msg_packet.data = (char *) msg;
  
  /*
   * Send the message.
   */
  if (!proos_send(&msg_packet)) {
    debug_msg("Send Message Failed: Net Problems\n");
    return 0x00;
  }
  
  /*
   * Get the answer.
   */
  proos_netwait();
  if (proos_answer.command != PROOS_PACKET_STDOUT_ANSWER) {
    debug_msg("Send Message Failed: Wrong Answer.\n");
    free(proos_answer.data);
    return 0x00;
  }
  if (!*proos_answer.data) {
    debug_msg("Send Message Failed: BOSS Returned False.\n");
    free(proos_answer.data);
    return 0x00;
  }
  free(proos_answer.data);

  /*
   * Great succes!
   */
  debug_msg("Send Message Succesful.\n");
  return 0x01;
}

unsigned char proos_get_client_number (void) {
  return proos_client_number;
}