#include "rpsdist.h"

/****** UTILITY FUNCTIONS ******/
/*
 *  Custom String Concatenation
 *  strcat sucks in that you overflow your buffer.  This adaptation is based off Jerry's
 *  my_strcat, but modified. the first argument string you pass in, is automatically freed
 *  this means you can append many things together and still be memory efficient.  So you
 *  can say: result = custom_strcat(2, result, buffer); All good (?)
 */
char * custom_strcat(int num_strings, ...){
  va_list ap;
  int i, len, j = 0;
  char * p;
  char buf[10*1024];
  va_start (ap, num_strings);
  for (i = num_strings; i > 0; i--) {
    p = va_arg (ap, char *);
    if(p != NULL){
      len = strlen (p);
      memcpy ((char *) (buf + j), p, len);
      if( i == num_strings)
	free(p);
      j += len;
    }
  }
  va_end(ap);
  buf[j] = '\0';
  return strdup(buf);
}
/*
 *  Since we print timestamps so often, wrote this
 */
void time_stamp(int fd){
  char buffer[200];
  struct timeval mod;
  struct tm * mz;
  fd_set write_fd;
  struct timeval tv;
  int year;
  int month;

  tv.tv_sec = 5;
  tv.tv_usec = 0;
  FD_ZERO(&write_fd);
  FD_SET(fd, &write_fd);
  
  gettimeofday(&mod, NULL);
  mz = gmtime((time_t*)&mod);
  year = 1900 + mz->tm_year;
  month = mz->tm_mon + 1;
  sprintf(buffer, "timestamp: %d%02d%02d %02d:%02d:%02d\n", year, month, 
	  mz->tm_mday,mz->tm_hour,mz->tm_min,mz->tm_sec);
  if( select(fd+1, NULL, &write_fd, NULL, &tv) > 0)
    write(fd, buffer, strlen(buffer));
  else
    printf("Write timeout: timeout fd(%d)\n", fd);
}

/* 
 *  Call gzip to zip transaction
 */
void zip_stream(int in_stream, int out_stream){
  int childpid;
  int  status;
 
  if(in_stream < 0 || out_stream < 0){
    printf("Invalid stream supplied to zip_stream\n");
    return;
  }
  
  switch( (childpid = fork())) {
  case -1:
   
    write(out_stream, "Fork error\n", 11);
    break;
    
  case 0:
  
    dup2(in_stream, STDIN_FILENO);
    dup2(out_stream, STDOUT_FILENO);
   
    if( execlp("gzip", "gzip","-cf", NULL) < 0 ){
      printf("execlp error");
      exit(1);
    }
    break;
    
  default:
    break;
    
  }
  while( (wait( &status)) != childpid);
}


/* 
 *  Call gunzip to unzip transaction
 */
void unzip_stream(int in_stream, int out_stream){
  int childpid;
  int  status;

  if(in_stream < 0 || out_stream < 0){
    printf("Invalid stream supplied to zip_stream\n");
    return;
  } 

  switch( (childpid = fork())) {
  case -1:
    
   
    write(out_stream, "Fork error\n", 11);
    break;
    
  case 0:

    dup2(in_stream, STDIN_FILENO);
    dup2(out_stream, STDOUT_FILENO);
   
    if( execlp("gunzip", "gunzip","-cf", NULL) < 0 ){
      printf("execlp error");
      exit(1);
    }
    
    break;
    
  default:
  
    
    break;
    
  }
 
  while( (wait( &status)) != childpid);
}

/*
  if(!regexec(&trans_sub_begin, input, 5, args, 0))
     printf("match, trans-sub-begin: %s", args[1].rm_sp);
  if(!regexec(&trans_label, input, 5, args, 0) )
    printf("match, trans-label: %s", args[1].rm_sp);
  if(!regexec(&resp_auth_type, input, 5, args, 0) )
    printf("match, resp-auth-type: %s",  args[1].rm_sp);
  if(!regexec(&trans_conf_type, input, 5, args, 0) )
    printf("match, trans_conf-type: %s",args[1].rm_sp);
  if(!regexec(&trans_sub_end, input,  5, args, 0) )
    printf("match, trans_sub_end: %s", args[1].rm_sp);
  if(!regexec(&sequence, input,  5, args, 0) )
    printf("match, sequence: %s", args[1].rm_sp);
  if(!regexec(&timestamp, input,  5, args, 0) )
    printf("match, YMD: %s", args[1].rm_sp);
  if(!regexec(&integrity, input,  5, args, 0) )
    printf("match, integrity: %s", args[1].rm_sp);
  if(!regexec(&auth_dep, input,  5, args, 0) )
    printf("match, auth_dep: %s", args[1].rm_sp);
  if(!regexec(&rep_sig, input,  5, args, 0) )
    printf("match, repository sig: %s", args[1].rm_sp);
  if(!regexec(&signature, input,  5, args, 0) )
    printf("match, signature: %s", args[1].rm_sp);
  if(!regexec(&trans_request, input,  5, args, 0) )
    printf("match, trans-request: %s", args[1].rm_sp);
  if(!regexec(&seq_begin, input,  5, args, 0) )
    printf("match, sequence-begin: %s", args[1].rm_sp);
  if(!regexec(&seq_end, input,  5, args, 0) )
    printf("match, seq_end: %s", args[1].rm_sp);
  if(!regexec(&trans_resp, input,  5, args, 0) )
    printf("match, trans_resp: %s", args[1].rm_sp); 
  if(!regexec(&heartbeat, input,  5, args, 0) )
    printf("match, heartbeat: %s", args[1].rm_sp);
  if(!regexec(&snap_req, input,  5, args, 0) )
    printf("match, snap_req: %s", args[1].rm_sp);
  if(!regexec(&snap_begin, input,  5, args, 0) )
    printf("match, snap-begin: %s", args[1].rm_sp);
}
*/

void init_regexes(void){
  regcomp(&trans_sub_begin, 
	  "^transaction-submit-begin:[ \t]*([[:alpha:]]([[:alnum:]_-]*[[:alnum:]])?)[ \t]+([[:digit:]]+)[ \t]*[\r]*\n$",
	  (REG_EXTENDED|REG_ICASE));
  regcomp(&resp_auth_type, "^response-auth-type:[ \t]*([[:alpha:]]+[[:alnum:]]*)[ \t]*[\r]*\n$",
	  (REG_EXTENDED|REG_ICASE));
  regcomp(&trans_conf_type, "^transaction-confirm-type:[ \t]*(none|commit|normal|legacy)([ \t]+verbose)?[ \t]*[\r]*\n$", 
	  (REG_EXTENDED|REG_ICASE));
  regcomp(&trans_sub_end, 
	  "^transaction-submit-end:[ \t]*([[:alpha:]]([[:alnum:]_-]*[[:alnum:]])?)[ \t]+([[:digit:]]+)[ \t]*[\r]*\n$",
	  (REG_EXTENDED|REG_ICASE));
  regcomp(&trans_label, "^transaction-label:[ \t]*([A-Z]+[[:alnum:]]+)[ \t]*\n$",
	  (REG_EXTENDED|REG_ICASE));
  regcomp(&sequence, "^sequence:[ \t]*([[:digit:]]+)[ \t]*\n$", 
	  (REG_EXTENDED|REG_ICASE));
  regcomp(&timestamp, "^timestamp:[ \t]*([[:digit:]]{8}[ \t][[:digit:]]{2}:[[:digit:]]{2}:[[:digit:]]{2})[ \t]*\n$", 
	  (REG_EXTENDED|REG_ICASE));
  regcomp(&integrity, "^integrity:[ \t]*(legacy|no-auth|auth-failed|authorized)[ \t]*\n$",
	  (REG_EXTENDED|REG_ICASE));
  regcomp(&auth_dep, "^auth-dependency:[ \t]*([[:alpha:]]([[:alnum:]_-]*[[:alnum:]])?)[ \t]*\n$",
	  (REG_EXTENDED|REG_ICASE));
  regcomp(&rep_sig, "^repository-signature:[ \t]*([[:alpha:]][a-z0-9_-]*[[:alnum:]]*)[ \t]*\n$", 
	  (REG_EXTENDED|REG_ICASE));
  regcomp(&signature, "^signature:", (REG_EXTENDED|REG_ICASE));
  regcomp(&trans_begin, "^transaction-begin:[ \t]*([[:digit:]]+)[ \t]*\n$",
	  (REG_EXTENDED|REG_ICASE));
  regcomp(&trans_method, "^transfer-method:[ \t]*(gzip|uuencode|base50|radix64|plain)[ \t]*\n$",
	  (REG_EXTENDED|REG_ICASE));
  regcomp(&trans_request, "^transaction-request:[ \t]*([[:alpha:]][a-z0-9_-]*[[:alnum:]]*)[ \t]*\n$",
	  (REG_EXTENDED|REG_ICASE));
  regcomp(&seq_begin, "^sequence-begin:[ \t]*([[:digit:]]+)[ \t]*\n$",
	  (REG_EXTENDED|REG_ICASE));
  regcomp(&seq_end, "^sequence-end:[ \t]*([[:digit:]]+)[ \t]*\n$",
	  (REG_EXTENDED|REG_ICASE));
  regcomp(&trans_resp, "^transaction-response:[ \t]*([[:alpha:]][a-z0-9_-]*[[:alnum:]]*)[ \t]*\n$",
	  (REG_EXTENDED|REG_ICASE));
  regcomp(&heartbeat, "^heartbeat:[ \t]*([[:alpha:]][a-z0-9_-]*[[:alnum:]]*)[ \t]*\n$",
	  (REG_EXTENDED|REG_ICASE));
  regcomp(&snap_req, "^snapshot-request:[ \t]*([[:alpha:]][a-z0-9_-]*[[:alnum:]]*)[ \t]*\n$",
	  (REG_EXTENDED|REG_ICASE));
  regcomp(&snap_begin, "^snapshot-begin:[ \t]*([[:alpha:]][a-z0-9_-]*[[:alnum:]]*)[ \t]*\n$",
	  (REG_EXTENDED|REG_ICASE));
  regcomp(&blank_line, "^[ \t]*[\r]*\n$", (REG_EXTENDED|REG_ICASE));

  /* temp */
  regcomp(&add_peer, "^add-peer:[ \t]*([[:alpha:]]+)[ \t]+([[:digit:]]+)[ \t]*[\r]*\n$",
	  (REG_EXTENDED|REG_ICASE));
  regcomp(&rm_peer, "^rm-peer:[ \t]*([[:alpha:]]+)[ \t]+([[:digit:]]+)[ \t]*[\r]*\n$",
	  (REG_EXTENDED|REG_ICASE));
  regcomp(&sh_peer, "^sh-peer[ \t]*[\r]*\n$",
	  (REG_EXTENDED|REG_ICASE));
}

void clear_regexes(void){
  regfree(&trans_sub_begin);
  regfree(&resp_auth_type);
  regfree(&trans_conf_type);
  regfree(&trans_sub_end);
  regfree(&trans_label);
  regfree(&sequence);
  regfree(&timestamp);
  regfree(&integrity);
  regfree(&auth_dep);
  regfree(&rep_sig);
  regfree(&signature);
  regfree(&trans_begin);
  regfree(&trans_method);
  regfree(&trans_request);
  regfree(&seq_begin);
  regfree(&seq_end);
  regfree(&trans_resp);
  regfree(&heartbeat);
  regfree(&snap_req);
  regfree(&snap_begin);
  regfree(&blank_line);

  regfree(&add_peer);
  regfree(&rm_peer);
  regfree(&sh_peer);
}

/* LIST FUNCTIONS */
/* Find the source 'source' in the source list pointed
 * to by 'start'.
 *
 * Return:
 *   pointer to element if 'source' is found
 *   NULL otherwise
 */
peer_t * find_peer_obj (root_t * start, char * host, int port) {
  peer_t *obj = NULL;

  obj = start->first;
  while (obj != NULL) {
    if ( !strcasecmp(obj->host, host) && (obj->port == port) ) 
      break;
    obj = obj->next;
  }
  
  return obj;
}

/* Create a source_t list element with value 
 *
 * Return:
 *   void
 */
peer_t * create_peer_obj (int fd, char * host, int port) {
  peer_t *obj;
  char *z;

  if (fd < 0 || host == NULL || port <= 0)
    return NULL;

  obj = (peer_t *) malloc (sizeof (peer_t));
  obj->fd = fd;
  obj->host = strdup(host);
  obj->port = port;
  pthread_mutex_init(&obj->write, NULL);

  return (obj);
}

/* Add a source_t element to the list pointed to by 'start'.
 *
 * Return:
 *   void
 */
void add_peer_obj (root_t * start, peer_t *obj) {

  obj->next = NULL;
  if (start->first == NULL)
    start->first = obj;
  else
    start->last->next = obj;

  start->last = obj;
}
/*
 * Delete an object.  Search through from start to object.
 * Once get to object, relink list using previous item.
 */
void delete_peer_obj(root_t * start, peer_t * obj) {
  peer_t * temp = start->first;
  peer_t * prev = NULL;

  while(temp) {
    if(temp == obj)
      break;
    else{
      prev = temp;
      temp = temp->next;
    }
  }
  
  if(temp == NULL){
    printf("Object not found for delete\n");
    return;
  }
  
  if(prev == NULL) /* if we're deleting the first item */
    start->first = temp->next;
  
  if(temp->next == NULL) /* if we're deleting end */
    start->last = prev;
    
  if(prev)
    prev->next = temp->next;
  
  free(temp);
}

/*
 * Custom read routine, because a heartbeat can come at anytime.  We need to check
 * for it on every read
 */
int scanning_read(int fd, char * buffer, int buf_size) {
  char t_buffer[4096];
  int amount_read = 0;

  fd_set read_fd;
  struct timeval tv;
  regmatch_t args[5];

  FD_ZERO(&read_fd);
  FD_SET(fd, &read_fd);
  tv.tv_sec = 5;
  tv.tv_usec = 0;
  
  if( select(fd+1, &read_fd, NULL, NULL, &tv) > 0 ){
    if( (amount_read = read(fd, t_buffer, 4096)) < 0){
      printf("Scan read: socket closed\n");
      return -1;
    }
    /* check for a heartbeat */
    if( !regexec(&heartbeat, t_buffer, 0, 0, 0) )  /* expects a string...GRRRR... */
      write(STDOUT_FILENO, "**FOUND A HEARTBEAT IN A READ**\n", 33);
  }
  else{
    printf("scan read: read timeout\n");
    return -1;
  }
  memcpy(buffer, t_buffer, buf_size);
  return amount_read;
}
/*
 *  Check the stamp agains the currecnt time.
 *  1  if older
 *  0  if same
 *  -1 if in future
 */
int check_timestamp(char * stamp){
  char buffer[200];
  struct timeval mod;
  struct tm * mz;
  fd_set write_fd;
  struct timeval tv;
  int year;
  int month;
  
  gettimeofday(&mod, NULL);
  mz = gmtime((time_t*)&mod);
  year = 1900 + mz->tm_year;
  month = mz->tm_mon + 1;
  sprintf(buffer, "%d%02d%02d %02d:%02d:%02d\n", year, month, 
	  mz->tm_mday,mz->tm_hour,mz->tm_min,mz->tm_sec);
  /*printf("original: %s\n", stamp);
  printf("now: %s\n", buffer);*/
  return( strcmp(buffer, stamp) );
}


