Logo Search packages:      
Sourcecode: yics version File versions  Download package

main.c

/*
 * YICS: Connect a FICS interface to the Yahoo! Chess server.
 * Copyright (C) 2004  Chris Howie
 *
 * 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.,
 * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <time.h>

#include "platform.h"

#ifdef _YICS_POSIX
#include <unistd.h>
#endif

#ifdef __GNUC__
#include <getopt.h>
#endif

#include "sockets.h"
#include "types.h"
#include "console.h"
#include "network.h"
#include "opcodes.h"
#include "ropcodes.h"
#include "version.h"
#include "util.h"
#include "globals.h"
#include "command.h"
#include "debug.h"
#include "vars.h"
#include "http.h"

/* server info */
static char s_hostname[1024] = "";
static unsigned short s_port = 0;
static char s_room[256] = "";
static char s_cookie[2048] = "";
static char s_ycookie[2048] = "";
static char s_region[8] = "us";
static char s_uagent[256] = "";

static void dologin(void);
static void getparams(void);

typedef struct {
      bool isSocket;
      union {
            FILE *file;
            int socket;
      } fd;
} SocketFile;

#define s_fgets(buf,max,sf) (sf.isSocket ? ngets(buf,max,sf.fd.socket) : fgets(buf,max,sf.fd.file))

int main(int argc, char *argv[]) {
      static char inbuf[2048];
      char inc;
      int inbufp = 0;
      String *info = NULL;
      int flag;
#if defined(_YICS_WIN32)
      int idle;
      int curdelay = 0;
#endif

      memset(players, sizeof(players), 0);
      memset(tables, sizeof(tables), 0);

      for (;;) {
            flag = getopt(argc, argv, "l:");
            if (flag == -1)
                  break;

            if (flag == 'l') {
                  netlog = fopen(optarg, "wb");
                  if (netlog == NULL)
                        dief("Unable to open %s: %s", optarg,
                              strerror(errno));
            } else if (flag == '?') {
                  exit(1);
            }
      }

      sysiprint("\n"
            "Welcome to YICS!  If you need any help, please consult the "
            "YICS documentation, available at http://wiki.yics.org .\n\n"

            "Lead programmer: crazycomputers\n"
            "Windows port:    websnarf\n"
            "Version:         ");
      sysiprint(VERSION);
      sysiprint("\n"
            "Website:         http://www.yics.org\n\n"

            "To log in, enter your Yahoo! ID, your password, and the "
            "room ID of the room you want to enter.  For a list of room "
            "IDs, see http://wiki.yics.org/Room_IDs .\n");

      getparams();

      sysiprint("\nAll OK.  Attempting to connect to server...\n");

      if (!yconnect(s_hostname, s_port))
            dief("Can't connect!  (%s)", strerror(errno));

      sysiprint("Connected.\n");
      dologin();

      sysiprint("\nLogged in as ");
      sysiprint(handle);
      sysiprint(/* ".  The following line is for interfaces that use it to "
            "determine your username.*/ "\n\n"

/*          "**** Starting FICS session as ");
      sysiprint(handle);
      sysiprint(" ****\n"
            "------------------------------------------------------------"
            "------------------\n\n"*/

            "Downloading room state....\n");

      while (!login_complete)
            handle_opcode();

      if (pme == NULL)
            die("The server did not send a login message about you; "
                  "cannot proceed.");

      info = StringNew("RESOLUTION java.awt.Dimension[width=800,height=600]", -1);
      packutfString(info, info);
      nprintropString(ROP_INFO, info);
      StringFree(info);
      info = NULL;

      prompting = true;
      sysiprint("\nDone.  ICS command emulation active.\n");
      prompt();

      lastcommand = time(NULL);

      for (;;) {
#if defined(_YICS_WIN32)
            idle = 1;
#endif

            while (stdin_ready()) {
#if defined(_YICS_WIN32)
                  idle = 0;
#endif

                  if (!stdin_getchar(&inc)) {
                        die("");    /* stdin was closed */
                        return 0;
                  }

                  if (inc == '\n') {
                        inbuf[inbufp] = '\0';

                        do_command(inbuf, false);

                        inbuf[0] = '\0';
                        inbufp = 0;
                  } else if ((inc != '\r') && (inbufp < 2047)) {
                        inbuf[inbufp++] = (char)inc;
                  }
            }

            while (socket_ready()) {
#if defined(_YICS_WIN32)
                  idle = 0;
#endif
                  handle_opcode();
            }

#if defined(_YICS_WIN32)
            if (idle) {
                  Sleep(curdelay);
                  curdelay += curdelay;
                  if (curdelay > 20)
                        curdelay = 20;
            } else {
                  curdelay = 1;
            }
#endif

            if (variables[VAR_NOTIMEOUT].number &&
            ((lastcommand + 1800) <= time(NULL))) {
                  iprint("Sending bogus command to server to prevent "
                        "timeout disconnection.\n");
                  prompt();
                  info = StringNew("ENDAD ABORT", -1);
                  packutfString(info, info);
                  nprintropString(ROP_INFO, info);
                  StringFree(info);
                  info = NULL;
            }
      }
}

static void dologin() {
      static char tmp[1024] = "";
      int length, i;
      unsigned long key_in, key_out;
      FILE *tlog = netlog;

      sysiprint("\nReceiving handshake...\n");

      if ((nread(tmp, 6) != 6) || strcmp(tmp, "YAHOO!"))
            die("Incorrect handshake.");
      nputc('Y');

      sysiprint("Creating encryption and decryption keys...\n");

      if (nread(tmp, 4) != 4)
            die("Keys could not be created.");
      memcpy(&key_out, tmp, 4);

      if (nread(tmp, 4) != 4)
            die("Keys could not be created.");
      memcpy(&key_in, tmp, 4);

      set_keys(ntohl(key_out), ntohl(key_in));

      sysiprint("Entering game room...\n");

      nputc('o');
      nprintutf(s_room, (unsigned short)strlen(s_room));
      if (ngetc() != 0x6f)
            die("Couldn't enter game room.");

      nreadutf(tmp);
      if (strcmp(tmp, s_room))
            die("Incorrect room handshake.");

      nread((char *)&session, 4);
      /* no ntohl because we only need to send it back to the server;
         converting it each time would be useless and wasteful. */

      if (ngetc() != 0x64)
            die("Incorrect room handshake.");

      nread(NULL, 4);
      nreadutf(tmp);
      if (strcmp(unpackutf(tmp, tmp), "GAMES"))
            die("Incorrect room handshake.");

      sysiprint("Sending login information...\n");

      nputc('d');
      nprint((char *)&session, 4);

      tmp[0] = '\0';
      length = 1;

      i = strlen(s_cookie);
      packutf(&tmp[1], s_cookie, (unsigned short)i);
      length += i + 2;

      i = strlen(s_ycookie);
      packutf(&tmp[length], s_ycookie, (unsigned short)i);
      length += i + 2;

      i = strlen(s_uagent);
      packutf(&tmp[length], s_uagent, (unsigned short)i);
      length += i + 2;

      i = strlen(s_region);
      packutf(&tmp[length], s_region, (unsigned short)i);
      length += i + 2;

      netlog = NULL;    /* Protect user's cookie from the log */
      nprintutf(tmp, (unsigned short)length);
      if (tlog != NULL) {
            netlog = tlog;    /* Restore log handle */
            fputc(2, tlog);   /* Write placeholder to the log */
      }

      if (ngetc() != 0x64)
            die("Protocol error.");

      nread(NULL, 4);
      nreadutf(tmp);
      if (tmp[0])
            dief( "\nServer says:\n\n%s\n\n"
            
                  "Usually this means that your applet.html file is "
                  "out of date.  Try fetching a new copy.\n", &tmp[1]);

      unpackutf(&tmp[1], &tmp[1]);
      mstrncpy(handle, &tmp[1], sizeof(handle));
}

static bool pline(char *buffer, int length, const char *msg) {
      *buffer = '\0';

      sysiprint(msg);
      mfgets(buffer, length, stdin);
      trim(buffer);

      return (*buffer == '\0') ? false : true;
}

static int http_login(char *username, char *password, char *room) {
      char *cookies[32] = {NULL};
      ValuePair login[3];
      ValuePair applet[6];
      int result, sd = 0;

      login[0].key   = "login";
      login[0].value = username;
      login[1].key   = "passwd";
      login[1].value = password;
      login[2].key   = NULL;
      login[2].value = NULL;

      applet[0].key   = "room";
      applet[0].value = room;
      applet[1].key   = "prof_id";
      applet[1].value = "chat_pf_1";
      applet[2].key   = "small";
      applet[2].value = "no";
      applet[3].key   = "follow";
      applet[3].value = "";
      applet[4].key   = "nosignedcab";
      applet[4].value = "no";
      applet[5].key   = NULL;
      applet[5].value = NULL;

      sysiprint("\nLogging in...\n");
      result = http_get("login.yahoo.com", "/config/login", login, cookies, &sd);

      if (result < 0) {
            sysiprint("Unable to contact login.yahoo.com.\n");
            return -1;
      }

      if (result != HTTP_SERVER_RETURN_VALUE_FOUND) {
            sysiprint("Login or password is invalid.\n");
            return -1;
      }

      sd = 1;
      sysiprint("Requesting applet.html...\n");
      http_get("games.yahoo.com", "/games/applet.html", applet, cookies, &sd);

      return sd;
}

static void getparams() {
      static char loc[2048], key[1024], value[1024];
      static char nameCopy[2048];
      char *line, *sp;
      Option *params = NULL, *opt = NULL;
      SocketFile applet;

      /* TODO: Rework this so it doesn't need goto. */
RETRY_LOGIN:
      pline(loc, 2048, "\n"
            "Enter your username, or \"/\" to read an applet.html file.\n"
            "login: ");

      if ((loc[0] != '/') || (loc[1] != '\0')) {
            /* Copy the name for profile logins. */
            mstrncpy(nameCopy, loc, sizeof(nameCopy));

            applet.isSocket = true;

            if (!pline(key, 1024, "\n"
                        "Enter your password.\n"
                        "password: "))
                  goto RETRY_LOGIN;

            if (!pline(value, 1024, "\n"
                        "Enter the room ID to join.\n"
                        "room: "))
                  goto RETRY_LOGIN;

            applet.fd.socket = http_login(loc, key, value);
            if (applet.fd.socket < 0)
                  goto RETRY_LOGIN;

            strcpy(loc, "applet.html from the server");
      } else {
            /* We don't have a profile name, since it's read from disk. */
            nameCopy[0] = '\0';
            applet.isSocket = false;

            do {
                  if (!pline(loc, 2048, "\n"
                              "Enter the location of applet.html below.\n"
                              "login: "))
                        goto RETRY_LOGIN;

                  if ((applet.fd.file = fopen(loc, "r")) == NULL) {
                        sysiprint("Unable to open ");
                        sysiprint(loc);
                        sysiprint("\n");
                  }
            } while (applet.fd.file == NULL);
      }

      sysiprint("\nGoing to read ");
      sysiprint(loc);
      sysiprint("...\n");

LINE: while (s_fgets(loc, 2048, applet) != NULL) {
            line = loc;
            while ((line = strstr (line, "<param name=\"")) != NULL) {
                  line += 13;

                  sp = key;
                  while (*line && (*line != '"'))
                        *(sp++) = *(line++);
                  *sp = '\0';

                  line = strstr(line, "value=\"");
                  if (line == NULL)
                        goto LINE;
                  line += 7;

                  sp = value;
                  while (*line && (*line != '"'))
                        *(sp++) = *(line++);
                  *sp = '\0';

                  if (params == NULL) {
                        params = createOption(key,
                              value);
                  } else {
                        setOption(params, key, value);
                  }
            }
      }

      if (applet.isSocket) {
            closesock(applet.fd.socket);
      } else {
            fclose(applet.fd.file);
      }

      if ((opt = findOption(params, "host")) == NULL) {
            if ((opt = findOption(params, "codebase")) == NULL) {
                  destroyOptions(params);
                  sysiprint("Neither \"host\" nor \"codebase\" "
                        "applet parameters found.\n");
                  goto RETRY_LOGIN;
            }

            line = opt->value;
            line = strstr(line, "http://");
            if (line == NULL) {
                  destroyOptions(params);
                  sysiprint("\"codebase\" applet parameter format is "
                        "invalid.");
                  goto RETRY_LOGIN;
            }

            line += 7;
            sp = s_hostname;

            while (*line && (*line != ':') && (*line != '/'))
                  *(sp++) = *(line++);

            *sp = '\0';

            sysiprint("WARNING: Server extracted from codebase."
                  "  (");
            sysiprint(s_hostname);
            sysiprint(")\n");
      } else {
            mstrncpy(s_hostname, opt->value, sizeof(s_hostname));
      }

      if ((opt = findOption(params, "port")) == NULL) {
            destroyOptions(params);
            sysiprint("\"port\" applet parameter not found.\n");
            goto RETRY_LOGIN;
      }
      s_port = (unsigned short)atoi(opt->value);

      if ((opt = findOption(params, "yport")) == NULL) {
            destroyOptions(params);
            sysiprint("\"yport\" applet parameter not found.\n");
            goto RETRY_LOGIN;
      }
      mstrncpy(s_room, opt->value, sizeof(s_room));

      if (nameCopy[0] != '\0') {
            /*
             * For profile logins.  This has the potential of breaking the
             * login if more than the ID is being stored in the cookie!
             *
             * XXX: The getapplet part should be able to handle this.
             * Do more sniffing and patch it.
             */
            snprintf(s_cookie, sizeof(s_cookie), "id=%s", nameCopy);
      } else {
            if ((opt = findOption(params, "cookie")) == NULL) {
                  destroyOptions(params);
                  sysiprint("\"cookie\" applet parameter not found.\n");
                  goto RETRY_LOGIN;
            }
            mstrncpy(s_cookie, opt->value, sizeof(s_cookie));
      }

      if ((opt = findOption(params, "ycookie")) == NULL) {
            destroyOptions(params);
            sysiprint("\"ycookie\" applet parameter not found.\n");
            goto RETRY_LOGIN;
      }
      mstrncpy(s_ycookie, opt->value, sizeof(s_ycookie));

      if ((opt = findOption(params, "intl_code")) != NULL)
            mstrncpy(s_region, opt->value, sizeof(s_region));

      if ((opt = findOption(params, "label")) != NULL) {
            sysiprint("\nRoom: ");
            sysiprint(opt->value);
            sysiprint("\n");
      }

#if i386
# define AGENT_ARCH "i386; "
#else
# define AGENT_ARCH ""
#endif

      snprintf(s_uagent, sizeof(s_uagent),
                  "Mozilla/5.0 (compatible; " AGENT_ARCH "YICS/%s)",
                  VERSION);

#undef AGENT_ARCH

      destroyOptions(params);
}

Generated by  Doxygen 1.6.0   Back to index