/*
 *  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 Library 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 "socket.h"

static ErrType err_type = STD_ERROR;
static unsigned int g_use_ssl = 0;
static char *g_ssl_pathstore = NULL;

/*
 * Tools functions
 */
 
const char * socket_strerror(int err) {
/* -----------------------------------------------------------------------------
 * Get str error, that can be strerror or ssl error
 *
 * @param  int, not used on OPENSSL_ERROR
 * @return char *, a static buffer
 * -------------------------------------------------------------------------- */
  switch (err_type) {
  case STD_ERROR:
    return (const char *)strerror(err);
    break;
  case OPENSSL_ERROR:
#ifdef USE_SOCKET_SSL
    err_type = STD_ERROR; /* Reinit to errno as it is the more frequent one */
    return ERR_reason_error_string(ERR_get_error());
#endif
    break;
  }
  return NULL;
}

void set_socket_ssl(unsigned int use) {
/* -----------------------------------------------------------------------------
 * Toggle the variable g_use_ssl
 *
 * @param  unsigned int
 * -------------------------------------------------------------------------- */
  g_use_ssl = use;   
}

unsigned int get_socket_ssl() {
/* -----------------------------------------------------------------------------
 * Get the variable g_use_ssl
 *
 * @return  unsigned int
 * -------------------------------------------------------------------------- */
  return g_use_ssl;   
}

void set_ssl_pathstore(char * pathstore) {
/* -----------------------------------------------------------------------------
 * Toggle the variable g_ssl_pathstore
 *
 * @param  char *
 * !! The pointer must contain a dynamically memory allocate
 * and must not be freed after
 * -------------------------------------------------------------------------- */
  if (g_ssl_pathstore != NULL)
    free(g_ssl_pathstore);
  g_ssl_pathstore = pathstore;
}

char * get_ssl_pathstore() {
/* -----------------------------------------------------------------------------
 * Get the variable g_ssl_pathstore
 *
 * @return  char *
 * -------------------------------------------------------------------------- */
  return g_ssl_pathstore;   
}

#ifdef USE_SOCKET_SSL
int BIO_read_buffer(BIO *bio, void * buffer, int count) {
/* -----------------------------------------------------------------------------
 * Read to buffer
 *
 * @param  BIO *, the SSL BIO pointer to read on
 * @param  void *, the buffer where to store
 * @param  int, the max number of character to read
 * @return int  the number of character read or -1 if an error occurss
 * -------------------------------------------------------------------------- */
  int status = 0, n;

  while(status < count) {
    n = BIO_read(bio, (void *)&((char *)buffer)[status], count - status);
    switch (n) {
    case 0:
    /* end-of-file (0) */
      return n;
      break;
    case -1:
      if (!BIO_should_retry(bio)) {
        err_type = OPENSSL_ERROR;
        return n;
      }
      break;
    default:
      status += n;
    }
  }
  return status;
}

int BIO_readline(BIO *bio, char * buffer, int maxlen) {
/* -----------------------------------------------------------------------------
 * Read line to buffer
 *
 * @param  BIO *, the SSL BIO pointer to read on
 * @param  char *, the buffer where to store
 * @param  int, the max number of character to read
 * @return int  the number of character read
 * -------------------------------------------------------------------------- */
  int count = 0, status;

  while(count < maxlen) {
    /* Read one character */
    
    if ((status = BIO_read_buffer(bio, buffer + count, 1)) <= 0) {
      /* Nothing to read */
      return count;
    }
    else {
      if (buffer[count] == '\n'){
        buffer[count + 1] = 0;
        return count + 1;
      }
    }
    count++;
  }
  buffer[count] = 0;
  return 0;
}

char * X509_get_verify_error_string(unsigned int verify) {
/* -----------------------------------------------------------------------------
 * Return an error string
 *
 * @param  
 * @param  unsigned int, the verify error number, from SSL_get_verify_result()
 * @return char *, error string
 * -------------------------------------------------------------------------- */
  static char verify_err_string[256];
  verify_err_string[0] = '\0';

  switch(verify) {
  case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
    strlcpy(verify_err_string,
            "unable to get issuer certificate",
            sizeof(verify_err_string));
    break;
  case X509_V_ERR_UNABLE_TO_GET_CRL:
    strlcpy(verify_err_string,
            "unable to get certificate CRL",
            sizeof(verify_err_string));
    break;
  case X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE:
    strlcpy(verify_err_string,
            "nable to decrypt certificate's signature",
            sizeof(verify_err_string));
    break;
  case X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE:
    strlcpy(verify_err_string,
            "unable to decrypt CRL's signature",
            sizeof(verify_err_string));
    break;
  case X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY:
    strlcpy(verify_err_string,
            "unable to decode issuer public key",
            sizeof(verify_err_string));
    break;
  case X509_V_ERR_CERT_SIGNATURE_FAILURE:
    strlcpy(verify_err_string,
            "certificate signature failure",
            sizeof(verify_err_string));
    break;
  case X509_V_ERR_CRL_SIGNATURE_FAILURE:
    strlcpy(verify_err_string,
            "CRL signature failure",
            sizeof(verify_err_string));
    break;
  case X509_V_ERR_CERT_NOT_YET_VALID:
    strlcpy(verify_err_string,
            "certificate is not yet valid",
            sizeof(verify_err_string));
    break;
  case X509_V_ERR_CERT_HAS_EXPIRED:
    strlcpy(verify_err_string,
            "certificate has expired",
            sizeof(verify_err_string));
    break;
  case X509_V_ERR_CRL_NOT_YET_VALID:
    strlcpy(verify_err_string,
            "CRL is not yet valid",
            sizeof(verify_err_string));
    break;
  case X509_V_ERR_CRL_HAS_EXPIRED:
    strlcpy(verify_err_string,
            "CRL has expired",
            sizeof(verify_err_string));
    break;
  case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
    strlcpy(verify_err_string,
            "format error in certificate's notBefore field",
            sizeof(verify_err_string));
    break;
  case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
    strlcpy(verify_err_string,
            "format error in certificate's notAfter field",
            sizeof(verify_err_string));
    break;
  case X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD:
    strlcpy(verify_err_string,
            "format error in CRL's lastUpdate field",
            sizeof(verify_err_string));
    break;
  case X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD:
    strlcpy(verify_err_string,
            "format error in CRL's nextUpdate field",
            sizeof(verify_err_string));
    break;
  case X509_V_ERR_OUT_OF_MEM:
    strlcpy(verify_err_string,
            "out of memory",
            sizeof(verify_err_string));
    break;
  case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
    strlcpy(verify_err_string,
            "self signed certificate",
            sizeof(verify_err_string));
    break;
  case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
    strlcpy(verify_err_string,
            "self signed certificate in certificate chain",
            sizeof(verify_err_string));
    break;
  case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
    strlcpy(verify_err_string,
            "unable to get issuer certificate locally",
            sizeof(verify_err_string));
    break;
  case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE:
    strlcpy(verify_err_string,
            "unable to verify the first certificate",
            sizeof(verify_err_string));
    break;
  case X509_V_ERR_CERT_CHAIN_TOO_LONG:
    strlcpy(verify_err_string,
            "certificate chain too long",
            sizeof(verify_err_string));
    break;
  case X509_V_ERR_CERT_REVOKED:
    strlcpy(verify_err_string,
            "certificate revoked",
            sizeof(verify_err_string));
    break;
  case X509_V_ERR_INVALID_CA:
    strlcpy(verify_err_string,
            "invalid CA certificate",
            sizeof(verify_err_string));
    break;
  case X509_V_ERR_PATH_LENGTH_EXCEEDED:
    strlcpy(verify_err_string,
            "path length constraint exceeded",
            sizeof(verify_err_string));
    break;
  case X509_V_ERR_INVALID_PURPOSE:
    strlcpy(verify_err_string,
            "unsupported certificate purpose",
            sizeof(verify_err_string));
    break;
  case X509_V_ERR_CERT_UNTRUSTED:
    strlcpy(verify_err_string,
            "certificate not trusted",
            sizeof(verify_err_string));
    break;
  case X509_V_ERR_CERT_REJECTED:
    strlcpy(verify_err_string,
            "certificate rejected",
            sizeof(verify_err_string));
    break;
  case X509_V_ERR_SUBJECT_ISSUER_MISMATCH:
    strlcpy(verify_err_string,
            "subject issuer mismatch",
            sizeof(verify_err_string));
    break;
  case X509_V_ERR_AKID_SKID_MISMATCH:
    strlcpy(verify_err_string,
            "authority and subject key identifier mismatch",
            sizeof(verify_err_string));
    break;
  case X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH:
    strlcpy(verify_err_string,
            "authority and issuer serial number mismatch",
            sizeof(verify_err_string));
    break;
  case X509_V_ERR_KEYUSAGE_NO_CERTSIGN:
    strlcpy(verify_err_string,
            "key usage does not include certificate signing",
            sizeof(verify_err_string));
    break;
  case X509_V_ERR_APPLICATION_VERIFICATION:
    strlcpy(verify_err_string,
            "application verification failure",
            sizeof(verify_err_string));
    break;
  }
  return verify_err_string;
}
#endif

/*
 * Server Socket functions
 */

ServerSocket *server_socket_new(const char * host, const char * port, const char * proto) {
/* -----------------------------------------------------------------------------
 * Create a server socket
 *
 * @param  const char *, the address to bind to
 * @param  const char *, port number or service name
 * @param  const char *, name protocol
 * @return ServerSocket * or NULL
 * -------------------------------------------------------------------------- */
  struct sockaddr_in socketaddr;
  int mastersock;
  int trueval = TRUE;
  ServerSocket * new_socket = NULL;
  u_long ip_addr;

  new_socket = (ServerSocket *)malloc(sizeof(ServerSocket));
  if (new_socket == NULL) return NULL;

  socketaddr_init(&socketaddr);
  if (host == NULL) {
    socketaddr.sin_addr.s_addr = INADDR_ANY;
    new_socket->addr = NULL;
  }
  else {
    if (isipv4(host)) {
      ip_addr = inet_addr(host);
      socketaddr.sin_addr.s_addr = ip_addr;

      new_socket->addr = (char *)strdup(host);
      if (new_socket->addr == NULL)
        return NULL;
    }
    else {
      if (!socketaddr_host(&socketaddr, host)) {
      /* host cannot be resolved */
        socketaddr.sin_addr.s_addr = INADDR_ANY;
        new_socket->addr = NULL;
      }
      else {
        new_socket->addr = (char *)strdup(host);
        if (new_socket->addr == NULL)
          return NULL;
      }
    }
  }
  if (port != NULL) {
    socketaddr_service(&socketaddr, port, proto);
    new_socket->port = (char *)strdup(port);
    if (new_socket->port == NULL)
      return NULL;
  }
  else
    new_socket->port = NULL;
  
  new_socket->proto = (char *)strdup(proto);
  if (new_socket->proto == NULL)
    return NULL;
  
  mastersock = socket(PF_INET, prototype(proto), resolveproto(proto));
  if (mastersock < 0) {
    return NULL;
  }

  setsockopt(mastersock, SOL_SOCKET, SO_REUSEADDR, &trueval, sizeof(trueval));
  if (bind(mastersock, (struct sockaddr *) &socketaddr, sizeof(socketaddr)) < 0) {
    return NULL;
  }

  if (prototype(proto) == SOCK_STREAM) {
    if (listen(mastersock, 5) < 0) {
      return NULL;
    }
  }
  
  new_socket->fd = mastersock;

  return new_socket;
}
Socket *server_socket_accept(ServerSocket *socket) {
/* -----------------------------------------------------------------------------
 * Do an accept() to the server socket
 *
 * @param  ServerSocket *
 * @return Socket *, client socket
 * -------------------------------------------------------------------------- */
  
  Socket * new_sock = NULL;
  char port[5];
  int sockid;
  struct sockaddr_in socketaddr;
  socklen_t size = sizeof(socketaddr);
  
  if ((sockid = accept(socket->fd, (struct sockaddr *)&socketaddr, &size)) >= 0) {
    new_sock = (Socket *)malloc(sizeof(Socket));
    if (new_sock != NULL) {
      new_sock->fd = sockid;
      new_sock->host = (char *)strdup(inet_ntoa(socketaddr.sin_addr));
      memset(&port, 0, 5);
      snprintf(port, sizeof(port), "%d", ntohs(socketaddr.sin_port));
      new_sock->port = (char *)strdup(port);
      new_sock->proto = (char *)strdup("tcp");
      new_sock->state = SOCK_CONNECTED;
      
      return new_sock;
    }
  }
  return NULL;
}

int server_socket_disconnect(ServerSocket *socket) {
/* -----------------------------------------------------------------------------
 * Close a server socket
 *
 * @param  ServerSocket *
 * @return int
 * -------------------------------------------------------------------------- */
  if (close(socket->fd) == 0) {
    return 1;
  }
  return 0;
}

int server_socket_destroy(ServerSocket *socket) {
/* -----------------------------------------------------------------------------
 * Destroy a server socket
 *
 * @param  ServerSocket *
 * @return int
 * -------------------------------------------------------------------------- */
  server_socket_disconnect(socket);
  if (socket->addr != NULL)
    free(socket->addr);
  free(socket->port);
  free(socket->proto);
  free(socket);
  socket = NULL;
  return 1;
}

/*
 * Client Socket functions
 */

Socket *socket_new(const char *host, const char * port, const char * proto) {
/* -----------------------------------------------------------------------------
 * Create a client socket
 *
 * @param  const char *, the hostname or IP address
 * @param  const char *, the port number or service name
 * @param  const char *, name protocol
 * @return Socket * or NULL
 * -------------------------------------------------------------------------- */
  int sockid = -1;
  Socket * new_socket = NULL;
#ifdef USE_SOCKET_SSL
  char *pathstore = NULL;
  SSL *ssl;
#endif
  
  new_socket = (Socket *)malloc(sizeof(Socket));
  if (new_socket == NULL) return NULL;
  
  new_socket->host = (char *)strdup(host);
  if (new_socket->host == NULL) {
    free(new_socket);
    return NULL;
  }
  new_socket->port = (char *)strdup(port);
  if (new_socket->port == NULL) {
    free(new_socket->host);
    free(new_socket);
    return NULL;
  }
  new_socket->proto = (char *)strdup(proto);
  if (new_socket->proto == NULL) {
    free(new_socket->host);
    free(new_socket->port);
    free(new_socket);
    return NULL;
  }
  
  new_socket->state = SOCK_DISCONNECTED;
  
  new_socket->use_ssl = 0;
  
#ifdef USE_SOCKET_SSL
  if (get_socket_ssl()) {
    pathstore = get_ssl_pathstore();
    if (pathstore != NULL) {
      new_socket->ctx = SSL_CTX_new(SSLv23_client_method());
      
      /* Load the Certificate Trust store */
      if (!SSL_CTX_load_verify_locations(new_socket->ctx, pathstore, NULL)) {
        err_type = OPENSSL_ERROR;
        SSL_CTX_free(new_socket->ctx);
        new_socket->ctx = NULL;
        
        free(new_socket->host);
        free(new_socket->port);
        free(new_socket->proto);
        free(new_socket);
        
        return NULL;
      }
      
      /* Create the BIO structure */
      new_socket->bio = BIO_new_ssl_connect(new_socket->ctx);
      
      /* Auto responding in background, if the server requested another handshake */
      BIO_get_ssl(new_socket->bio, &ssl);
      SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);
      
      new_socket->use_ssl = 1;
    }
    else {
      logmsg(LOG_ERR, "an SSL certificate trust store must be specified.");
      
      free(new_socket->host);
      free(new_socket->port);
      free(new_socket->proto);
      free(new_socket);
      
      return NULL;
    }
  }
  else {
    new_socket->bio = NULL;
    new_socket->ctx = NULL;
#else 
  if (get_socket_ssl()) {
    logmsg(LOG_ERR, "Warning : socket creation with SSL is requested but SSL "
           "is not supported. Define the USE_SOCKET_SSL.");
  }
#endif
  sockid = socket(PF_INET, prototype(proto), resolveproto(proto));
  if (sockid < 0) {
    free(new_socket->host);
    free(new_socket->port);
    free(new_socket->proto);
    free(new_socket);
    
    return NULL;
  }
#ifdef USE_SOCKET_SSL
  }
#endif
  new_socket->fd = sockid;  
  return new_socket;
}

int socket_bind(Socket *socket, const char *host, const char *port) {
/* -----------------------------------------------------------------------------
 * Bind a socket to a particular port (and host)
 *
 * @param  Socket *
 * @param  const char *, the ip / host address
 *         if NULL, INADDR_ANY will be used
 * @param  const char *, the port
 * @return int
 * -------------------------------------------------------------------------- */
  struct sockaddr_in socketaddr;
  u_long ip_addr;
 
  socketaddr_init(&socketaddr);
  socketaddr_service(&socketaddr, port, socket->proto);
  if (host == NULL)
    socketaddr.sin_addr.s_addr = INADDR_ANY;
  else {
    if (isipv4(host)) {
      ip_addr = inet_addr(host);
      socketaddr.sin_addr.s_addr = ip_addr;
    }
    else {
      if (!socketaddr_host(&socketaddr, host))
      /* host cannot be resolved */
        socketaddr.sin_addr.s_addr = INADDR_ANY;
    }
  }

  return bind(socket->fd, (struct sockaddr *)&socketaddr, sizeof(socketaddr));
}

int socket_connect(Socket *socket) {
/* -----------------------------------------------------------------------------
 * Connect a client socket
 *
 * @param  Socket *
 * @return int (see SocketConnectStatus enum)
 * -------------------------------------------------------------------------- */
  struct sockaddr_in socketaddr;
  int retcode = SOCK_OK;

#ifdef USE_SOCKET_SSL
  SSL *ssl;
  X509 *peerCertificate;
  char commonName[512];
  X509_NAME *name;

  if ((socket->use_ssl)
  && (socket->bio != NULL)) {
    if (isipv4(socket->host))
      BIO_set_conn_ip(socket->bio, socket->host);
    else
      BIO_set_conn_hostname(socket->bio, socket->host);
    BIO_set_conn_port(socket->bio, socket->port);
    
    /* Verify the connection opened */
    if (BIO_do_connect(socket->bio) <= 0) {
      err_type = OPENSSL_ERROR;
      return SOCK_ERR;
    }
    
    /* Verify the certificate validity
     * Note that the connection can still be used
     * The caller must chose to be paranoid and refuse the connection or not
     */
    BIO_get_ssl(socket->bio, &ssl);
    peerCertificate = SSL_get_peer_certificate(ssl);
    if (peerCertificate != NULL) {

      socket->ssl_verify = SSL_get_verify_result(ssl);
      if (socket->ssl_verify != X509_V_OK) {
        logmsg(LOG_ERR, "Warning : certificate cannot be verified with trust store : %s", 
                        X509_get_verify_error_string(socket->ssl_verify));
        retcode = SOCK_SSL_VERIFY_ERR;
      }
      
      name = X509_get_subject_name(peerCertificate);
      X509_NAME_get_text_by_NID(name, NID_commonName, commonName, 512);
      if(strncmp(commonName, socket->host, 512) != 0) {
        logmsg(LOG_ERR, "Warning : peer certificate name verification failed for %s"
                        " : commonName mis-match : %s", socket->host, commonName);
        retcode = SOCK_SSL_CN_MISMATCH_ERR;
      }
    }
    else {
      logmsg(LOG_ERR, "Warning : no peer certificate found");
      retcode = SOCK_SSL_NO_PEER_CERT_ERR;
    }

    /* Get the file descriptor, useful for select() loop */
    socket->fd = SSL_get_fd(ssl);
  }
  else {
#endif
  
  socketaddr_init(&socketaddr);
  socketaddr_service(&socketaddr, socket->port, socket->proto);
  socketaddr_host(&socketaddr, socket->host);

  
  if (connect(socket->fd, (struct sockaddr *)&socketaddr, sizeof(socketaddr)) < 0)
    return SOCK_ERR;
#ifdef USE_SOCKET_SSL
  }
#endif
  socket->state = SOCK_CONNECTED;
  return retcode;
}
int socket_disconnect(Socket *socket) {
/* -----------------------------------------------------------------------------
 * Close a client socket
 *
 * @param  Socket *
 * @return int
 * -------------------------------------------------------------------------- */
#ifdef USE_SOCKET_SSL
  if ((socket->use_ssl)
  && (socket->bio != NULL)) {
    BIO_reset(socket->bio);
    socket->state = SOCK_DISCONNECTED;
    return 1;
  }
  else {
#endif
  if (close(socket->fd) == 0) {
    socket->state = SOCK_DISCONNECTED;
    return 1;
  }
#ifdef USE_SOCKET_SSL
  }
#endif
  return 0;
}

int socket_destroy(Socket *socket) {
/* -----------------------------------------------------------------------------
 * Destroy a client socket
 *
 * @param  Socket *
 * @return int
 * -------------------------------------------------------------------------- */
  socket_disconnect(socket);
  free(socket->host);
  free(socket->port);
  free(socket->proto);
#ifdef USE_SOCKET_SSL
  if ((socket->use_ssl)
  && (socket->bio != NULL)) {
    BIO_free_all(socket->bio);
    SSL_CTX_free(socket->ctx);
  }
#endif
  free(socket);
  socket = NULL;
  return 1;
}

int socket_readline(Socket *socket, char * buffer, int maxlen) {
/* -----------------------------------------------------------------------------
 * Read line from a client socket
 *
 * @param  Socket *
 * @param  char *, buffer to fill
 * @param  int, maxlen to read
 * @return int, number of characters read
 * -------------------------------------------------------------------------- */
#ifdef USE_SOCKET_SSL
  if ((socket->use_ssl)
  && (socket->bio != NULL)) {
    return BIO_readline(socket->bio, buffer, maxlen);  
  }
  else {
#endif
  return readline(socket->fd, buffer, maxlen);
#ifdef USE_SOCKET_SSL
  }
#endif
}

int socket_read(Socket *socket, char * buffer, int maxlen) {
/* -----------------------------------------------------------------------------
 * Read from a client socket
 *
 * @param  Socket *
 * @param  char *, buffer to fill
 * @param  int, maxlen to read
 * @return int, number of characters read
 * -------------------------------------------------------------------------- */
#ifdef USE_SOCKET_SSL
  if ((socket->use_ssl)
  && (socket->bio != NULL)) {
    return BIO_read_buffer(socket->bio, buffer, maxlen);
  }
  else {
#endif
  return read_buffer(socket->fd, buffer, maxlen);
#ifdef USE_SOCKET_SSL
  }
#endif
}

int socket_write(Socket *socket, int flags, const char * str, ...) {
/* -----------------------------------------------------------------------------
 * Write to client socket
 *
 * @param  Socket *
 * @param  int, flags passed to send()
 * @param  const char *, printf format
 * @return int, number of characters written
 * -------------------------------------------------------------------------- */
  va_list args;
  char *result = NULL;
  int retval;
  va_start(args, str);
  vasprintf(&result, str, args);
  va_end(args);

  if (result != NULL) {
#ifdef USE_SOCKET_SSL
    if ((socket->use_ssl)
    && (socket->bio != NULL)) {
      retval = BIO_write(socket->bio, result, strlen(result));
      if (retval <= 0) {
        if (!BIO_should_retry(socket->bio)) {
          err_type = OPENSSL_ERROR;
        }
      }
    }
    else {
#endif
    retval = send(socket->fd, result, strlen(result), flags);
    free(result);    
    switch(errno) {
      case EBADF:
      case ENOTSOCK:
      case EFAULT:
      case EMSGSIZE:
      case EAGAIN:
      case ENOBUFS:
      case EINTR:
      case ENOMEM:
      case EINVAL:
      case EPIPE:
        return -1;
        break;
      default:
        return retval;
        break;
    }
#ifdef USE_SOCKET_SSL
    }
#endif
  }
  return -1;
}

int socket_rawrite(Socket *socket, int flags, const char * data, int size) {
/* -----------------------------------------------------------------------------
 * Write to client socket
 *
 * @param  Socket *
 * @param  int, flags passed to send()
 * @param  const void *, data to write
 * @param  int, number of bytes to write
 * @return int, number of characters written
 * -------------------------------------------------------------------------- */
  int retval;
 
 #ifdef USE_SOCKET_SSL
  if ((socket->use_ssl)
  && (socket->bio != NULL)) {
    retval = BIO_write(socket->bio, data, size);
    if (retval <= 0) {
      if (!BIO_should_retry(socket->bio)) {
        err_type = OPENSSL_ERROR;
      }
    }
  }
  else {
#endif
    retval = send(socket->fd, data, size, flags);
    switch(errno) {
      case EBADF:
      case ENOTSOCK:
      case EFAULT:
      case EMSGSIZE:
      case EAGAIN:
      case ENOBUFS:
      case EINTR:
      case ENOMEM:
      case EINVAL:
      case EPIPE:
        return -1;
        break;
      default:
        return retval;
        break;
    }
#ifdef USE_SOCKET_SSL
  }
#endif
  return -1;
 
}


syntax highlighted by Code2HTML, v. 0.9.1