//---------------------------------------------------------------------------
#include "stdafx.h"
#include "FileZillaApi.h"
#include "mainthread.h"

//////////////////////////////////////////////////////////////////////
// Konstruktion/Destruktion
//////////////////////////////////////////////////////////////////////

CFileZillaApi::CFileZillaApi()
{
  m_nInternalMessageID=0;
  m_pMainThread=0;
  m_bInitialized=FALSE;
}

CFileZillaApi::~CFileZillaApi()
{
  Destroy();
}

int CFileZillaApi::Init(TFileZillaIntern * Intern, CFileZillaTools * pTools)
{
  //Check if call allowed
  if (m_bInitialized)
    return FZ_REPLY_ALREADYINIZIALIZED;

  //Initialize variables
  m_nInternalMessageID=RegisterWindowMessage( L"FileZillaInternalApiMessage{F958620E-040C-4b33-A091-7E04E10AA660}" );
  if (!m_nInternalMessageID)
    return FZ_REPLY_NOTINITIALIZED;

  //Create thread object
  m_pMainThread = CMainThread::Create(THREAD_PRIORITY_BELOW_NORMAL, CREATE_SUSPENDED);

  //Initialize Thread variables
  m_pMainThread->m_nInternalMessageID=m_nInternalMessageID;
  m_pMainThread->m_pTools=pTools;

  m_pMainThread->InitIntern(Intern);

  //Resume Thread
  m_pMainThread->ResumeThread();

  //Initialization OK
  m_bInitialized=TRUE;
  return FZ_REPLY_OK;
}

int CFileZillaApi::IsConnected()
{
  if (!m_bInitialized)
    return FZ_REPLY_NOTINITIALIZED;
  return m_pMainThread->IsConnected()?FZ_REPLY_OK:FZ_REPLY_NOTCONNECTED;
}

int CFileZillaApi::IsBusy()
{
  if (!m_bInitialized)
    return FZ_REPLY_NOTINITIALIZED;
  return m_pMainThread->IsBusy()?FZ_REPLY_BUSY:FZ_REPLY_IDLE;
}

int CFileZillaApi::Connect(const t_server &server)
{
  //Check parameters
  if (server.host==L"" || server.port<1 || server.port>65535)
    return FZ_REPLY_INVALIDPARAM;

#ifndef MPEXT_NO_GSS
  BOOL bUseGSS = FALSE;
  if (GetOptionVal(OPTION_USEGSS))
  {
    USES_CONVERSION;

    CString GssServers = GetOption(OPTION_GSSSERVERS);
    hostent *fullname = gethostbyname(T2CA(server.host));
    CString host;
    if (fullname)
      host = fullname->h_name;
    else
      host = server.host;
    host.MakeLower();
    int i;
    while ((i=GssServers.Find( L";" ))!=-1)
    {
      if ((L"."+GssServers.Left(i))==host.Right(GssServers.Left(i).GetLength()+1) || GssServers.Left(i)==host)
      {
        bUseGSS = TRUE;
        break;
      }
      GssServers = GssServers.Mid(i+1);
    }
  }
  if (!bUseGSS && server.user == L"")
    return FZ_REPLY_INVALIDPARAM;
#endif

  if (!(server.nServerType&FZ_SERVERTYPE_HIGHMASK))
    return FZ_REPLY_INVALIDPARAM;

  //Check if call allowed
  if (!m_bInitialized)
    return FZ_REPLY_NOTINITIALIZED;
  if (m_pMainThread->IsBusy())
    return FZ_REPLY_BUSY;

  t_command command;
  command.id=FZ_COMMAND_CONNECT;
  command.server=server;
  m_pMainThread->Command(command);
  return m_pMainThread->LastOperationSuccessful()?FZ_REPLY_OK:FZ_REPLY_ERROR;
}

int CFileZillaApi::Cancel()
{
  //Check if call allowed
  if (!m_bInitialized)
    return FZ_REPLY_NOTINITIALIZED;
  if (IsBusy()!=FZ_REPLY_BUSY)
    return FZ_REPLY_NOTBUSY;
  m_pMainThread->PostThreadMessage(m_nInternalMessageID, FZAPI_THREADMSG_CANCEL, 0);
  return FZ_REPLY_WOULDBLOCK;
}

void CFileZillaApi::Destroy()
{
  if (!m_bInitialized)
    return;
  DebugAssert(m_pMainThread);
  HANDLE tmp=m_pMainThread->m_hThread;
  m_pMainThread->Quit();
  //Wait for the main thread to quit
  WaitForSingleObject(tmp, 10000);

  m_pMainThread=0;
  m_bInitialized=FALSE;
}

int CFileZillaApi::Disconnect()
{
  //Check if call allowed
  if (!m_bInitialized)
    return FZ_REPLY_NOTINITIALIZED;
  if (IsConnected()==FZ_REPLY_NOTCONNECTED)
    return FZ_REPLY_NOTCONNECTED;
  if (IsBusy()==FZ_REPLY_BUSY)
    return FZ_REPLY_BUSY;

  m_pMainThread->PostThreadMessage(m_nInternalMessageID,FZAPI_THREADMSG_DISCONNECT,0);
  return FZ_REPLY_WOULDBLOCK;
}

int CFileZillaApi::List(const CServerPath& path)
{
  //Check if call allowed
  if (!m_bInitialized)
    return FZ_REPLY_NOTINITIALIZED;
  if (IsConnected()==FZ_REPLY_NOTCONNECTED)
    return FZ_REPLY_NOTCONNECTED;
  if (path.IsEmpty() && !m_pMainThread->GetIntern()->GetOptionVal(OPTION_MPEXT_WORK_FROM_CWD))
    return FZ_REPLY_INVALIDPARAM;

  if (m_pMainThread->IsBusy())
    return FZ_REPLY_BUSY;

  t_command command;
  command.id=FZ_COMMAND_LIST;
  command.path=path;
  m_pMainThread->Command(command);
  return m_pMainThread->LastOperationSuccessful()?FZ_REPLY_OK:FZ_REPLY_ERROR;
}

int CFileZillaApi::ListFile(CString FileName, const CServerPath & path)
{
  //Check if call allowed
  if (!m_bInitialized)
    return FZ_REPLY_NOTINITIALIZED;
  if (IsConnected()==FZ_REPLY_NOTCONNECTED)
    return FZ_REPLY_NOTCONNECTED;
  if (path.IsEmpty())
    return FZ_REPLY_INVALIDPARAM;
  if (FileName=="")
    return FZ_REPLY_INVALIDPARAM;

  if (m_pMainThread->IsBusy())
    return FZ_REPLY_BUSY;
  t_command command;
  command.id=FZ_COMMAND_LISTFILE;
  command.param1=FileName;
  command.path=path;
  m_pMainThread->Command(command);
  return m_pMainThread->LastOperationSuccessful()?FZ_REPLY_OK:FZ_REPLY_ERROR;
}

int CFileZillaApi::FileTransfer(const t_transferfile &TransferFile)
{
  //Check if call allowed
  if (!m_bInitialized)
    return FZ_REPLY_NOTINITIALIZED;
  if (IsConnected()==FZ_REPLY_NOTCONNECTED)
    return FZ_REPLY_NOTCONNECTED;
  if (TransferFile.remotefile==L"" || TransferFile.localfile==L"" || (TransferFile.remotepath.IsEmpty() && !m_pMainThread->GetIntern()->GetOptionVal(OPTION_MPEXT_WORK_FROM_CWD)))
    return FZ_REPLY_INVALIDPARAM;
  if (IsBusy()==FZ_REPLY_BUSY)
    return FZ_REPLY_BUSY;
  t_command command;
  command.id=FZ_COMMAND_FILETRANSFER;
  command.transferfile=TransferFile;
  m_pMainThread->Command(command);
  return m_pMainThread->LastOperationSuccessful()?FZ_REPLY_OK:FZ_REPLY_ERROR;
}

int CFileZillaApi::GetCurrentServer(t_server &server)
{
  //Check if call allowed
  if (!m_bInitialized)
    return FZ_REPLY_NOTINITIALIZED;
  if (IsConnected()==FZ_REPLY_NOTCONNECTED)
    return FZ_REPLY_NOTCONNECTED;
  if (m_pMainThread->GetCurrentServer(server))
    return FZ_REPLY_OK;
  else
    return FZ_REPLY_NOTCONNECTED;
}

int CFileZillaApi::SetCurrentPath(CServerPath path)
{
  //Check if call allowed
  if (!m_bInitialized)
    return FZ_REPLY_NOTINITIALIZED;
  if (IsConnected()==FZ_REPLY_NOTCONNECTED)
    return FZ_REPLY_NOTCONNECTED;
  m_pMainThread->SetCurrentPath(path);
  return FZ_REPLY_OK;
}

int CFileZillaApi::GetCurrentPath(CServerPath & path)
{
  //Check if call allowed
  if (!m_bInitialized)
    return FZ_REPLY_NOTINITIALIZED;
  if (IsConnected()==FZ_REPLY_NOTCONNECTED)
    return FZ_REPLY_NOTCONNECTED;
  return (m_pMainThread->GetCurrentPath(path) ? FZ_REPLY_OK : FZ_REPLY_NOTCONNECTED);
}

bool CFileZillaApi::UsingMlsd()
{
  //Check if call allowed
  if (!m_bInitialized)
    return false;
  if (IsConnected()==FZ_REPLY_NOTCONNECTED)
    return false;
  return m_pMainThread->UsingMlsd();
}

bool CFileZillaApi::UsingUtf8()
{
  //Check if call allowed
  if (!m_bInitialized)
    return false;
  if (IsConnected()==FZ_REPLY_NOTCONNECTED)
    return false;
  return m_pMainThread->UsingUtf8();
}

std::string CFileZillaApi::GetTlsVersionStr()
{
  //Check if call allowed
  if (!m_bInitialized)
    return std::string();
  if (IsConnected()==FZ_REPLY_NOTCONNECTED)
    return std::string();
  return m_pMainThread->GetTlsVersionStr();
}

std::string CFileZillaApi::GetCipherName()
{
  //Check if call allowed
  if (!m_bInitialized)
    return std::string();
  if (IsConnected()==FZ_REPLY_NOTCONNECTED)
    return std::string();
  return m_pMainThread->GetCipherName();
}

int CFileZillaApi::CustomCommand(CString CustomCommand)
{
  //Check if call allowed
  if (!m_bInitialized)
    return FZ_REPLY_NOTINITIALIZED;
  if (IsConnected()==FZ_REPLY_NOTCONNECTED)
    return FZ_REPLY_NOTCONNECTED;
  if (IsBusy()==FZ_REPLY_BUSY)
    return FZ_REPLY_BUSY;
  t_server server;
  int res=GetCurrentServer(server);
  if (res!=FZ_REPLY_OK)
    return res;
  if (CustomCommand==L"")
    return FZ_REPLY_INVALIDPARAM;

  t_command command;
  command.id=FZ_COMMAND_CUSTOMCOMMAND;
  command.param1=CustomCommand;
  m_pMainThread->Command(command);
  return m_pMainThread->LastOperationSuccessful()?FZ_REPLY_OK:FZ_REPLY_ERROR;
}

int CFileZillaApi::Delete(CString FileName, const CServerPath &path, bool filenameOnly)
{
  //Check if call allowed
  if (!m_bInitialized)
    return FZ_REPLY_NOTINITIALIZED;
  if (IsConnected()==FZ_REPLY_NOTCONNECTED)
    return FZ_REPLY_NOTCONNECTED;
  if (IsBusy()==FZ_REPLY_BUSY)
    return FZ_REPLY_BUSY;
  if (FileName=="")
    return FZ_REPLY_INVALIDPARAM;

  CServerPath path2=path;
  if (path2.IsEmpty())
  {
    m_pMainThread->GetCurrentPath(path2);
    if (path2.IsEmpty())
      return FZ_REPLY_INVALIDPARAM;
  }

  t_command command;
  command.id=FZ_COMMAND_DELETE;
  command.param1=FileName;
  command.path=path2;
  command.param4 = (filenameOnly ? 1 : 0);
  m_pMainThread->Command(command);
  return m_pMainThread->LastOperationSuccessful()?FZ_REPLY_OK:FZ_REPLY_ERROR;
}

int CFileZillaApi::RemoveDir(CString DirName, const CServerPath &path /*=CServerPath()*/)
{
  //Check if call allowed
  if (!m_bInitialized)
    return FZ_REPLY_NOTINITIALIZED;
  if (IsConnected()==FZ_REPLY_NOTCONNECTED)
    return FZ_REPLY_NOTCONNECTED;
  if (IsBusy()==FZ_REPLY_BUSY)
    return FZ_REPLY_BUSY;
  if (DirName==L"")
    return FZ_REPLY_INVALIDPARAM;

  CServerPath path2=path;
  if (path2.IsEmpty())
  {
    m_pMainThread->GetCurrentPath(path2);
    if (path2.IsEmpty())
      return FZ_REPLY_INVALIDPARAM;
  }

  t_command command;
  command.id=FZ_COMMAND_REMOVEDIR;
  command.param1=DirName;
  command.path=path2;
  m_pMainThread->Command(command);
  return m_pMainThread->LastOperationSuccessful()?FZ_REPLY_OK:FZ_REPLY_ERROR;
}

int CFileZillaApi::MakeDir(const CServerPath &path)
{
  //Check if call allowed
  if (!m_bInitialized)
    return FZ_REPLY_NOTINITIALIZED;
  if (IsConnected()==FZ_REPLY_NOTCONNECTED)
    return FZ_REPLY_NOTCONNECTED;
  if (IsBusy()==FZ_REPLY_BUSY)
    return FZ_REPLY_BUSY;
  if (path.IsEmpty() || !path.HasParent())
    return FZ_REPLY_INVALIDPARAM;

  t_command command;
  command.id=FZ_COMMAND_MAKEDIR;
  command.path=path;
  m_pMainThread->Command(command);
  return m_pMainThread->LastOperationSuccessful()?FZ_REPLY_OK:FZ_REPLY_ERROR;
}

int CFileZillaApi::Rename(CString oldName, CString newName, const CServerPath &path /*=CServerPath()*/, const CServerPath &newPath /*=CServerPath()*/)
{
  //Check if call allowed
  if (!m_bInitialized)
    return FZ_REPLY_NOTINITIALIZED;
  if (IsConnected()==FZ_REPLY_NOTCONNECTED)
    return FZ_REPLY_NOTCONNECTED;
  if (IsBusy()==FZ_REPLY_BUSY)
    return FZ_REPLY_BUSY;
  if (oldName==L"" || newName==L"")
    return FZ_REPLY_INVALIDPARAM;

  CServerPath path2 = path;
  if (path2.IsEmpty())
  {
    m_pMainThread->GetCurrentPath(path2);
    if (path2.IsEmpty())
      return FZ_REPLY_INVALIDPARAM;
  }

  t_command command;
  command.id = FZ_COMMAND_RENAME;
  command.param1 = oldName;
  command.param2 = newName;
  command.path = path2;
  command.newPath = newPath;
  m_pMainThread->Command(command);
  return m_pMainThread->LastOperationSuccessful()?FZ_REPLY_OK:FZ_REPLY_ERROR;
}

int CFileZillaApi::SetAsyncRequestResult(int nAction, CAsyncRequestData *pData)
{
  if (!this || !pData)
    return FZ_REPLY_CRITICALERROR | FZ_REPLY_INVALIDPARAM;

  if (IsBadWritePtr(pData, sizeof(CAsyncRequestData)))
    return FZ_REPLY_CRITICALERROR;

  if (!m_bInitialized)
  {
    delete pData;
    return FZ_REPLY_NOTINITIALIZED;
  }
  if (IsConnected()==FZ_REPLY_NOTCONNECTED)
  {
    delete pData;
    return FZ_REPLY_NOTCONNECTED;
  }

  switch(pData->nRequestType)
  {
  case FZ_ASYNCREQUEST_OVERWRITE:
    break;
  case FZ_ASYNCREQUEST_VERIFYCERT:
    if (!((CVerifyCertRequestData *)pData)->pCertData)
    {
      delete pData;
      return FZ_REPLY_INVALIDPARAM;
    }
    break;
  case FZ_ASYNCREQUEST_NEEDPASS:
    break;
#ifndef MPEXT_NO_GSS
  case FZ_ASYNCREQUEST_GSS_AUTHFAILED:
  case FZ_ASYNCREQUEST_GSS_NEEDUSER:
  case FZ_ASYNCREQUEST_GSS_NEEDPASS:
    break;
#endif
  default:
    delete pData;
    return FZ_REPLY_INVALIDPARAM;
  }
  pData->nRequestResult = nAction;
  if (!m_pMainThread)
  {
    delete pData;
    return FZ_REPLY_NOTINITIALIZED;
  }

  m_pMainThread->PostThreadMessage(m_nInternalMessageID, FZAPI_THREADMSG_ASYNCREQUESTREPLY,  (LPARAM)pData);

  return FZ_REPLY_OK;
}

int CFileZillaApi::Chmod(int nValue, CString FileName, const CServerPath &path /*=CServerPath()*/ )
{
  //Check if call allowed
  if (!m_bInitialized)
    return FZ_REPLY_NOTINITIALIZED;
  if (IsConnected()==FZ_REPLY_NOTCONNECTED)
    return FZ_REPLY_NOTCONNECTED;
  if (IsBusy()==FZ_REPLY_BUSY)
    return FZ_REPLY_BUSY;
  if (FileName==L"")
    return FZ_REPLY_INVALIDPARAM;

  t_command command;
  command.id=FZ_COMMAND_CHMOD;
  command.param1=FileName;
  command.param4=nValue;
  command.path=path;
  m_pMainThread->Command(command);
  return m_pMainThread->LastOperationSuccessful()?FZ_REPLY_OK:FZ_REPLY_ERROR;
}

void CFileZillaApi::SetDebugLevel(int nDebugLevel)
{
  m_pMainThread->GetIntern()->SetDebugLevel(nDebugLevel);
}

//CAsyncRequestData derived classes
CAsyncRequestData::CAsyncRequestData()
{
  nRequestType = 0;
  nRequestID = 0;
  nRequestResult = 0;
}

CAsyncRequestData::~CAsyncRequestData()
{
}

COverwriteRequestData::COverwriteRequestData()
{
  size1 = 0;
  size2 = 0;
  nRequestType=FZ_ASYNCREQUEST_OVERWRITE;
  localtime=0;
  remotetime.hasdate = false;
  pTransferFile=0;
}

COverwriteRequestData::~COverwriteRequestData()
{
  delete pTransferFile;
  delete localtime;
}

CVerifyCertRequestData::CVerifyCertRequestData()
{
  nRequestType=FZ_ASYNCREQUEST_VERIFYCERT;
  pCertData=0;
}

CVerifyCertRequestData::~CVerifyCertRequestData()
{
  delete pCertData;
}

CNeedPassRequestData::CNeedPassRequestData()
{
  nRequestType=FZ_ASYNCREQUEST_NEEDPASS;
  nOldOpState=0;
}

CNeedPassRequestData::~CNeedPassRequestData()
{
}

#ifndef MPEXT_NO_GSS
CGssNeedPassRequestData::CGssNeedPassRequestData()
{
  nRequestType=FZ_ASYNCREQUEST_GSS_NEEDPASS;
}

CGssNeedPassRequestData::~CGssNeedPassRequestData()
{
}

CGssNeedUserRequestData::CGssNeedUserRequestData()
{
  nRequestType = FZ_ASYNCREQUEST_GSS_NEEDUSER;
}

CGssNeedUserRequestData::~CGssNeedUserRequestData()
{
}
#endif
