/* Simple network communication between applications */
/* Created 11.10.2005 T. Milius
   Changed 12.01.2007 T. Milius gethostbyname non blocking */
/* (c) Copyright 2005-2007 by Thomas Milius Stade, Germany
   Source must not be altered without agreement of the owner.
   The owner of the source is allowed to use this code inside programs without
   publishing the code of this programs. These programs may be commercial.
   Other developers can use this source freely inside own software if the source
   code of this programs is made public to same conditions like valid to this code
   and no commercial profit is taken from the programs based on this code

   Code or parts of it are not allowed to be used within GPL code or
   similar licenses which are "infecting" other code and trying to "supersede"
   other licenses. */
/* TCP/IP ANSI-C */

#ifndef network_h
#define network_h

/* !!!!!!!!!! libraries !!!!!!!!!! */
/* ---------- ANSI-C ---------- */

/* ---------- TCP/IP ---------- */
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <netdb.h>
#include <socklib.h>
#include <unixlib.h>
#include <sys/errno.h>
#include <sys/ioctl.h>
#include <arpa/inet.h>

/* !!!!!!!!!!! definitions !!!!!!!!!! */
#define NETWORK_JOB_STATUS_INACTIVE  0
#define NETWORK_JOB_STATUS_ERROR     1
#define NETWORK_JOB_STATUS_FINISHED  2
#define NETWORK_JOB_STATUS_SENDING   3
#define NETWORK_JOB_STATUS_RECEIVING 4
#define NETWORK_JOB_STATUS_DNS       5
/* For optional use by other functions to
   indicate a complete transfer (request and response) */
#define NETWORK_JOB_STATUS_DONE      6

#define NETWORK_PORT 9152

#define NO_SOCKET -1

#define NETWORK_BUFFER_SIZE 2048

#define NETWORK_MAX_HOST_NAME_LENGTH 256

#define NETWORK_MESSAGE_ELEMENT_TYPE_VALUE      0
#define NETWORK_MESSAGE_ELEMENT_TYPE_BLOCK      1
#define NETWORK_MESSAGE_ELEMENT_TYPE_STRING     2

/* !!!!!!!!!! data structures !!!!!!!!!! */
struct network_job_struct {
struct network_job_struct *previous_job;
struct network_job_struct *next_job;
void (*final_function) (struct network_job_struct *);
unsigned long long identification;
unsigned long status;
int socket;
/* For non-blocking DNS */
char host_name[NETWORK_MAX_HOST_NAME_LENGTH];
unsigned long actual_transfered_size;
unsigned long message_size;
unsigned long buffer_size;
void *buffer;
};

struct network_message_element_block_struct {
unsigned long size;
void *pointer;
};

struct network_message_element_struct {
unsigned long type;
union {
  unsigned long value;
  struct network_message_element_block_struct block;
  } details;
};

/* Format of messages:
   Size  Description
   ----  -----------
   4     Total_size           (mandatory, size bytes included)
   4     Number of elements   (mandatory)
   ----------------------- From here on depending on program
   Elements this can be either
   - Value (4 Bytes)
   - Blocks:
     4 Bytes size of data (size bytes excluded)
     followed by Bytes of data as specified

   A message requires transmisision of at least 4 Bytes (the size).
   All integer values are stored as big endian. */

struct {
struct network_job_struct *first_network_job;
unsigned long netmask;
unsigned long netallowed;
int network_port;
int socket;
void (*final_function) (struct network_job_struct *);
FILE *trace_file;
} network_common;

/* !!!!!!!!!! support functions !!!!!!!!!! */
void network_job_drop(struct network_job_struct *job)
{

if (network_common.trace_file) {
  fprintf(network_common.trace_file,
          "network_job_drop(%lx)\n",
          (unsigned long) job);
  fflush(network_common.trace_file);
  }
if (!job) return;
if (job->previous_job) {
  job->previous_job->next_job=job->next_job;
  }
else {
  network_common.first_network_job=job->next_job;
  }
if (job->next_job) {
  job->next_job->previous_job=job->previous_job;
  }
if (job->socket != NO_SOCKET) {
  socketclose(job->socket);
  }
free(job);
}

struct network_job_struct *network_job_add(void *buffer,
                                           unsigned long max_buffer_size,
                                           unsigned long long identification)
{
struct network_job_struct *job;

if (network_common.trace_file) {
  fprintf(network_common.trace_file,
          "network_job_add(%lx, %ld, %lld)\n",
          (unsigned long) buffer,
          max_buffer_size,
          identification);
  fflush(network_common.trace_file);
  }
if (buffer) {
  if ((job=malloc(sizeof(struct network_job_struct))) == NULL) {
    return NULL;
    }
  job->buffer=buffer;
  }
else {
  /* If no buffer is given create it at the end of the job structure */
  if ((job=malloc(sizeof(struct network_job_struct) + max_buffer_size)) == NULL) {
    return NULL;
    }
  job->buffer=(void *) ((unsigned long) job + sizeof(struct network_job_struct));
  }
job->previous_job=NULL;
job->next_job=network_common.first_network_job;
if (network_common.first_network_job) {
  network_common.first_network_job->previous_job=job;
  }
network_common.first_network_job=job;
job->status=NETWORK_JOB_STATUS_INACTIVE;
job->socket=NO_SOCKET;
job->buffer_size=max_buffer_size;
job->final_function=NULL;
job->identification=identification;
if (network_common.trace_file) {
  fprintf(network_common.trace_file,
          "New job %lx\n",
          (unsigned long) job);
  fflush(network_common.trace_file);
  }
return job;
}

/* Necassary to handle various long data inside messages.
   1. Byte is at the given address */
void store_long_unaligned(void *buffer,
                          unsigned long value)
{

*((unsigned char *) buffer)=(unsigned char) (value & 0x000000FF);
*((unsigned char *) ((unsigned long) buffer + 1))=(unsigned char) ((value>>8) & 0x000000FF);
*((unsigned char *) ((unsigned long) buffer + 2))=(unsigned char) ((value>>16) & 0x000000FF);
*((unsigned char *) ((unsigned long) buffer + 3))=(unsigned char) ((value>>24) & 0x000000FF);
}

unsigned long read_long_unaligned(void *buffer)
{
unsigned long value;

value=(unsigned long) (*((unsigned char *) buffer));
value|=(unsigned long) (*((unsigned char *) ((unsigned long) buffer + 1)))<<8;
value|=(unsigned long) (*((unsigned char *) ((unsigned long) buffer + 2)))<<16;
value|=(unsigned long) (*((unsigned char *) ((unsigned long) buffer + 3)))<<24;
return value;
}

/* !!!!!!!!!! functions !!!!!!!!!! */
bool network_initialize(char *network,
                        char *netmask,
                        int port,
                        void (*final_function) (struct network_job_struct *),
                        char *trace_file_name)
{
int i;
struct sockaddr_in server;

network_common.final_function=final_function;
network_common.first_network_job=NULL;
network_common.trace_file=NULL;
network_common.socket=NO_SOCKET;
if (port != 0) {
  network_common.network_port=port;
  }
else {
  network_common.network_port=NETWORK_PORT;
  }
network_common.netmask=inet_addr(netmask);
network_common.netallowed=inet_addr(netmask) & inet_addr(network);
if (inet_addr(network) == 0) return true;
if (trace_file_name) {
  network_common.trace_file=fopen(trace_file_name, "w");
  }
if (network_common.trace_file) {
  fprintf(network_common.trace_file,
          "network_initialize(%s, %s, %d, ..., %s)\n",
          network,
          netmask,
          port,
          trace_file_name);
  fflush(network_common.trace_file);
  }
if ((network_common.socket=socket(AF_INET,
                                  SOCK_STREAM,
                                  0)) < 0) {
  if (network_common.trace_file) {
    fprintf(network_common.trace_file,
            "Listening on port %d failed\n",
            network_common.network_port);
    fflush(network_common.trace_file);
    }
  network_common.socket=NO_SOCKET;
  return false;
  }
if (network_common.trace_file) {
  fprintf(network_common.trace_file,
          "Listening socket %d for port %d\n",
          network_common.socket,
          network_common.network_port);
  fflush(network_common.trace_file);
  }
server.sin_family=AF_INET;
server.sin_addr.s_addr=INADDR_ANY;
server.sin_port=htons(network_common.network_port);
if (bind(network_common.socket,
         (struct sockaddr *) &server,
         sizeof(server))) {
  if (network_common.trace_file) {
    fprintf(network_common.trace_file,
            "Binding failed\n");
    fflush(network_common.trace_file);
    }
  socketclose(network_common.socket);
  network_common.socket=NO_SOCKET;
  return false;
  }
i=1;
if (socketioctl(network_common.socket,
                FIOSLEEPTW,
                &i) < 0) {
  if (network_common.trace_file) {
    fprintf(network_common.trace_file,
            "Non blocking failed\n");
    fflush(network_common.trace_file);
    }
  socketclose(network_common.socket);
  network_common.socket=NO_SOCKET;
  return false;
  }
listen(network_common.socket, 5);
return true;
}

bool network_shutdown(void)
{

if (network_common.trace_file) {
  fprintf(network_common.trace_file,
          "network_shutdown()");
  fflush(network_common.trace_file);
  }
while(network_common.first_network_job) {
  network_job_drop(network_common.first_network_job);
  }
if (network_common.socket != NO_SOCKET) {
  socketclose(network_common.socket);
  }
network_common.socket=NO_SOCKET;
if (network_common.trace_file) {
  fclose(network_common.trace_file);
  network_common.trace_file=NULL;
  }
return true;
}

bool network_connection_establish(struct network_job_struct *job,
                                  char *host_name)
{

if (network_common.trace_file) {
  fprintf(network_common.trace_file,
          "network_connection_establish(%lx, %s)\n",
          (unsigned long) job,
          host_name);
  fflush(network_common.trace_file);
  }
if (job->socket != NO_SOCKET) return false;
job->socket=socket(AF_INET,
                   SOCK_STREAM,
                   0);
if (job->socket < 0) {
  if (network_common.trace_file) {
    fprintf(network_common.trace_file,
            "No socket %d\n",
            job->socket);
    fflush(network_common.trace_file);
    }
  job->socket=NO_SOCKET;
  return false;
  }
/* Must be kept temporary because of polling */
strncpy(job->host_name, host_name, NETWORK_MAX_HOST_NAME_LENGTH - 1);
job->host_name[NETWORK_MAX_HOST_NAME_LENGTH - 1]='\0';
job->status=NETWORK_JOB_STATUS_DNS;
return true;
}

bool network_connection_close(struct network_job_struct *job,
                              unsigned long status)
{

if (network_common.trace_file) {
  fprintf(network_common.trace_file,
          "network_connection_close(%lx, %ld)\n",
          (unsigned long) job,
          status);
  fflush(network_common.trace_file);
  }
if (job->socket != NO_SOCKET) {
  socketclose(job->socket);
  job->socket=NO_SOCKET;
  }
job->status=status;
return true;
}

bool network_process(void)
{
int i;
int new_socket;
int bytes_transfered;
int activity_flag;
fd_set ready;
struct timeval to;
struct network_job_struct *job;
struct sockaddr_in cli_addr;

/* No network activities in this area */
if (network_common.socket == NO_SOCKET) return false;
activity_flag=false;
/* Accept new connections */
FD_ZERO(&ready);
FD_SET(network_common.socket,
       &ready);
to.tv_sec=0;
to.tv_usec=1;
if (select(network_common.socket + 1,
           &ready,
           0,
           0,
           &to) >= 0) {
  if (FD_ISSET(network_common.socket,
               &ready)) {
    new_socket=accept(network_common.socket,
                      NULL,
                      NULL);
    if (new_socket != -1) {
      if (network_common.trace_file) {
        fprintf(network_common.trace_file,
                "request accept on socket %d\n",
                new_socket);
        fflush(network_common.trace_file);
        }
      /* Test whether it is an request from an accepted machine */
      i=sizeof cli_addr;
      getpeername(new_socket, (struct sockaddr *) &cli_addr, &i);
      if ((cli_addr.sin_addr.s_addr & network_common.netmask) != network_common.netallowed) {
        /* Reject unwanted connections by simply ignoring them */
        if (network_common.trace_file) {
          fprintf(network_common.trace_file,
                  "Illegal request from %s rejected\n",
                  inet_ntoa(cli_addr.sin_addr));
          fflush(network_common.trace_file);
          }
        socketclose(new_socket);
        }
      else {
        /* Non Blocking */
        i=1;
        if (socketioctl(new_socket, FIONBIO, &i) < 0) {
          if (network_common.trace_file) {
            fprintf(network_common.trace_file,
                    "Non blocking failed\n");
            fflush(network_common.trace_file);
            }
          socketclose(new_socket);
          }
        else {
          /* Setup new receiving job */
          if ((job=network_job_add(NULL,
                                   NETWORK_BUFFER_SIZE,
                                   0)) != NULL) {
            job->socket=new_socket;
            job->status=NETWORK_JOB_STATUS_RECEIVING;
            job->actual_transfered_size=0;
            job->message_size=0;
            job->final_function=network_common.final_function;
            activity_flag=true;
            }
          else {
            socketclose(new_socket);
            }
          }
        }
      }
    }
  }
else {
  if (network_common.trace_file) {
    fprintf(network_common.trace_file,
            "network_process select failure\n");
    fflush(network_common.trace_file);
    }
  network_shutdown();
  }
/* Process pending Jobs */
job=network_common.first_network_job;
while (job) {
  switch(job->status) {
    case NETWORK_JOB_STATUS_SENDING: {
      FD_ZERO(&ready);
      FD_SET(job->socket, &ready);
      to.tv_sec=0;
      to.tv_usec=1;
      if (select(job->socket + 1,
                 NULL,
                 &ready,
                 NULL,
                 &to) < 0) {
        if (network_common.trace_file) {
          fprintf(network_common.trace_file,
                  "network_process select failure for sending job %lx\n",
                  (unsigned long) job);
          fflush(network_common.trace_file);
          }
        if (job->final_function) {
          network_connection_close(job,
                                   NETWORK_JOB_STATUS_ERROR);
          (*job->final_function)(job);
          }
        else {
          network_job_drop(job);
          }
        }
      else {
        if (FD_ISSET(job->socket,
                     &ready)) {
          activity_flag=true;
          if (job->actual_transfered_size == 0) {
            store_long_unaligned(job->buffer,
                                 job->message_size);
            }
          if ((bytes_transfered=socketwrite(job->socket,
                                            (void *) ((unsigned long) job->buffer + job->actual_transfered_size),
                                            job->message_size - job->actual_transfered_size)) <= 0) {
            if (network_common.trace_file) {
              fprintf(network_common.trace_file,
                      "network_process transmission problem for sending job %lx\n",
                      (unsigned long) job);
              fflush(network_common.trace_file);
              }
            if (job->final_function) {
              network_connection_close(job,
                                       NETWORK_JOB_STATUS_ERROR);
              (*job->final_function)(job);
              }
            else {
              network_job_drop(job);
              }
            }
          else {
            if (network_common.trace_file) {
              if (bytes_transfered > 0) {
                fprintf(network_common.trace_file,
                        "network_process %d bytes transfered for sending job %lx\n",
                        bytes_transfered,
                        (unsigned long) job);
                fflush(network_common.trace_file);
                }
              }
            job->actual_transfered_size+=bytes_transfered;
            if (job->actual_transfered_size >= job->message_size) {
              /* Message sent */
              if (network_common.trace_file) {
                fprintf(network_common.trace_file,
                        "network_process sending job %lx finished\n",
                        (unsigned long) job);
                fflush(network_common.trace_file);
                }
              if (job->final_function) {
                /* Keep connection open for reply but change status */
                job->status=NETWORK_JOB_STATUS_FINISHED;
                (*job->final_function)(job);
                }
              else {
                network_job_drop(job);
                }
              }
            }
          }
        }
      }
    break;
    case NETWORK_JOB_STATUS_RECEIVING: {
      FD_ZERO(&ready);
      FD_SET(job->socket, &ready);
      to.tv_sec=0;
      to.tv_usec=1;
      if (select(job->socket + 1,
                 &ready,
                 NULL,
                 NULL,
                 &to) < 0) {
        if (network_common.trace_file) {
          fprintf(network_common.trace_file,
                  "network_process select failure for receiving job %lx\n",
                  (unsigned long) job);
          fflush(network_common.trace_file);
          }
        if (job->final_function) {
          network_connection_close(job,
                                   NETWORK_JOB_STATUS_ERROR);
          (*job->final_function)(job);
          }
        else {
          network_job_drop(job);
          }
        }
      else {
        if (FD_ISSET(job->socket,
                     &ready)) {
          activity_flag=true;
          if ((bytes_transfered=socketread(job->socket,
                                           (void *) ((unsigned long) job->buffer + job->actual_transfered_size),
                                           job->buffer_size - job->actual_transfered_size)) <= 0) {
            if (network_common.trace_file) {
              fprintf(network_common.trace_file,
                      "network_process transmission problem for receiving job %lx\n",
                      (unsigned long) job);
              fflush(network_common.trace_file);
              }
            if (job->final_function) {
              network_connection_close(job,
                                       NETWORK_JOB_STATUS_ERROR);
              (*job->final_function)(job);
              }
            else {
              network_job_drop(job);
              }
            }
          else {
            if (network_common.trace_file) {
              if (bytes_transfered > 0) {
                fprintf(network_common.trace_file,
                        "network_process %d bytes transfered for receiving job %lx\n",
                        bytes_transfered,
                        (unsigned long) job);
                fflush(network_common.trace_file);
                }
              }
            job->actual_transfered_size+=bytes_transfered;
            if ((job->actual_transfered_size >= sizeof(unsigned long)) &&
                (job->message_size == 0)) {
              job->message_size=read_long_unaligned(job->buffer);
              if (network_common.trace_file) {
                fprintf(network_common.trace_file,
                        "network_process message size of receiving job %lx is %ld\n",
                        (unsigned long) job,
                        job->message_size);
                fflush(network_common.trace_file);
                }
              }
            if (job->actual_transfered_size >= job->message_size) {
              /* Message received */
              if (network_common.trace_file) {
                fprintf(network_common.trace_file,
                        "network_process receiving job %lx finished\n",
                        (unsigned long) job);
                fflush(network_common.trace_file);
                }
              if (job->final_function) {
                /* Keep connection open for reply but change status */
                job->status=NETWORK_JOB_STATUS_FINISHED;
                (*job->final_function)(job);
                }
              else {
                network_job_drop(job);
                }
              }
            }
          }
        }
      }
    break;
    case NETWORK_JOB_STATUS_DNS: {
      struct sockaddr_in server;
      struct hostent *hp;
      _kernel_swi_regs regs;
      _kernel_oserror *os_error;

      /* Wandle Rechner-Name in IP-Adresse */
      /* Multitasking-Ersatz fr gethostbyname */
      regs.r[0]=(int) job->host_name;
      if ((os_error=_kernel_swi(0x00046001, &regs, &regs)) != NULL) {
        network_connection_close(job,
                                 NETWORK_JOB_STATUS_ERROR);
        }
      else {
        hp=(struct hostent *) regs.r[1];
        if (!hp) {
          if (regs.r[0] != 36) {
            /* Fehler */
            if (network_common.trace_file) {
              fprintf(network_common.trace_file,
                      "DNS problem\n");
              fflush(network_common.trace_file);
              }
            network_connection_close(job,
                                     NETWORK_JOB_STATUS_ERROR);
            }
          else {
            /* Noch nicht da */
            activity_flag=true;
            }
          }
        else {
          server.sin_family=AF_INET;
          bcopy(hp->h_addr,
                &server.sin_addr,
                hp->h_length);
          server.sin_port=htons(network_common.network_port);
          if (connect(job->socket,
                      (struct sockaddr *) &server,
                      sizeof(server)) < 0) {
            if (network_common.trace_file) {
              fprintf(network_common.trace_file,
                      "Connection to port %d failed\n",
                      network_common.network_port);
              fflush(network_common.trace_file);
              }
            network_connection_close(job,
                                     NETWORK_JOB_STATUS_ERROR);
            }
          else {
            /* Non Blocking. But not earlier to simplify */
            i=1;
            if (socketioctl(job->socket,
                            FIONBIO,
                            &i) < 0) {
              if (network_common.trace_file) {
                fprintf(network_common.trace_file,
                        "Non blocking of port %d failed\n",
                        network_common.network_port);
                fflush(network_common.trace_file);
                }
              network_connection_close(job,
                                       NETWORK_JOB_STATUS_ERROR);
              }
            else {
              /* Initiate Transfer */
              job->actual_transfered_size=0;
              job->status=NETWORK_JOB_STATUS_SENDING;
              activity_flag=true;
              }
            }
          }
        }
      }
    break;
    }
  job=job->next_job;
  }
return activity_flag;
}

bool network_build_message(struct network_job_struct *job,
                           struct network_message_element_struct *element,
                           unsigned long number_of_elements)
{
int i;
void *buffer_pos;

if (network_common.trace_file) {
  fprintf(network_common.trace_file,
          "network_build_message(%lx, %lx, %ld)\n",
          (unsigned long) job,
          (unsigned long) element,
          number_of_elements);
  fflush(network_common.trace_file);
  }
if ((!job) || (!element)) {
  if (network_common.trace_file) {
    fprintf(network_common.trace_file,
            "Wrong parameters!\n");
    fflush(network_common.trace_file);
    }
  return false;
  }
if (number_of_elements < 1) {
  if (network_common.trace_file) {
    fprintf(network_common.trace_file,
            "No elements!\n");
    fflush(network_common.trace_file);
    }
  return false;
  }
job->message_size=0;
buffer_pos=job->buffer;
if (!buffer_pos) {
  if (network_common.trace_file) {
    fprintf(network_common.trace_file,
            "No buffer!\n");
    fflush(network_common.trace_file);
    }
  return false;
  }
/* reserve space for total message size */
job->message_size+=sizeof(unsigned long);
if (job->message_size > job->buffer_size) {
  if (network_common.trace_file) {
    fprintf(network_common.trace_file,
            "Buffer to small!\n");
    fflush(network_common.trace_file);
    }
  job->message_size=0;
  return false;
  }
buffer_pos=(void *)((unsigned long) buffer_pos + sizeof(unsigned long));
/* store number of parameters */
job->message_size+=sizeof(unsigned long);
if (job->message_size > job->buffer_size) {
  if (network_common.trace_file) {
    fprintf(network_common.trace_file,
            "Buffer to small!\n");
    fflush(network_common.trace_file);
    }
  job->message_size=0;
  return false;
  }
store_long_unaligned(buffer_pos,
                     number_of_elements);
buffer_pos=(void *)((unsigned long) buffer_pos + sizeof(unsigned long));
i=0;
while (i < number_of_elements) {
  if (network_common.trace_file) {
    fprintf(network_common.trace_file,
            "Element %d:\n",
            i);
    fflush(network_common.trace_file);
    }
  switch(element[i].type) {
    case NETWORK_MESSAGE_ELEMENT_TYPE_VALUE: {
      if (network_common.trace_file) {
        fprintf(network_common.trace_file,
                "Value\nContent: %lx\n",
                element[i].details.value);
        fflush(network_common.trace_file);
        }
      job->message_size+=sizeof(unsigned long);
      if (job->message_size > job->buffer_size) {
        if (network_common.trace_file) {
          fprintf(network_common.trace_file,
                  "Buffer to small!\n");
          fflush(network_common.trace_file);
          }
        job->message_size=0;
        return false;
        }
      store_long_unaligned(buffer_pos,
                           element[i].details.value);
      buffer_pos=(void *)((unsigned long) buffer_pos + sizeof(unsigned long));
      }
    break;
    case NETWORK_MESSAGE_ELEMENT_TYPE_BLOCK: {
      if (network_common.trace_file) {
        fprintf(network_common.trace_file,
                "Block\nSize: %ld\n",
                element[i].details.block.size);
        fflush(network_common.trace_file);
        }
      job->message_size+=sizeof(unsigned long);
      if (job->message_size > job->buffer_size) {
        if (network_common.trace_file) {
          fprintf(network_common.trace_file,
                  "Buffer to small!\n");
          fflush(network_common.trace_file);
          }
        job->message_size=0;
        return false;
        }
      store_long_unaligned(buffer_pos,
                           element[i].details.block.size);
      buffer_pos=(void *)((unsigned long) buffer_pos + sizeof(unsigned long));
      /* Block may have zero size */
      if (element[i].details.block.size > 0) {
        job->message_size+=element[i].details.block.size;
        if (job->message_size > job->buffer_size) {
          if (network_common.trace_file) {
            fprintf(network_common.trace_file,
                    "Buffer to small!\n");
            fflush(network_common.trace_file);
            }
          job->message_size=0;
          return false;
          }
        if (element[i].details.block.pointer) {
          memcpy(buffer_pos, element[i].details.block.pointer, element[i].details.block.size);
          }
        else {
          if (network_common.trace_file) {
            fprintf(network_common.trace_file,
                    "NULL pointer!\n");
            fflush(network_common.trace_file);
            }
          job->message_size=0;
          return false;
          }
        buffer_pos=(void *)((unsigned long) buffer_pos + element[i].details.block.size);
        }
      }
    break;
    case NETWORK_MESSAGE_ELEMENT_TYPE_STRING: {
      if (network_common.trace_file) {
        fprintf(network_common.trace_file,
                "String\n");
        fflush(network_common.trace_file);
        }
      job->message_size+=sizeof(unsigned long);
      if (job->message_size > job->buffer_size) {
        if (network_common.trace_file) {
          fprintf(network_common.trace_file,
                  "Buffer to small!\n");
          fflush(network_common.trace_file);
          }
        job->message_size=0;
        return false;
        }
      if (!element[i].details.block.pointer) {
        if (network_common.trace_file) {
          fprintf(network_common.trace_file,
                  "NULL pointer!\n");
          fflush(network_common.trace_file);
          }
        job->message_size=0;
        return false;
        }
      /* Determine string size */
      element[i].details.block.size=strlen(element[i].details.block.pointer);
      if (network_common.trace_file) {
        fprintf(network_common.trace_file,
                "Size: %ld\n",
                element[i].details.block.size);
        if (element[i].details.block.size > 0) {
          fprintf(network_common.trace_file,
                  "Content: %s\n",
                  element[i].details.block.pointer);
          }
        fflush(network_common.trace_file);
        }
      store_long_unaligned(buffer_pos,
                           element[i].details.block.size);
      buffer_pos=(void *)((unsigned long) buffer_pos + sizeof(unsigned long));
      /* Block may have zero size */
      if (element[i].details.block.size > 0) {
        job->message_size+=element[i].details.block.size;
        if (job->message_size > job->buffer_size) {
          if (network_common.trace_file) {
            fprintf(network_common.trace_file,
                    "Buffer to small!\n");
            fflush(network_common.trace_file);
            }
          job->message_size=0;
          return false;
          }
        if (element[i].details.block.pointer) {
          memcpy(buffer_pos, element[i].details.block.pointer, element[i].details.block.size);
          }
        buffer_pos=(void *)((unsigned long) buffer_pos + element[i].details.block.size);
        }
      }
    break;
    default: {
      if (network_common.trace_file) {
        fprintf(network_common.trace_file,
                "Invalid type: %ld!\n",
                element[i].type);
        fflush(network_common.trace_file);
        }
      return false;
      }
    }
  i++;
  }
if (network_common.trace_file) {
  fprintf(network_common.trace_file,
          "Message size: %ld\n",
          job->message_size);
  fflush(network_common.trace_file);
  }
return true;
}

unsigned long network_extract_message(struct network_job_struct *job,
                                      struct network_message_element_struct *element,
                                      unsigned long max_number_of_elements,
                                      unsigned long first_element,
                                      unsigned long *number_of_elements)
{
int i;
unsigned long message_size;
unsigned long local_block_size;
unsigned long block_size;
void *buffer_pos;

if (network_common.trace_file) {
  fprintf(network_common.trace_file,
          "network_extract_message(%lx, %lx, %ld, %ld, ...)\n",
          (unsigned long) job,
          (unsigned long) element,
          max_number_of_elements,
          first_element);
  fflush(network_common.trace_file);
  }
if (number_of_elements) {
  *number_of_elements=0;
  }
if ((!job) || (!element)) {
  if (network_common.trace_file) {
    fprintf(network_common.trace_file,
            "Wrong parameters!\n");
    if (number_of_elements) {
      fprintf(network_common.trace_file,
              "Number of message elements: 0\n");
      }
    fprintf(network_common.trace_file,
            "Number of extracted elements: 0\n");
    fflush(network_common.trace_file);
    }
  return 0;
  }
buffer_pos=job->buffer;
if (!buffer_pos) {
  if (network_common.trace_file) {
    fprintf(network_common.trace_file,
            "No buffer!\n");
    if (number_of_elements) {
      fprintf(network_common.trace_file,
              "Number of message elements: 0\n");
      }
    fprintf(network_common.trace_file,
            "Number of extracted elements: 0\n");
    fflush(network_common.trace_file);
    }
  return 0;
  }
message_size=0;
/* job message size must be the same as received */
message_size+=sizeof(unsigned long);
if (message_size > job->message_size) {
  if (network_common.trace_file) {
    fprintf(network_common.trace_file,
            "Message to small (no size at all)!\n");
    if (number_of_elements) {
      fprintf(network_common.trace_file,
              "Number of message elements: 0\n");
      }
    fprintf(network_common.trace_file,
            "Number of extracted elements: 0\n");
    fflush(network_common.trace_file);
    }
  return 0;
  }
buffer_pos=(void *)((unsigned long) buffer_pos + sizeof(unsigned long));
/* extract number of received parameters */
message_size+=sizeof(unsigned long);
if (message_size > job->message_size) {
  if (network_common.trace_file) {
    fprintf(network_common.trace_file,
            "Message to small (no number of parameters)!\n");
    if (number_of_elements) {
      fprintf(network_common.trace_file,
              "Number of message elements: 0\n");
      }
    fprintf(network_common.trace_file,
            "Number of extracted elements: 0\n");
    fflush(network_common.trace_file);
    }
  return 0;
  }
i=read_long_unaligned(buffer_pos);
if (number_of_elements) {
  *number_of_elements=i;
  }
if (network_common.trace_file) {
  fprintf(network_common.trace_file,
          "Message parameters %d\n",
          i);
  fflush(network_common.trace_file);
  }
if (i < max_number_of_elements) {
  max_number_of_elements=i;
  }
buffer_pos=(void *)((unsigned long) buffer_pos + sizeof(unsigned long));
i=0;
while (i < max_number_of_elements) {
  if (network_common.trace_file) {
    fprintf(network_common.trace_file,
            "Element %d:\n",
            i);
    fflush(network_common.trace_file);
    }
  switch(element[i].type) {
    case NETWORK_MESSAGE_ELEMENT_TYPE_VALUE: {
      if (network_common.trace_file) {
        fprintf(network_common.trace_file,
                "Value\n");
        fflush(network_common.trace_file);
        }
      message_size+=sizeof(unsigned long);
      if (message_size > job->message_size) {
        if (network_common.trace_file) {
          fprintf(network_common.trace_file,
                  "Message to small!\n");
          if (number_of_elements) {
            fprintf(network_common.trace_file,
                    "Number of message elements: %ld\n",
                    *number_of_elements);
            }
          fprintf(network_common.trace_file,
                 "Number of extracted elements: 0\n");
          fflush(network_common.trace_file);
          }
        return 0;
        }
      if (i >= first_element) {
        element[i].details.value=read_long_unaligned(buffer_pos);
        if (network_common.trace_file) {
          fprintf(network_common.trace_file,
                  "Content: %lx\n",
                  element[i].details.value);
          fflush(network_common.trace_file);
          }
        }
      buffer_pos=(void *)((unsigned long) buffer_pos + sizeof(unsigned long));
      }
    break;
    case NETWORK_MESSAGE_ELEMENT_TYPE_BLOCK: {
      if (network_common.trace_file) {
        fprintf(network_common.trace_file,
                "Block\n");
        fflush(network_common.trace_file);
        }
      message_size+=sizeof(unsigned long);
      if (message_size > job->message_size) {
        if (network_common.trace_file) {
          fprintf(network_common.trace_file,
                  "Message to small!\n");
          if (number_of_elements) {
            fprintf(network_common.trace_file,
                    "Number of message elements: %ld\n",
                    *number_of_elements);
            }
          fprintf(network_common.trace_file,
                 "Number of extracted elements: 0\n");
          fflush(network_common.trace_file);
          }
        return 0;
        }
      block_size=read_long_unaligned(buffer_pos);
      if (i >= first_element) {
        local_block_size=block_size;
        if ((element[i].details.block.size != 0) &&
            (local_block_size > element[i].details.block.size)) {
          local_block_size=element[i].details.block.size;
          }
        element[i].details.block.size=block_size;
        if (network_common.trace_file) {
          fprintf(network_common.trace_file,
                  "Size: %ld\n",
                  element[i].details.block.size);
          fflush(network_common.trace_file);
          }
        }
      buffer_pos=(void *)((unsigned long) buffer_pos + sizeof(unsigned long));
      message_size+=block_size;
      if (message_size > job->message_size) {
        if (network_common.trace_file) {
          fprintf(network_common.trace_file,
                  "Message to small!\n");
          if (number_of_elements) {
            fprintf(network_common.trace_file,
                    "Number of message elements: %ld\n",
                    *number_of_elements);
            }
          fprintf(network_common.trace_file,
                 "Number of extracted elements: 0\n");
          fflush(network_common.trace_file);
          }
        return 0;
        }
      if (i >= first_element) {
        /* Block may have zero size */
        if (local_block_size > 0) {
          if (element[i].details.block.pointer) {
            /* Copy content of message block to memory */
            memcpy(element[i].details.block.pointer, buffer_pos, local_block_size);
            }
          else {
            /* No pointer given, so fill with pointer to messageblock */
            element[i].details.block.pointer=buffer_pos;
            }
          }
        }
      buffer_pos=(void *)((unsigned long) buffer_pos + block_size);
      }
    break;
    case NETWORK_MESSAGE_ELEMENT_TYPE_STRING: {
      if (network_common.trace_file) {
        fprintf(network_common.trace_file,
                "String\n");
        fflush(network_common.trace_file);
        }
      message_size+=sizeof(unsigned long);
      if (message_size > job->message_size) {
        if (network_common.trace_file) {
          fprintf(network_common.trace_file,
                  "Message to small!\n");
          if (number_of_elements) {
            fprintf(network_common.trace_file,
                    "Number of message elements: %ld\n",
                    *number_of_elements);
            }
          fprintf(network_common.trace_file,
                 "Number of extracted elements: 0\n");
          fflush(network_common.trace_file);
          }
        return 0;
        }
      block_size=read_long_unaligned(buffer_pos);
      if (i >= first_element) {
        local_block_size=block_size;
        /* If given buffer size is 0 this indicates no restrictions
           eg in case that memory is allocated by caller. */
        if ((element[i].details.block.size != 0) &&
            (local_block_size >= element[i].details.block.size)) {
          local_block_size=(element[i].details.block.size - 1);
          }
        element[i].details.block.size=block_size;
        if (network_common.trace_file) {
          fprintf(network_common.trace_file,
                  "Size: %ld\n",
                  element[i].details.block.size);
          fflush(network_common.trace_file);
          }
        }
      buffer_pos=(void *)((unsigned long) buffer_pos + sizeof(unsigned long));
      message_size+=block_size;
      if (message_size > job->message_size) {
        if (network_common.trace_file) {
          fprintf(network_common.trace_file,
                  "Message to small!\n");
          if (number_of_elements) {
            fprintf(network_common.trace_file,
                    "Number of message elements: %ld\n",
                    *number_of_elements);
            }
          fprintf(network_common.trace_file,
                 "Number of extracted elements: 0\n");
          fflush(network_common.trace_file);
          }
        return 0;
        }
      if (i >= first_element) {
        /* Block may have zero size */
        if (local_block_size > 0) {
          if (element[i].details.block.pointer) {
            /* Copy content of message block to memory */
            memcpy(element[i].details.block.pointer, buffer_pos, local_block_size);
            /* Terminate */
            ((char *) element[i].details.block.pointer)[local_block_size]='\0';
            if (network_common.trace_file) {
              fprintf(network_common.trace_file,
                      "Content: %s\n",
                      (char *) element[i].details.block.pointer);
              fflush(network_common.trace_file);
              }
            }
          else {
            /* No pointer given, so fill with pointer to messageblock */
            element[i].details.block.pointer=buffer_pos;
            }
          }
        else {
          if (element[i].details.block.pointer) {
            /* Terminate */
            ((char *) element[i].details.block.pointer)[0]='\0';
            }
          }
        }
      buffer_pos=(void *)((unsigned long) buffer_pos + block_size);
      }
    break;
    default: {
      if (network_common.trace_file) {
        fprintf(network_common.trace_file,
                "Invalid type: %ld!\n",
                element[i].type);
        if (number_of_elements) {
          fprintf(network_common.trace_file,
                  "Number of message elements: %ld\n",
                  *number_of_elements);
          }
        fprintf(network_common.trace_file,
               "Number of extracted elements: 0\n");
        fflush(network_common.trace_file);
        }
      return 0;
      }
    }
  i++;
  }
return i;
}

#endif
