/* Copyright (C) 2004 TightVNC Team.  All Rights Reserved.
 *    
 * This 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 software 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 software; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
 * USA.
 */

// -=- RFB Protocol

#include <rfb/Exception.h>
#include <rfb/LogWriter.h>

#include <rfbplayer/RfbProto.h>

using namespace rfb;

static LogWriter vlog("RfbProto");

//
// Constructor
//

RfbProto::RfbProto(char *fileName) {
  is = NULL;
  reader = NULL;
  newSession(fileName);
}

//
// Destructor
//

RfbProto::~RfbProto() {
  if (is) delete is;
  is = 0;
  if (reader) delete reader;
  reader = 0;
}

void RfbProto::newSession(char *fileName) {
  // Close the previous session
  if (is) {
    delete is;
    is = NULL;
    delete reader;
    reader = NULL;
  }

  // Begin the new session
  if (fileName != NULL) {
    is = new FbsInputStream(fileName);
    reader = new CMsgReaderV3(this, is);
    initialiseProtocol();
  }
}

void RfbProto::initialiseProtocol() {
  state_ = RFBSTATE_PROTOCOL_VERSION;
}

void RfbProto::processMsg()
{
  switch (state_) {

  case RFBSTATE_PROTOCOL_VERSION: processVersionMsg();       break;
  case RFBSTATE_SECURITY:         processSecurityMsg();      break;
  case RFBSTATE_INITIALISATION:   processInitMsg();          break;
  case RFBSTATE_NORMAL:           reader->readMsg();         break;
  default:
    throw rfb::Exception("RfbProto::processMsg: invalid state");
  }
}

void RfbProto::processVersionMsg()
{
  vlog.debug("reading protocol version");
  memset(&cp, 0, sizeof(cp));
  bool done;
  if (!cp.readVersion(is, &done)) {
    state_ = RFBSTATE_INVALID;
    throw rfb::Exception("reading version failed: wrong file format?");
  }
  if (!done) return;

  // The only official RFB protocol versions are currently 3.3, 3.7 and 3.8
  if (!cp.isVersion(3,3) && !cp.isVersion(3,7) && !cp.isVersion(3,8)) {
    char msg[256];
    sprintf(msg,"File have unsupported RFB protocol version %d.%d",
            cp.majorVersion, cp.minorVersion);
    state_ = RFBSTATE_INVALID;
    throw rfb::Exception(msg);
  }

  state_ = RFBSTATE_SECURITY;

  vlog.info("Using RFB protocol version %d.%d",
            cp.majorVersion, cp.minorVersion);
}

void RfbProto::processSecurityMsg()
{
  vlog.debug("processing security types message");

  int secType = secTypeInvalid;

  // legacy 3.3 server may only offer "vnc authentication" or "none"
  secType = is->readU32();
  if (secType == secTypeInvalid) {
    int reasonLen = is->readU32();
    char *reason = new char[reasonLen];
    is->readBytes(reason, reasonLen);
    throw rfb::Exception(reason); 
  }

  if (secType != secTypeNone) {
    throw rfb::Exception("Wrong authentication type in the session file");
  }

  state_ = RFBSTATE_INITIALISATION;
}

void RfbProto::processInitMsg() {
  vlog.debug("reading server initialisation");
  reader->readServerInit(); 
}

void RfbProto::serverInit()
{
  state_ = RFBSTATE_NORMAL;
  vlog.debug("initialisation done");
}