/* Nessus
 * Copyright (C) 1998 - 2001 Renaud Deraison
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2,
 * as published by the Free Software Foundation
 *
 * 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 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., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * In addition, as a special exception, Renaud Deraison
 * gives permission to link the code of this program with any
 * version of the OpenSSL library which is distributed under a
 * license identical to that listed in the included COPYING.OpenSSL
 * file, and distribute linked combinations including the two.
 * You must obey the GNU General Public License in all respects
 * for all of the code used other than OpenSSL.  If you modify
 * this file, you may extend this exception to your version of the
 * file, but you are not obligated to do so.  If you do not wish to
 * do so, delete this exception statement from your version.
 */

/** @file
 * This is the Authentication Manager.
 */


#include <includes.h>
#include <stdarg.h>
#include "comm.h"
#include "auth.h"
#include "sighand.h"

/**
 * @brief Sends the login and password to the OpenVAS daemon.
 *
 * Note : this function does NOT check if the login/password are valid.
 *
 * @param user     Login users name.
 * @param password Login users password.
 *
 * @returns 0 if the login informations were sent successfully,
 *          -1 if a problem occured.
 */
int
auth_login (int soc, const char *user, const char* password)
{
  char * buf = emalloc(255);

  if ( soc <= 0 ) return -1;

  /* Note: even if we use SSLv3 authentication, we ask for a password anyway */
  network_gets(soc, buf, 7);
  if(strncmp(buf, "User : ", strlen(buf))) /* RATS: ignore, network_gets ensures nul termination (together with recv_line) */
    return(-1);
  network_printf(soc, "%s\n", user);

  bzero(buf, 255);
  network_gets (soc, buf, 11);
  if(strncmp(buf, "Password : ", strlen(buf)))  /* RATS: ignore, network_gets ensures nul termination (together with recv_line) */
    return(-1);
  network_printf(soc, "%s\n", password);
  efree(&buf);
  return(0);
}


/**
 * @brief This function sends a string to the server using a printf syntax.
 * 
 * @param soc  Socket over which to send data.
 * @param data Format string in printf fashion.
 */
void
network_printf (int soc, char * data, ...)
{
  va_list param;
  int r, s = 16384;
  char * buffer = emalloc(s);
  int len, n = 0;
  signal(SIGPIPE, sighand_pipe);

  if ( soc <= 0 ) soc = Context->socket;

  if(  soc <= 0 ) 
    return;

  for(;;)
  {
   va_start(param, data);
   r = vsnprintf(buffer, s, data, param);
   if(r >= 0 && r < s)break;
   s = r > s ? r + 1 : s * 2;
   buffer = erealloc(buffer, s);
  }
  len = strlen(buffer);
  while(n < len)
  {
   int m = 0;
   int size = 1024;
   /* send by packets of 1024 bytes due to a bug in libpeks */
   while(m < size)
   {
   int e;
   if((len - m - n) < size)size = len - m - n;
   e = nsend(soc, &(buffer[n+m]), size, 0);
   if(e < 0) {
     perror("send");
    return;
   }
   m+=e;
  }
  n+=m;
 }
  signal(SIGPIPE, SIG_IGN);
  va_end(param);
  efree(&buffer);
}


/**
 * @brief Reads data sent by the server into the provided buffer.
 * 
 * @param[out] s Buffer to read data into, will always be nul- terminated.
 * 
 * @return >0 is the amount of data placed in s
 *         <0 means there was an error.
 */
int
network_gets (int soc, char* s, size_t size)
{
  int n;

  if ( soc <= 0 )
    soc = Context->socket;

  if ( soc <= 0 )
    return 0;


  /* We are assuming that recv_line() will block until it has 
   * recvieved a full line of data, encountered a hard error, or eof 
   * (socket close?)
   * Also, recv_line will return 0 on error. */
  n = recv_line(soc, s, size);
  if (n > 0)
    return n;
  else
    s[0] = '\0';    /* zero the buffer */
  return -1;
}

/**
 * @brief Reads maximal size bytes from the socket soc into s and returns it,
 * @brief also stops at \\0's and \\n's.
 * 
 * Stops reading at '\0' and '\n' characters, and appends a terminating '\0' to
 * s.
 * 
 * @param soc  The socket (if NULL use current Contexts socket).
 * @param s    Pointer to the string (will not be set to NULL if NULL returned!).
 * @param size Number of bytes to maximally read from socket.
 * 
 * @return The '\0' terminated string s or NULL if an error occured.
 */
char*
network_gets_raw (int soc, char* s, size_t size)
{
  int n = 0, processed ;

  if (soc <= 0) soc = Context->socket;
  if (soc <= 0) return NULL;

  /* Read until no more data, or a line terminating character '\0'
     or '\n' is found */
  for (processed = 0; processed < (int)size; processed ++) {
    if ((n = nrecv (soc, s + processed, 1, 0)) <= 0) {
      /* On error, the characers read so far might be garbage */
      if (n < 0)
        processed = 0;
      break;
    }
    if (   s [processed] == '\0'
        || s [processed] == '\n')
      break;
  }

  /* Append a terminating 0 character, return NULL on empty read */
  if (processed + 1 == (int) size)
    processed-- ;

  s [processed] = '\0';
  if (!processed) s = 0;

  return s;
}
