/* Collection of code snippets by Arne Vajhøj */
/* (likely posted to comp.os.vms/INFO-VAX, INFO-TPU, MACRO32, eksperten.dk, newz.dk, LinkedIn or other place sometime between 1990 and now) */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <time.h>

#include <sys/socket.h>
#include <fcntl.h>
#include <netdb.h>
#include <errno.h>

#include "simple_stomp.h"

#define MAX_BUF 10000

static int print_debug = 0;

static int helper_write(simple_stomp_t *ctx, const char *cmd, const char *header, const char *body)
{
   int status;
   char buf[MAX_BUF], errmsg[200], *p;
   p = buf;
   p += snprintf(p, sizeof(buf) - (p - buf), "%s\n", cmd);
   if(header != NULL)
   {
      p += snprintf(p, sizeof(buf) - (p - buf), "%s\n", header);
   }
   p += snprintf(p, sizeof(buf) - (p - buf), "\n");
   if(body != NULL)
   {
      p += snprintf(p, sizeof(buf) - (p - buf), "%s\n", body);
   }
   if(print_debug)
   {
      printf("%s\n", buf);
   }
   status = send(ctx->sd, buf, strlen(buf) + 1, 0);
   if(status < 0)
   {
      snprintf(errmsg, sizeof(errmsg), "Error sending to socket: %s", strerror(errno));
      ctx->eh(errmsg);
      return 0;
   }
   return 1;
}

static int helper_read(simple_stomp_t *ctx, char *cmd, char *header, char *body)
{
   int ix;
   char buf[MAX_BUF], errmsg[200], *p;
   memset(buf, '\0', sizeof(buf));
   ix = 0;
   do 
   {
      ix += recv(ctx->sd, buf + ix, sizeof(buf) - ix, 0);
   }
   while(ix < sizeof(buf) && ix <= strlen(buf));
   if(print_debug)
   {
      printf("%s\n", buf);
   }
   p = strstr(buf, "\n");
   if(p != NULL)
   {
      strncpy(cmd, buf, p - buf);
      cmd[p - buf] = '\0';
   }
   else
   {
      ctx->eh("No command found in frame");
      return 0;
   }
   p = strstr(buf, "\n\n");
   if(p != NULL)
   {
      strcpy(body, p + 2);
   }
   else
   {
      ctx->eh("No body found in frame");
      return 0;
   }
   return 1;
}

void simple_stomp_debug(int debug)
{
   print_debug = debug;
}

int simple_stomp_init(simple_stomp_t *ctx, const char *host, int port, error_handler eh)
{
   int status;
   char errmsg[200], cmd[80], header[80], body[80];
   struct sockaddr local, remote;
   struct hostent *hostinfo;
   ctx->eh = eh;
   ctx->sd = socket(AF_INET,SOCK_STREAM, 0);
   if(ctx->sd < 0)
   {
      snprintf(errmsg, sizeof(errmsg), "Error creating socket: %s", strerror(errno));
      ctx->eh(errmsg);
      return 0;
   }
   local.sa_family = AF_INET;
   memset(local.sa_data, 0, sizeof(local.sa_data));
   status = bind(ctx->sd, &local, sizeof(local));
   if(status<0)
   {
      snprintf(errmsg, sizeof(errmsg), "Error binding socket: %s", strerror(errno));
      ctx->eh(errmsg);
      return 0;
   }
   hostinfo = gethostbyname(host);
   if(!hostinfo)
   {
      snprintf(errmsg, sizeof(errmsg), "Error looking up host %s: %s", host, strerror(errno));
      ctx->eh(errmsg);
      return 0;
   }
   remote.sa_family = hostinfo->h_addrtype;
   memcpy(remote.sa_data + 2, hostinfo->h_addr_list[0], hostinfo->h_length);
   *((short *)remote.sa_data) = htons(port);
   status = connect(ctx->sd, &remote, sizeof(remote));
   if(status!=0) {
      snprintf(errmsg, sizeof(errmsg), "Error connecting to host %s port %d: %s", host, port, strerror(errno));
      ctx->eh(errmsg);
      return 0;
   }
   if(!helper_write(ctx, "CONNECT", NULL, NULL))
   {
      snprintf(errmsg, sizeof(errmsg), "Error sending CONNECT");
      ctx->eh(errmsg);
      return 0;
   }
   if(!helper_read(ctx, cmd, header, body))
   {
      snprintf(errmsg, sizeof(errmsg), "Error receiving CONNECTED");
      ctx->eh(errmsg);
      return 0;
   }
   return 1;
}

int simple_stomp_write(simple_stomp_t *ctx, const char *qname, const char *msg)
{
   char header[80], errmsg[200];
   snprintf(header, sizeof(header), "destination: %s", qname);
   if(!helper_write(ctx, "SEND", header, msg))
   {
      snprintf(errmsg, sizeof(errmsg), "Error sending SEND");
      ctx->eh(errmsg);
      return 0;
   }
   return 1;
}

int simple_stomp_read(simple_stomp_t *ctx, const char *qname, char *msg)
{
   char cmd[80], header[80], errmsg[200];
   snprintf(header, sizeof(header), "destination: %s", qname);
   if(!helper_write(ctx, "SUBSCRIBE", header, NULL))
   {
      snprintf(errmsg, sizeof(errmsg), "Error sending SUBSCRIBE");
      ctx->eh(errmsg);
      return 0;
   }
   if(!helper_read(ctx, cmd, header, msg))
   {
      snprintf(errmsg, sizeof(errmsg), "Error receiving MESSAGE");
      ctx->eh(errmsg);
      return 0;
   }
   return 1;
}

int simple_stomp_close(simple_stomp_t *ctx)
{
   char errmsg[200];
   if(!helper_write(ctx, "DISCONNECT", NULL, NULL))
   {
      snprintf(errmsg, sizeof(errmsg), "Error sending CONNECT");
      ctx->eh(errmsg);
      return 0;
   }
   close(ctx->sd);
   return 1;
}

