#if HAVE_CONFIG_H
# include "config.h"
#endif

#include "rw.h"
#include "ssl.h"
#include <errno.h>
#include <unistd.h>
#include <syslog.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>

/* two wrapper functions making reading and writing transparent */
static ssize_t generic_read(int fd, void * buf, size_t count) {
  fd_set rs;
  struct timeval to = { 30, 0 };
  FD_ZERO(&rs);
  if (buf == NULL)
    return 0;
  FD_SET(fd,&rs);
  select(fd+1,&rs,NULL,NULL,&to);
  if (!FD_ISSET(fd,&rs)) {
    write_line(fd,"-ERR read() timed out.\r\n");
    syslog(LOG_ERR,"read() timed out.");
    exit(EXIT_FAILURE);
  }
#if HAVE_LIBSSL
  if (use_ssl()) {
    return SSL_read(get_ssl_handle(),buf,count);
  }
#endif
  return read(fd,buf,count);
}

static ssize_t generic_write(int fd, void * buf, size_t count) {
  fd_set ws;
  struct timeval to = { 30, 0 };
  FD_ZERO(&ws);
  if (buf == NULL)
    return 0;
  FD_SET(fd,&ws);
  select(fd+1,NULL,&ws,NULL,&to);
  if (!FD_ISSET(fd,&ws)) {
    syslog(LOG_ERR,"write() timed out.");
    exit(EXIT_FAILURE);
  }
#if HAVE_LIBSSL
  if (use_ssl()) {
    return SSL_write(get_ssl_handle(),buf,count);
  }
#endif
  return write(fd,buf,count);
}

int write_line(int fd, char * line) {
  size_t n;
  size_t nleft;
  ssize_t nwritten;
  char * ptr;

  if (line == NULL) return 0;
  n = strlen(line);

  ptr = line;
  nleft = n;
  while (nleft > 0) {
    if ((nwritten = generic_write(fd, ptr, nleft)) <= 0) {
      if (errno!= EINTR) {
        syslog(LOG_ERR,"%s: %s","write failed - aborting",strerror(errno));
        exit(EXIT_FAILURE);
      }
      nwritten = 0;
    }
    nleft -= nwritten;
    ptr += nwritten;
  }
  return n;
}

static ssize_t my_read(int fd, char * ptr) {
  static int read_cnt = 0;
  static char * read_ptr;
  static char read_buf[MAXLINE];
  if (ptr==NULL)
    return 0;
  if (read_cnt <= 0) {
again:
    if ((read_cnt = generic_read(fd,read_buf,sizeof(read_buf))) < 0) {
      if (errno == EINTR)
        goto again;
      return -1;
    } else if (read_cnt == 0)
      return 0;
    read_ptr = read_buf;
  }
  read_cnt--;
  *ptr = *read_ptr++;
  return 1;
}

static ssize_t my_readline(int fd, void * vptr, size_t maxlen) {
  int n, rc;
  char c, * ptr;
  if (vptr==NULL)
    return 0;
  ptr = vptr;
  for (n = 1; n < maxlen; n++) {
    if ((rc = my_read(fd,&c)) == 1) {
      *ptr++ = c;
      if (c=='\n')
        break;
    } else if (rc==0) {
      if (n==1) {
        *ptr = '\0';
        return 0;
      } else
        break;
    } else
      return -1;
  }
  *ptr = '\0';
  /* truncate lines that are too long */
  if (((char *)vptr)[strlen((char *)vptr)-1]!='\n') {
    char c;
    int rc;
    rc = generic_read(fd,&c,sizeof(c));
    while (rc==sizeof(c) && c!='\n') {
      rc = generic_read(fd,&c,sizeof(c));
    }
  }
  return n;
}

ssize_t read_line(int fd, void * ptr, size_t maxlen) {
  ssize_t n;
  if (ptr==NULL)
    return 0;
  if ((n = my_readline(fd,ptr,maxlen)) < 0) {
    syslog(LOG_ERR,"%s: %s","read failed - aborting",strerror(errno));
    exit(EXIT_FAILURE);
  }
  return n;
}
