

/*
 * srvio --- lowish-level server communication
 */


#include <stdio.h>
#include <netdb.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <sys/errno.h>
#include <netinet/in.h>
#include "client.h"
#include "config.h"
#include "sfrp.h"

int srvio_debug = 0;

read_server_response (con, buf, siz)	/* reads a _single line_ of a server response */
     Connection *con;			/* basically, a glorified gets */
     char *buf;
     int siz;
{
  struct timeval tm;
  fd_set fds, efds;
  char *c = buf;
  char *nl;
  int left = siz;
  int n;
  
  if (left)
    buf[--left] = 0;
  else
    return;
  if (con->closed) {
  closed:
    sprintf (buf, "%03d ", SERVERDIEDOUT);
    return;
  }
  if (n = con->pushedback) {
    if (n > left) n = left;
    bcopy (con->pushback, buf, n);
    c += n;
    left -= n;
    con->pushedback -= n;
    if (con->pushedback)
      bcopy (con->pushback + n, con->pushback, con->pushedback);
  }
  while (left) {
    *c = 0;
    if (nl = index(buf, '\n'))
      break;
    tm.tv_sec = CLIENT_TIMEOUT;
    tm.tv_usec = 0;
    FD_ZERO (&fds);
    FD_SET (con->fd, &fds);
    FD_ZERO (&efds);
    FD_SET (con->fd, &efds);
    if (select(FD_SETSIZE, &fds, 0, &efds, &tm) > 0) {
      if (FD_ISSET(con->fd, &fds)) {
	n = PUSHBACKLEN - con->pushedback;
	if (left < n) n = left;
	if ((n = read (con->fd, c, n)) > 0) {
	  c += n,
	  left -= n;
	} else if (n == 0) 
	  goto closing;
	else {
	  if (srvio_debug) {
	    fprintf (stderr, "read returns %d ... ", n);
	    perror ("");
	  }
	  goto closing;
	}
      } else if (FD_ISSET(con->fd, &efds)) {
      closing:
	con->closed = 1;
	if (c == buf)
	  goto closed;
	nl = c;
	*c++ = '\n',
	left--;
      }
    } else {
      if (errno == EBADF)
	goto closing;
    }
  }
  if (nl) {
    *nl++ = 0;
    if (n = c - nl) {
      if (con->pushedback)
        bcopy (con->pushback, con->pushback + n, con->pushedback);
      bcopy (nl, con->pushback + con->pushedback, n);
      con->pushedback += n;
    }
  }
  if (srvio_debug) 
    printf ("<-- %s\n", buf);
}


char *parse_server_response (resp, code, more) /* Parse out the "###" part of the response */
     char *resp;			/*   Returns pointer to first interesting character */
     int *code;				/*   Return: first three digits of response line */
     int *more;				/*   Return: 0 means last line; 1 means more lines coming */
{
  if (strlen(resp) < 4) {
    *code = 500;
    *more = 0;
    return resp;
  } else {
    *code = atoi(resp);
    *more = (resp[3] == '-');
    return resp + 4;
  }
}


read_server_code (con, code, more)	/* Reads just the "###" part of the response */
     Connection *con;
     int *code;				/*   Return: first three digits of response line */
     int *more;				/*   Return: 0 means last line; 1 means more lines coming */
{
  char buf[1024];

  read_server_response (con, buf, sizeof(buf));
  (void) parse_server_response (buf, code, more);
}


write_server (con, buf, siz)
     Connection *con;
     char *buf;
     int siz;
{
  int n;

  if (srvio_debug) {
    write (1, "--> ", 4);
    write (1, buf, siz);
    if (buf[siz-1] != '\n')
      write (1, "\n", 1);
  }
  if ((n = write (con->fd, buf, siz)) != siz) 
    if (n == -1) {
      perror ("failed to write server");
      exit (1);
    } else
      fprintf (stderr, "bizarre: only could write %d of %d bytes\n", n, siz);
}


open_server(server_host, con)		/* Fills in connection structure*/
     char *server_host;
     Connection *con;			/* Gets and sends kerberos ticket, too! */
{					
  static struct sockaddr_in server_addr;
  static int inited = 0;
  int fd;
  int code, more;
  
  if (!inited) {
    struct hostent *hp;
    inited = 1;
    hp = gethostbyname (server_host);
    if (!hp) {
      fprintf ("Can't resolve server host %s\n", server_host);
      exit(1);
    }
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(PORT_NUMBER);
    server_addr.sin_addr.s_addr = *((u_long *) hp->h_addr_list[0]);
    printf ("Opening connection to %s\n", inet_ntoa(server_addr.sin_addr));
  }
  fd = socket (AF_INET, SOCK_STREAM, 0);
  con->pushedback = 0;
  con->fd = fd;
  con->hasvolume = 0;
  strcpy (con->mountpoint, "<unknown>");
  con->daysleft = 9999;
  if (connect(fd, &server_addr, sizeof(server_addr))) {
    perror ("connect to server");
    con->closed = 1;
    return;
  }
#ifdef KERBEROS
  {
    u_short ticket_size;
    KTEXT_ST ticket;
    char hbuf[64], *c;
    int stat;
    
    bcopy (server_host, hbuf, sizeof(hbuf));
    hbuf[sizeof(hbuf)-1] = 0;
    if (c = index(hbuf, '.')) *c = 0;
    if (stat = get_authenticator(hbuf, &ticket)) {
      fprintf (stderr, "kerberos failure: %s\n", krb_err_txt[stat]);
      exit(1);
    }
    ticket_size = (u_short) htons((short) ticket.length);
    write (fd, &ticket_size, 2);
    write (fd, ticket.dat, ticket.length);
  }
#else
  {
    u_short uname_size;
    char *uname;

    uname = (char *) getenv("USER");
    uname_size = (u_short) htons((short) strlen(uname));
    write (fd, &uname_size, 2);
    write (fd, uname, strlen(uname));
  }
#endif  
  do 
    read_server_code(con, &code, &more);
  while (more);
  if (ISERROROUT(code))
    fprintf (stderr, "Server refused connection (code %d)\n", code);
}


