"Fossies" - the Fresh Open Source Software archive

Member "tvnserver-2.0.4/tvnserver-app/TvnServer.cpp" of archive tvnserver-2.0.4-src.zip:


// Copyright (C) 2008, 2009, 2010 GlavSoft LLC.
// All rights reserved.
//
//-------------------------------------------------------------------------
// This file is part of the TightVNC software.  Please visit our Web site:
//
//                       http://www.tightvnc.com/
//
// 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.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
//-------------------------------------------------------------------------
//

#include "TvnServer.h"
#include "WsConfigRunner.h"
#include "AdditionalActionApplication.h"
#include "win-system/CurrentConsoleProcess.h"

#include "server-config-lib/Configurator.h"

#include "thread/GlobalMutex.h"

#include "tvnserver/resource.h"

#include "wsconfig-lib/TvnLogFilename.h"

#include "network/socket/WindowsSocket.h"

#include "util/FileLog.h"
#include "util/StringTable.h"

#include "file-lib/File.h"

#include "tvncontrol-app/TransportFactory.h"
#include "tvncontrol-app/ControlPipeName.h"

#include "tvnserver/BuildTime.h"

#include <crtdbg.h>
#include <time.h>

TvnServer::TvnServer(bool runsInServiceContext)
: Singleton<TvnServer>(),
  ListenerContainer<TvnServerListener *>(),
  m_runAsService(runsInServiceContext),
  m_rfbClientManager(0),
  m_httpServer(0), m_controlServer(0), m_rfbServer(0)
{
  Configurator *configurator = Configurator::getInstance();

  configurator->setServiceFlag(m_runAsService);

  configurator->load();

  m_config = Configurator::getInstance()->getServerConfig();

  resetLogFilePath();

  m_log.changeLevel(m_config->getLogLevel());

  Log::message(_T("TightVNC Server Build on %s"), BuildTime::DATE);

  Log::info(_T("Initialize WinSock"));

  try {
    WindowsSocket::startup(2, 1);
  } catch (Exception &ex) {
    Log::interror(_T("%s"), ex.getMessage());
  }

  ZombieKiller *zombieKiller = new ZombieKiller();

  m_rfbClientManager = new RfbClientManager(NULL);

  m_rfbClientManager->addListener(this);

  Configurator::getInstance()->addListener(this);

  {
    AutoLock l(&m_mutex);

    restartMainRfbServer();
    (void)m_extraRfbServers.reload(m_runAsService, m_rfbClientManager);
    restartHttpServer();
    restartControlServer();
  }
}

TvnServer::~TvnServer()
{
  Configurator::getInstance()->removeListener(this);

  stopControlServer();
  stopHttpServer();
  m_extraRfbServers.shutDown();
  stopMainRfbServer();

  ZombieKiller *zombieKiller = ZombieKiller::getInstance();

  zombieKiller->killAllZombies();

  m_rfbClientManager->removeListener(this);

  delete m_rfbClientManager;

  delete zombieKiller;

  Log::info(_T("Shutdown WinSock"));

  try {
    WindowsSocket::cleanup();
  } catch (Exception &ex) {
    Log::error(_T("%s"), ex.getMessage());
  }

  delete Configurator::getInstance();
}

void TvnServer::onConfigReload(ServerConfig *serverConfig)
{
  resetLogFilePath();

  m_log.changeLevel(serverConfig->getLogLevel());

  {
    AutoLock l(&m_mutex);

    bool toggleMainRfbServer =
      m_config->isAcceptingRfbConnections() != (m_rfbServer != 0);
    bool changeMainRfbPort = m_rfbServer != 0 &&
      (m_config->getRfbPort() != (int)m_rfbServer->getBindPort());

    const TCHAR *bindHost =
      m_config->isOnlyLoopbackConnectionsAllowed() ? _T("localhost") : _T("0.0.0.0");
    bool changeBindHost =  m_rfbServer != 0 &&
      _tcscmp(m_rfbServer->getBindHost(), bindHost) != 0;

    if (toggleMainRfbServer ||
        changeMainRfbPort ||
        changeBindHost) {
      restartMainRfbServer();
    }

    (void)m_extraRfbServers.reload(m_runAsService, m_rfbClientManager);
  }

  {
    AutoLock l(&m_mutex);

    bool toggleHttp =
      m_config->isAcceptingHttpConnections() != (m_httpServer != 0);
    bool changePort = m_httpServer != 0 &&
      (m_config->getHttpPort() != (int)m_httpServer->getBindPort());

    if (toggleHttp || changePort) {
      restartHttpServer();
    }
  }
}

void TvnServer::getServerInfo(TvnServerInfo *info)
{
  bool rfbServerListening = true;
  {
    AutoLock l(&m_mutex);
    rfbServerListening = m_rfbServer != 0;
  }

  StringStorage statusString;

  bool vncAuthEnabled = m_config->isUsingAuthentication();
  bool noVncPasswords = !m_config->hasPrimaryPassword() && !m_config->hasReadOnlyPassword();
  bool vncPasswordsError = vncAuthEnabled && noVncPasswords;

  if (rfbServerListening) {
    if (vncPasswordsError) {
      statusString = StringTable::getString(IDS_NO_PASSWORDS_SET);
    } else {
      char localAddressString[1024];
      getLocalIPAddrString(localAddressString, 1024);

      statusString.fromAnsiString(localAddressString);

      if (!vncAuthEnabled) {
        statusString.appendString(StringTable::getString(IDS_NO_AUTH_STATUS));
      } 
    } 
  } else {
    statusString = StringTable::getString(IDS_SERVER_NOT_LISTENING);
  } 

  UINT stringId = m_runAsService ? IDS_TVNSERVER_SERVICE : IDS_TVNSERVER_APP;

  info->m_statusText.format(_T("%s - %s"),
                            StringTable::getString(stringId),
                            statusString.getString());
  info->m_acceptFlag = rfbServerListening && !vncPasswordsError;
  info->m_serviceFlag = m_runAsService;
}

void TvnServer::generateExternalShutdownSignal()
{
  AutoLock l(&m_listeners);

  vector<TvnServerListener *>::iterator it;
  for (it = m_listeners.begin(); it != m_listeners.end(); it++) {
    TvnServerListener *each = *it;

    each->onTvnServerShutdown();
  } 
}

bool TvnServer::isRunningAsService() const
{
  return m_runAsService;
}

void TvnServer::afterFirstClientConnect()
{
}

void TvnServer::afterLastClientDisconnect()
{
  ServerConfig::DisconnectAction action = m_config->getDisconnectAction();

  StringStorage keys;

  switch (action) {
  case ServerConfig::DA_LOCK_WORKSTATION:
    keys.format(_T("%s"), AdditionalActionApplication::LOCK_WORKSTATION_KEY);
    break;
  case ServerConfig::DA_LOGOUT_WORKSTATION:
    keys.format(_T("%s"), AdditionalActionApplication::LOGOUT_KEY);
    break;
  default:
    return;
  }

  Process *process;

  if (isRunningAsService()) {
    process = new CurrentConsoleProcess(_T("tvnserver.exe"), keys.getString());
  } else {
    process = new Process(_T("tvnserver.exe"), keys.getString());
  }

  Log::message(_T("Execute disconnect action in separate process"));

  try {
    process->start();
  } catch (SystemException &ex) {
    Log::error(_T("Failed to start application: \"%s\""), ex.getMessage());
  }

  delete process;
}

void TvnServer::restartHttpServer()
{

  stopHttpServer();

  if (m_config->isAcceptingHttpConnections()) {
    Log::message(_T("Starting HTTP server"));
    try {
      m_httpServer = new HttpServer(_T("0.0.0.0"), m_config->getHttpPort(), m_runAsService);
    } catch (Exception &ex) {
      Log::error(_T("Failed to start HTTP server: \"%s\""), ex.getMessage());
    }
  }
}

void TvnServer::restartControlServer()
{

  stopControlServer();

  Log::message(_T("Starting control server"));

  try {
    StringStorage pipeName;
    ControlPipeName::createPipeName(isRunningAsService(), &pipeName);

    SecurityAttributes *pipeSecurity = new SecurityAttributes();
    pipeSecurity->setInheritable();
    pipeSecurity->shareToAllUsers();

    PipeServer *pipeServer = new PipeServer(pipeName.getString(), pipeSecurity);
    m_controlServer = new ControlServer(pipeServer , m_rfbClientManager);
  } catch (Exception &ex) {
    Log::error(_T("Failed to start control server: \"%s\""), ex.getMessage());
  }
}

void TvnServer::restartMainRfbServer()
{

  stopMainRfbServer();

  if (!m_config->isAcceptingRfbConnections()) {
    return;
  }

  const TCHAR *bindHost = m_config->isOnlyLoopbackConnectionsAllowed() ? _T("localhost") : _T("0.0.0.0");
  unsigned short bindPort = m_config->getRfbPort();

  Log::message(_T("Starting main RFB server"));

  try {
    m_rfbServer = new RfbServer(bindHost, bindPort, m_rfbClientManager, m_runAsService);
  } catch (Exception &ex) {
    Log::error(_T("Failed to start main RFB server: \"%s\""), ex.getMessage());
  }
}

void TvnServer::stopHttpServer()
{
  Log::message(_T("Stopping HTTP server"));

  HttpServer *httpServer = 0;
  {
    AutoLock l(&m_mutex);
    httpServer = m_httpServer;
    m_httpServer = 0;
  }
  if (httpServer != 0) {
    delete httpServer;
  }
}

void TvnServer::stopControlServer()
{
  Log::message(_T("Stopping control server"));

  ControlServer *controlServer = 0;
  {
    AutoLock l(&m_mutex);
    controlServer = m_controlServer;
    m_controlServer = 0;
  }
  if (controlServer != 0) {
    delete controlServer;
  }
}

void TvnServer::stopMainRfbServer()
{
  Log::message(_T("Stopping main RFB server"));

  RfbServer *rfbServer = 0;
  {
    AutoLock l(&m_mutex);
    rfbServer = m_rfbServer;
    m_rfbServer = 0;
  }
  if (rfbServer != 0) {
    delete rfbServer;
  }
}

void TvnServer::resetLogFilePath()
{
  StringStorage pathToLogDirectory;

  TvnLogFilename::queryLogFileDirectory(m_runAsService,
    m_config->isSaveLogToAllUsersPathFlagEnabled(),
    &pathToLogDirectory);

  {
    File logDirectory(pathToLogDirectory.getString());

    logDirectory.mkdir();
  }

  StringStorage pathToLogFile;

  TvnLogFilename::queryLogFilePath(m_runAsService,
    m_config->isSaveLogToAllUsersPathFlagEnabled(),
    &pathToLogFile);

  m_config->setLogFilePath(pathToLogFile.getString());

  m_log.changeFilename(pathToLogFile.getString());
}