/*
 * static char *rcsid_client_c =
 *    "$Id: client.c,v 1.20 1997/03/09 04:01:49 master Exp master $";
 */

/*
    CrossFire, A Multiplayer game for X-windows

    Copyright (C) 1992 Frank Tore Johansen

    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 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.

    The author can be reached via e-mail to frankj@ifi.uio.no.
*/

/* include "includes.h" so all the various system identifiers
 * (ie, __sgi__, __sun__, etc) are set up correctly.  A lot of
 * the include files could probably be deleted below.
 */

#include <includes.h>
#include <config.h>

#ifdef SERVER
#include <string.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <stdio.h>
#include <X11/Xlib.h>
#include <pwd.h>
#ifdef NCR
#include <sys/utsname.h>
#endif

#if defined(__sgi__) || defined(SVR4)
#ifndef SYS_NMLN
#define SYS_NMLN 257
#endif
#include <sys/systeminfo.h>
#endif
#if !defined (__STRICT_ANSI__) || defined (__sun__)
# if !defined (Mips) && !defined (vax) && !defined(ibm032)
#  include <stdlib.h>
# endif
# if !defined (MACH) && !defined (sony) && !defined (vax) && !defined(ibm032) \
	&& !defined (NeXT) && !defined(linux) && !defined(DMALLOC)
#  include <malloc.h>
# endif
#endif

#ifndef NAME_MAX
# ifdef _POSIX_NAME_MAX
#  define     NAME_MAX        pathconf("/dev/null", _POSIX_NAME_MAX)
# else
#  ifdef MAXHOSTNAMELEN
#   define    NAME_MAX        MAXHOSTNAMELEN
#  else
#   define    NAME_MAX        256
#  endif
# endif
#endif


#include <global.h> /* Sad to include the whole thing, only need libproto.h */
#include <version.h>

#ifndef __CEXTRACT__
#include "proto.h" /* My own prototyes, made by "make proto" */
#endif


char *font_graphic=FONTNAME;

/*
 * A replacement of strdup(), since it's not defined at some
 * unix variants.
 */
char *strdup_local(char *str) {
  char *c=(char *)malloc(sizeof(char)*(strlen(str)+1));
  strcpy(c,str);
  return c;
}

/*
 * This function adds the path to the fontpath of the given display.
 * It's mostly copied from the X11R5 distribution.
 */

void set_font_path(Display *dpy, char *path) {
  char **currentList = NULL; int ncurrent = 0;
  char **directoryList = NULL; int ndirs = 0;

  currentList = XGetFontPath (dpy, &ncurrent);
  if(currentList==NULL) {
    fprintf(stderr,"Unable to get old font path.\n");
    return;
  }
  {
    register char *cp = path;
    ndirs=1;
    while((cp=strchr(cp, ','))!=NULL)
      ndirs++,cp++;
    directoryList=(char **) malloc(ndirs*sizeof(char *));
    if(!directoryList) {
      fprintf(stderr,"Unable to allocate memory for font path directory.\n");
      return;
    }
  }
  {
    int i=0;
    char *cp = path;
    directoryList[i++]=cp;
    while((cp=strchr(cp, ','))!=NULL)
      directoryList[i++]=cp+1,
      *cp++='\0';
    if(i!=ndirs) {
      fprintf(stderr,"Internal error, only parsed %d of %d dirs.\n",i,ndirs);
      return;
    }
  }
  {
    int nnew=ndirs+ncurrent;
    char **newList = (char **) malloc (nnew * sizeof(char *));

    if(!newList) {
      fprintf(stderr,"Couldn't get memory for new fontpath.\n");
      return;
    }
/* #if defined(SYSV) || defined(SVR4) */
    memcpy((void *)newList,(void *)directoryList,
           (unsigned) (ndirs*sizeof (char *)));
    memcpy((void *) (newList + ndirs), (void *) currentList,
           (unsigned) (ncurrent*sizeof (char *)));
    XSetFontPath(dpy,newList, nnew);
    free((char *)newList);
  }
  if (directoryList)
    free((char *) directoryList);
  if (currentList)
    XFreeFontPath (currentList);
}

/*
 * Checks if "crossfire" is present somewhere in the fontpath of
 * the given display.
 */

int check_font_path(Display *dpy) {
  int count;
  char **list;

  list = XListFonts(dpy, font_graphic, 1, &count);
  fprintf(stderr, "Matching fonts to %s: %d (%s)\n",
      font_graphic,count,count?*list:"");
  XFreeFontNames(list);
  return count;
}

int
main(int argc, char **argv, char **env)
{
  char *dispname = (char *) getenv("DISPLAY");
  char localdispname[65+6];
  char *hostname = (char *) getenv("HOST");
  char *optcmd = (char *) NULL;
  char *ch, *p, *q;
  char *username;
  struct passwd *pwent;
  struct protoent *protox;
  Display *display;
  struct sockaddr_in insock;
  int i;
  char buf[MAX_BUF], buf2[MAX_BUF];
  int fd, fd2;
  FILE *fpin,*fpout;
  int set_pixmaps = 0, set_xpm = 0;
  int set_split = 0;
  int synchronize = (-1);

#if 0
  /*init_library();*/
  init_globals();
  init_function_pointers();
  init_defaults();
#endif

  for (i = 1; i < argc; i++)
  {
    int l = strlen(argv[i]);
    if(l > 1)
    {
      if (!strncmp(argv[i], "-server", l))
      {
        if (++i == argc)
        {
          fprintf(stderr, "You must specify a hostname after -server.\n");
          return 1;
        }
        hostname = argv[i];
        continue;
      }
      if (!strncmp(argv[i], "-display", l))
      {
        if (++i == argc)
        {
          fprintf(stderr, "You must specify the display after -display.\n");
          return 1;
        }
        dispname = argv[i];
        continue;
      }
      if (!strncmp(argv[i], "-debug", l))
      {
/*        debug = 1;*/
        continue;
      }
      if (!strncmp(argv[i], "-cmd", l))
      {
        if (++i == argc)
        {
          fprintf(stderr, "You must specify the command to run after -cmd.\n");
          return 1;
        }
        optcmd = argv[i];
        continue;
      }
      if (!strncmp(argv[i], "-pix", l))
      {
        set_pixmaps = 1;
        continue;
      }
      if (!strncmp(argv[i], "-xpm", l))
      {
        set_xpm = 1;
        continue;
      }
      if (!strncmp(argv[i], "-split", l) || !strcmp("w", argv[i]))
      {
        set_split = 1;
        continue;
      }
      if (!strncmp(argv[i], "-synchronize", l))
      {
        if (++i == argc || !sscanf(argv[i], "%d", &synchronize))
        {
          fprintf(stderr, "You must specify a number after -synchronize.\n");
          return 1;
        }
        continue;
      }
      if (!strncmp(argv[i], "-help", l))
        cl_usage(argv[0]);
    }
    fprintf(stderr,"Unknown argument: %s.\n",argv[i]);
    cl_usage(argv[0]);
  }
  if (hostname == (char *) NULL)
  {
    fprintf(stderr, "You must set the HOST environment variable.\n");
    fprintf(stderr, "(Or specify server with the -server option.)\n");
    return 1;
  }

  if ((pwent = getpwuid(getuid())) == (struct passwd *) NULL)
  {
    fprintf(stderr, "Can't find your username.\n");
    return 1;
  }
  username = strdup_local(pwent->pw_name);


  if (optcmd == (char *) NULL)
  {
    if (dispname == (char *) NULL)
    {
      fprintf(stderr, "You must set the DISPLAY environment variable.\n");
      fprintf(stderr, "(Or specify it with the -display option.)\n");
      return 1;
    }
    if (*dispname == ':')
    {
      char localhostname[65+6];

#ifdef SVR4
      if (sysinfo(SI_HOSTNAME, localhostname, NAME_MAX) <0) {
	fprintf(stderr, "Can't get local hostname.\n");
	perror("%s: SI_HOSTNAME");
      }
#else   /* Assume BSD */
      if (gethostname(localhostname, 65))
      {
	fprintf(stderr, "Can't get local hostname.\n");
	perror("gethostname ");
      }
#endif
      sprintf(localdispname, "%s%s", localhostname, dispname);
      dispname = localdispname;
    }

    if ((display = XOpenDisplay(dispname)) == (Display *) NULL)
    {
      fprintf(stderr, "Can't open display %s.\n",dispname);
      return 1;
    }
    if (!set_pixmaps && !set_xpm && !check_font_path(display))
    {
      fprintf(stderr,"Trying to fix fontpath for %s.\n",dispname);
      fflush(stderr);
      set_font_path(display,FONTDIR);
      if (!check_font_path(display))
      {
        fprintf(stderr,"Failed to fix the fontpath.\n");
        return 1;
      }
    }
    XCloseDisplay(display);
  }

  /* End of X-stuff, over to socket-stuff... */

  protox = getprotobyname("tcp");
  if (protox == (struct protoent  *) NULL)
  {
    fprintf(stderr, "Error getting prorobyname (tcp)\n");
    return 1;
  }
  fd = socket(PF_INET, SOCK_STREAM, protox->p_proto);
  if (fd == (-1))
  {
    perror("Error on socket command");
    return 1;
  }
  insock.sin_family = AF_INET;
  insock.sin_port = htons((unsigned short)PORT);
  if (isdigit(*hostname))
  {
    insock.sin_addr.s_addr = inet_addr(hostname);
    sprintf(buf,"xhost +%s",hostname);
  }
  else
  {
    struct hostent *hostbn = gethostbyname(hostname);
    if (hostbn == (struct hostent *) NULL)
    {
      fprintf(stderr,"Unknown host: %s\n",hostname);
      return 1;
    }
#if defined(__sun__) || defined(SVR4)
    memcpy(&insock.sin_addr, hostbn->h_addr, hostbn->h_length);
#else
    bcopy(hostbn->h_addr, &insock.sin_addr, hostbn->h_length);
#endif
    sprintf(buf,"xhost +%s",hostbn->h_name);
  }
#ifndef SECURE	/* I don't like xhost + */
  if (optcmd == (char *) NULL)
  {
    fprintf(stderr,"%s\n",buf);
    system(buf);
  }
#endif
  if (connect(fd,(struct sockaddr *)&insock,sizeof(insock)) == (-1))
  {
    perror("Can't connect to server");
    return 1;
  }
  fd2 = dup(fd);
  fpin = fdopen(fd,"r");
  fpout = fdopen(fd2,"w");
  fputs("version\n",fpout);
  fflush(fpout);
  fgets(buf, MAX_BUF-1, fpin);
  if (!strncmp(buf, "Welcome",7)) {
    if (fgets(buf, MAX_BUF-1, fpin)==NULL) {
      fprintf(stderr,"Syntax error in remote version: %s",buf);
      return 1;
    }
  }
  if ((ch = strchr(buf, 'v')) == NULL) {
      fprintf(stderr,"Syntax error in remote version: %s",buf);
      return 1;
  }
  else
  if(compare_versions(++ch))
    return 1;

  fprintf(fpout,"listen 0\n");
  fflush(fpout);
  if (fgets(buf, MAX_BUF-1, fpin) == NULL || strcmp(buf,"OK.\n"))
  {
    fprintf(stderr, "Failed to set listen to 0: %s", buf);
    return 1;
  }

  fprintf(fpout,"name %s\n",username);
  fflush(fpout);
  if (fgets(buf, MAX_BUF-1, fpin) == NULL || strcmp(buf,"OK.\n"))
  {
    fprintf(stderr, "Failed to set name: %s", buf);
    return 1;
  }
  if (set_xpm)
  {
    fputs("set xpm\n", fpout);
    fflush(fpout);
    if (fgets(buf, MAX_BUF-1, fpin) == NULL || strcmp(buf,"OK.\n"))
    {
      fprintf(stderr, "Failed to set XPM mode (server might not have it enabled): %s", buf);
      return 1;
    }
  }
  else if (set_pixmaps)
  {
    fputs("set pixmaps\n", fpout);
    fflush(fpout);
    if (fgets(buf, MAX_BUF-1, fpin) == NULL || strcmp(buf,"OK.\n"))
    {
      fprintf(stderr, "Failed to set pixmaps: %s", buf);
      return 1;
    }
  }
  else {
    fputs("set font\n", fpout);
    fflush(fpout);
    if (fgets(buf, MAX_BUF-1, fpin) == NULL || strcmp(buf,"OK.\n"))
    {
      fprintf(stderr, "Failed to select font mode: %s", buf);
      return 1;
    }
  }
  if (set_split)
  {
    fputs("set split\n", fpout);
    fflush(fpout);
    if (fgets(buf, MAX_BUF-1, fpin) == NULL || strcmp(buf,"OK.\n"))
    {
      fprintf(stderr, "Failed to set split windows: %s", buf);
      return 1;
    }
  }
  if (synchronize != (-1))
  {
    fprintf(fpout, "synchronize %d\n", synchronize);
    fflush(fpout);
    if (fgets(buf, MAX_BUF-1, fpin) == NULL || strcmp(buf,"OK.\n"))
    {
      fprintf(stderr, "Failed to set synchronize: %s", buf);
      return 1;
    }
  }
  if (optcmd != NULL) {
    cl_set_protocol(fpin, fpout, 1);
    strcpy (q = buf2, optcmd);
    do {
	if ((p = strchr (q, ';')))
	    *p = '\0';
	fprintf(fpout,"%s\n", q);
	q = p + 1;
	fflush(fpout);
	while(fgets(buf, MAX_BUF - 1, fpin) != NULL && strcmp(buf,"EOT\n"))
	    fprintf(stderr,"%s",buf);
    } while (p);
  }
  else /* Default command is add... */
  {
    fprintf(fpout,"add %s\n",dispname);
    fflush(fpout);
    if (fgets(buf, MAX_BUF - 1, fpin) == NULL || strcmp(buf,"OK.\n"))
    {
      fprintf(stderr,"Failed to add %s: %s",dispname,buf);
      return 1;
    }
  }
  fputs("quit\n",fpout);
  fflush(fpout);
  if (fgets(buf, MAX_BUF - 1, fpin) != NULL)
  {
    fclose(fpin);
    fclose(fpout);
  }
  close(fd2); /* Hmm, why do I bother... */
  close(fd);
  return 0;
}

void
cl_set_protocol(FILE *fpin, FILE *fpout, int p)
{
  char buf[256];
  fprintf(fpout,"protocol %d\n",p);
  fflush(fpout);
  if (fgets(buf, 250, fpin) == NULL || strcmp(buf,"OK.\n"))
  {
    fprintf(stderr,"Failed to set protocol.\n");
    exit(1);
  }
  if (p == 1)
    if(fgets(buf, 250,fpin) == NULL || strcmp(buf,"EOT\n"))
    {
      fprintf(stderr, "Protocol 1 out of order.\n");
      exit(1);
    }
}

int
compare_versions(char *remote_ver)
{
#if 1
  return 0;
#else
  int remote_version,remote_subversion,remote_patchlevel;
  int client_version,client_subversion,client_patchlevel;
  int remote_sum, client_sum;

  if(sscanf(remote_ver,"%d.%d.%d",
            &remote_version,&remote_subversion,&remote_patchlevel) != 3)
  {
    fprintf(stderr, "Failed to decode remote version: %s\n",remote_ver);
    return 1;
  }
  sscanf(VERSION,"%d.%d",&client_version,&client_subversion);
  sscanf(PATCH,".%d",&client_patchlevel);
  remote_sum = remote_version*10000 + remote_subversion*100 + remote_patchlevel;
  client_sum = client_version*10000 + client_subversion*100 + client_patchlevel;
  if (remote_sum > client_sum)
  {
    fprintf(stderr,"You will need to get a newer version of crossclient,\n");
    fprintf(stderr,"at least version %s (this is %s%s)\n",
            remote_ver,VERSION,PATCH);
    return 1;
  }
  if (remote_sum < client_sum)
  {
    fprintf(stderr,
            "Warning: The remote version is older than your client version.\n");
    fprintf(stderr,"(It should still be backward compatible with your font)\n");
  }
  return 0;
#endif
}

void
cl_usage(char *progname)
{
  char *ch = strrchr(progname, '/');
  fprintf(stderr, "Usage: %s [options]\n",
          (ch == (char *) NULL ? progname : ch + 1));
  fprintf(stderr,"Options:\n");
  fprintf(stderr,
	"-server <name>   - Connect to <name> instead of \"localhost\".\n");
  fprintf(stderr,
	"-display <name>  - Use <name> instead of the DISPLAY environment var.\n");
  fprintf(stderr,
	"-pix             - Use pixmaps instead of fonts.\n");
  fprintf(stderr,
	"-xpm             - Use color pixmaps (XPM) instead of fonts.\n");
  fprintf(stderr,
	"                   Note: Some servers may not support this\n");
  fprintf(stderr,
	"-split           - Use split windows.\n");
  fprintf(stderr,
	"-cmd <command>   - Specify an other command than the default \"add\".\n");
  fprintf(stderr,
	"                   Try \"help\" as <command> to see what is allowed.\n");
  fprintf(stderr,
	"-debug           - Turn on debugging.\n");
  fprintf(stderr,
	"-synchronize <nr>- Specifies frequency of synchronize Vs flush.\n");
  fprintf(stderr,
	"-help            - Display this information.\n");
  exit(0);
}
#else
#include <stdio.h>

#ifdef __sun__
int fprintf();
#endif

int
main(int argc, char **argv, char **env)
{
  (void) fprintf(stderr,
                 "You have to define \"SERVER\" in include/config.h if you\n");
  (void) fprintf(stderr,"want the client to work.\n");
  return 0;
}
#endif

