#define I_IOCTL
#define I_SYS
#define I_GETOPT
#define I_SOCKET
#define I_STAT
#define I_SIGNAL
#define I_STRING
#define I_ARGS
#define I_INET
#include "includes.h"

#include "client.h"

char *term_server = "";

int lcompression = -1;		/* defaults. */
int rcompression = -1;
int priority = 0;
char com_result[1024];
char *command_result;
int savedeid     = -1;
long unsigned remote_term_version = 0;

static int try_connect_server(int S,char *server) {
  char path[1024];
  char *home_p=NULL, *share_p=NULL, *p=NULL;
  int s;
  
  if (!server) server = "";

  home_p = getenv( "TERMDIR" );
  if (!home_p) home_p = getenv( "HOME" );

#ifdef SHAREDIR
#ifndef USERSHARE
  share_p = getenv( "TERMSHARE" );
#endif
  if (!share_p) share_p = SHAREDIR ":/usr/lib/term:/etc";

  if (share) {

#ifdef _POSIX_SAVED_IDS
    if (savedeid >= 0)
#ifdef USERSHARE
      setuid(savedeid);
#else
      setgid(savedeid);
#endif
#endif
    for(p = share_p;p != NULL; p = strchr(p,':')){
      if (*p == ':') p++;
      strcpy(path,p);
      if (strchr(path,':') != NULL) *strchr(path, ':') = '\0';
      strcat(path,"/tmp/private");
      if (eaccess(path, X_OK & R_OK) < 0) continue;
      break;
    };
#ifdef _POSIX_SAVED_IDS
#ifdef USERSHARE
    setuid(getuid());
#else
    setgid(getgid());
#endif
#endif
  }else
#endif
  {
    if(!home_p) home_p = "/tmp";
    for(p = home_p;p != NULL; p = strchr(p,':')){
      if(*p == ':') p++;
      strcpy(path,p);
      if (strchr(path,':') != NULL) *strchr(path, ':') = '\0';
      strcat(path,"/.term");
      if (access(path, X_OK & R_OK) < 0) continue;
      break;
    };
  };

  if(p == NULL) return -1;
  sprintf(&path[strlen(path)],"/socket%s", server);

				/* Try and connect to term. */
#if defined(_POSIX_SAVED_IDS) && defined(SHAREDIR)
  if (savedeid >= 0)
#ifdef USERSHARE
    setuid(savedeid);
#else
    setgid(savedeid);
#endif
#endif
  s = connect_unix(S,path);
#if defined(_POSIX_SAVED_IDS) && defined(SHAREDIR)
#ifdef USERSHARE
  setuid(getuid());
#else
  setgid(getgid());
#endif
#endif

  if(s<0){
    if(term_debug>=0) fprintf(stderr,"Term: Failed to connect to socket %s\n",path);
    return -1;
  };

  if ( lcompression >=0 )
    send_command(s, C_COMPRESS, 1, "%d", lcompression);
  if ( rcompression >=0 )
    send_command(s, C_COMPRESS, 0, "%d", rcompression);

  if (priority) {
    send_command(s, C_PRIORITY, 1, "%d", priority);
    send_command(s, C_PRIORITY, 0, "%d", priority);
  }

  return s;
}

				/* parse all the options, and then */
				/* return the first unused argument. */
int client_options(int argc, char *argv[], char *myopts, Callback callback)
{
  int c;
  char args[200];

  /* make sure we get all the args to pass to getopt */
  if (getenv("TERMSERVER"))
    term_server = getenv("TERMSERVER");


#ifdef SHAREDIR
  strcpy(args, "t:crp:S:");
#else
  strcpy(args, "t:crp:");
#endif
  strcat(args, myopts);

  while ((c = getopt(argc, argv, args))!=EOF) {
    switch(c) {
    case 't':
      term_server = optarg;	
      break;
    case 'c': /* for compressionion */
      lcompression = 1;
      rcompression = 1;
      break;
    case 'r': /* for raw, no compressionion */
      lcompression = 0;
      rcompression = 0;
      break;
    case 'p':
      priority = atoi(optarg);
      fprintf(stderr, "Changeing priority to %d\n", priority);
      break;
#ifdef SHAREDIR
    case 'S':
      share = (!strcmp(optarg,"on"));
      break;
#endif
    case '?':
      return -1;
      break;
    default:
      if ( (callback == NULL) || ( callback(c, optarg) == -1 ) )
	{
	  fprintf(stderr, "Unrecognized option %s\n", argv[optind]);
	  return -1; 
	}
      break;
    }
  }
  return optind;
}
				/* Send a command to the Term */
				/* process.*/ 
#ifdef USE_VARARGS
int send_command(sock , comm, local , fmt, va_alist)
int sock , comm, local;
char *fmt;
va_dcl {
#else
int send_command(int sock, int comm, int local, char *fmt, ...) {
#endif
  char buff[1024];
  va_list v;
  int i;
#ifdef USE_VARARGS
  va_start(v);
#else
  va_start(v, fmt);
#endif
				/* A command follows.. */
  buff[0] = C_SWITCH;
				/* Is it for the local term? or the */
				/* remote term? */
  if (local)
    buff[1] = C_SWITCH - 1;
  else
    buff[1] = C_SWITCH - 2;
				/* Set the command in. */
  buff[2] = comm;
  if (fmt)
    vsprintf(buff+3, fmt, v);
  else
    buff[3] = 0;

  va_end(v);
				/* Ok. Buff holds the entire command. */
				/* Lets do the damned thing.. */
  if (write(sock, buff, strlen(buff)+1) < 0)
    return -1;
				/* If the command is C_DUMB, then no */
				/* reply will be forthcoming. */
  if ((comm == C_DUMB) || (comm == C_QUIT)) return 1;

  i = 0;
  do {
    read(sock, &com_result[i], 1);
  } while (com_result[i++]);
  command_result = &com_result[3];

  if (com_result[2] != I_OK)
    return -1;
/*  printf("command return was (%d)%s.\n", i, com_result);*/
  return 1;
}


/* This is a short routine to allow the user to try to connect a socket
 * to term.  If a negative file descriptor is supplied, a new socket 
 * will be created.
 */

int socket_connect_server(int S,char *server) {
  int s = -1;
#ifdef SHAREDIR

  set_share_mode();
	/* If "share" is still undecided, I try both shared and unshared. */
  if(share==-1){
    share=1;
    set_share_mode();
    s=try_connect_server(S,server);
    if ( s < 0 ) share=!share;
  };
#endif
  if ( s < 0 ) 
    s=try_connect_server(S,server);
  if ( s < 0 )
    return -1;
  if (remote_term_version) return s;
  if (send_command(s, C_STATS, 1, "%d", -10) >= 0) 
    sscanf(command_result,"VERSION %lu",&remote_term_version);
  if (! remote_term_version) {
    send_command(s, C_STATS, 0, "%d", -1);	/* This is a dummy command */
    if (send_command(s, C_STATS, 1, "%d", -10) >= 0) 
      sscanf(command_result,"VERSION %lu",&remote_term_version);
    if (! remote_term_version)  
      remote_term_version = 10800;
  }
  return s;
}

/* This is a short routine to allow the user to try to connect to a term
 * server.  The main difference between this and socket_connect_server with
 * a negative file descriptor, is this routine will never return if the
 * connection attept fails.
 */

int connect_server(char *server) {
  int s = -1;
#ifdef SHAREDIR

  set_share_mode();
	/* If "share" is still undecided, I try both shared and unshared. */
  if(share==-1){
    share = 1;
    s = try_connect_server(-1,server);
    if (s < 0) share = 0;
  };
#endif
  if (s < 0) s = try_connect_server(-1,server);
  if (s < 0) exit(1);
  if (remote_term_version) return s;
  if (send_command(s,C_STATS, 1, "%d", -10) >= 0) 
    sscanf(command_result,"VERSION %lu",&remote_term_version);
  if (! remote_term_version) {
    send_command(s, C_STATS, 0, "%d", -1);	/* This is a dummy command */
    if (send_command(s, C_STATS, 1, "%d", -10) >= 0) 
      sscanf(command_result,"VERSION %lu",&remote_term_version);
    if (! remote_term_version)  
      remote_term_version = 10800;
  }
  return s;
}


/* build_arg: build a string from char ** argv to be passed 
 * to C_EXEC family. 
 *
 * by: croutons
 *
 * Notes:
 *  returns a pointer to malloced space.
 *  takes a null pointer as the end of the array of char*.
 *  assumes null terminated strings. (for using string(3))
 *  returns NULL on error.
 *
 *  we assume '\377' is ok for the new terminator
 */
char *build_arg( char **arg )
{
	int i, s;
	static char *f = NULL;
        char *term = NULL;

        free(f);
	if (! arg) return NULL;
        if (*arg == NULL) return NULL;

	for (s = i = 0; NULL != arg[i]; i++) s += strlen(arg[i]);

        if (remote_term_version >= 11715) {
          term = getenv("TERM");
          if (term != NULL)
            s += strlen(term) + strlen("-DTERM=") + 1;
        }
        s += i + 2;

	if ( NULL == (f = (char *)malloc( s * sizeof(char) ) ) ) 
		return NULL;

		/* This passes the terminal type */

        if (term != NULL) 
          sprintf(f,"-DTERM=%s%c",term,'\377');
        else
          f[0] = '\0';

 	for ( i = 0; NULL != arg[i]; i++ ) {	
		strcat(f,arg[i]);
                strcat(f,"\377");
        }

	return f;
}


