/* TCP Network Objects -- Implementation
   by Ian Smith 
   of The Software Engineering Research Center's Multimedia Lab */

/* credit where credit is due: I must give a lot of the credit for
these objects to some employee of saber software, whose name eludes
me.  Along time ago, I read a paper called "Sockets: a Quick and Dirty
Primer" by an employee of that company and got alot of the information
and motivation for this code.  In fact, some of this code may be
verbatim from the paper, but I don't know as I have hacked up this so
many times that it is hard to tell what is from where.  Also, to the
peole who wrote the BSD supplemental papers, thank you, I'm in your
debt. Nobody learns IPC without IPC primers from the berkely docs. A
thank you to Keith Edwards for his information and encouragemnt,
without which nothing would get done.  Also, thanks to Henry
Strickland for some info on socketops.  */

#import <objc/objc-runtime.h>
#import <objc/Object.h>
#import "tcp.h"
#import <stdio.h>
#import <sys/types.h>
#import <sys/time.h>
#import <sys/socket.h>
#import <string.h>
#import <netdb.h>
#import <netinet/in.h>
#import <errno.h>
#import <stdlib.h>

#define MAXHOSTNAME 64
#define FLAG 10000 
#define BUFSIZE 1024

@implementation network_tcp

- (int) setup:(int) port;
{
	char myname[MAXHOSTNAME+1];
    	int s,len,one=1;
 	struct sockaddr_in sa;
 	struct hostent *hp;
        struct linger ling;

	bzero(&sa,sizeof(struct sockaddr_in));
	gethostname(myname,MAXHOSTNAME);
	hp= gethostbyname(myname);
	if (hp==NULL) {
	  fprintf(stderr,"tcp error:gethostbyname\n");
	  return(-1);
	}
	sa.sin_family=hp->h_addrtype;
	sa.sin_port=htons(port);
	if ( (s=socket(AF_INET,SOCK_STREAM,0)) < 0) {
	  fprintf(stderr,"tcp error:socket\n");
	  return(-1) ;
	  }
	if (bind(s,(struct sockaddr *)&sa,sizeof(sa)) < 0) {
	  fprintf(stderr,"tcp error:bind\n");
	  return(-1);
	}
	listen(s,5);
	ling.l_onoff=0;
	ling.l_linger=0;
        len=sizeof(ling);
        setsockopt(s,SOL_SOCKET,SO_LINGER,(char *)&ling,len);
	len=sizeof(int);
	setsockopt(s,SOL_SOCKET,SO_REUSEADDR,(char *)&one,len);
	sock = s;
	return(1);
}

- (int) receive;
{
	struct sockaddr_in isan;
	int i,t,rt;

	
	i=sizeof(isan);
	getsockname(sock,(struct sockaddr *)&isan,&i);
	if ((t=accept(sock,(struct sockaddr *)&isan,&i)) < 0)  {
	  fprintf(stderr,"tcp error:accept\n");
	  return(-1);
	}
	fd = t;
	return(1);

}
- (int) call:(char *)target onport :(int) port;
{
  struct sockaddr_in sa;
  struct hostent *hp;
  int a,s,f,rt;
  char buff[80];

  if ((hp=gethostbyname(target))==NULL) {
      errno=ECONNREFUSED;
      fprintf(stderr,"tcp error:gethostbyname\n");
      return(-1);
    }
  bzero(&sa,sizeof(sa));
  bcopy(hp->h_addr,(char *)&sa.sin_addr,hp->h_length);
  sa.sin_family=hp->h_addrtype;
  sa.sin_port=htons((u_short)port);
  s=socket(hp->h_addrtype,SOCK_STREAM,0);
  if ( s < 0 ) {
    fprintf(stderr,"tcp error:socket\n");
    return(-1);
  }
  if (connect(s,(struct sockaddr *)&sa,sizeof(sa)) < 0) {
      shutdown(s,2);
      fprintf(stderr,"tcp error:connection refused\n");
      return(-1);
    }
  fd =(int) s;
  return(1);
}
- (int) send_text:(char *)string;
{
  int ln,err;

  ln=strlen(string)+1;
  err=write(fd,&ln,sizeof (int) );
  if (err!=sizeof(int)) {
    fprintf(stderr,"tcp error:write\n");
    return(err);
  }
  err=write(fd,string,ln);
  if (err!=ln){
    fprintf(stderr,"tcp2 error:write\n");
    return(err);
  }
  return(1);
}
- (int) send_data:(void *)data ofsize: (int) size;
{
  int err;
  err=write(fd,&size,sizeof(int));
  if (err!=sizeof(int)) {
    fprintf(stderr,"tcp error:write\n");
    return(err);
  }
  err=write(fd,data,size);
  if (err!=size) {
    fprintf(stderr,"tcp error:write\n");
    return(err);
  }
  return(1);

}


- (int) send_file:(char *)fname;
{
  char buf[BUFSIZE];
  int ln,done=0,err;
  FILE *fp;
  
  fp=fopen(fname,"r");
  if (fp==NULL) {
    fprintf(stderr,"tcp error:unable to open file %s \n",fname);
    return(-1);
  }
  fseek(fp,0L,SEEK_SET);
  while (!done) {
    ln=fread(buf,1,BUFSIZE,fp);
    if (feof(fp)) done=1; 
    else done=0;
    err=[self send_data:(void *)buf ofsize:ln];
    if (err==-1) {
      fprintf(stderr,"tcp error:send_data in file transfer\n");
      return(-1);
    }
    err=[self send_data:(void *)&done ofsize:sizeof(int)];
    if (err==-1) {
      fprintf(stderr,"tcp error:send_data in file transfer\n");
      return(-1);
    }
  }
  fclose(fp);
  return(1);
}
- (int) recv_text:(char *)string;
{
  int err,ln;

  err=read(fd,&ln,sizeof(int));
  if (err!=sizeof(int)) {
    fprintf(stderr,"tcp error:read\n");
    return(err);
  }
  err=read(fd,string,ln);
  if (err!=ln) {
    fprintf(stderr,"tcp2 error:read\n");
    return(err);
  }
  return(1);
}
- (int) recv_data:(void *)data ofsize: (int *)size;
{
  int err,amt;

  err=read(fd,&amt,sizeof(int));
  if (err!=sizeof(int)) {
    fprintf(stderr,"tcp error:read\n");
    return(err);
  }
  err=read(fd,data,amt);
  if (err!=amt) {
    fprintf(stderr,"tcp2 error:read\n");
    return(err);
  }
  *size=amt;
  return(1);
}
- (int) recv_file:(char *)fname;
{
  int ln,done=0,err,tmp;
  char buf[1024];
  FILE *fp;

  fp=fopen(fname,"w");
  if (fp==NULL) {
    fprintf(stderr,"tcp error: unable to open file for writing\n");
    return(-1);
  }
  fseek(fp,0L,SEEK_SET);
  while (!done) {
    err=[self recv_data:(void *)buf ofsize:&ln];
    if (err==-1) {
      fprintf(stderr,"tcp error: recv_data in file transfer\n");
      return(-1);
    }
    err=[self recv_data:(void *)&done ofsize:&tmp];
    if (err==-1) {
      fprintf(stderr,"tcp error: recv_data in file transfer\n");
      return(-1);
    }
    err=fwrite(buf,1,ln,fp);
    if (err==0) {
      fprintf(stderr,"tcp error: unable to write to disk\n");
      return(-1);
    }
  }
  fclose(fp);
  return(1);
}
  
- (int) call_waiting;
{
  fd_set fds;
  struct timeval to;
  int n;

  to.tv_sec=0;
  to.tv_usec=0;
  FD_ZERO(&fds);
  FD_SET(sock,&fds);
  n=select(FD_SETSIZE,&fds,0,0,&to);
  return(n);
}
- (int) data_waiting;
{
  fd_set fds;
  struct timeval to;
  int n;

  to.tv_sec=0;
  to.tv_usec=0;
  FD_ZERO(&fds);
  FD_SET(fd,&fds);
  n=select(FD_SETSIZE,&fds,0,0,&to);
  return(n);

}

- (int) initialize;
{
  fd=0;
  sock=0;
  return(1);
}

- (int) close_tcp ;
{
  if (fd!=0) close(fd);
  if (sock!=0) shutdown(sock,2);
  return(1);
}
