/*****************************************************************
ksmserver - the KDE session management server

Copyright (C) 2000 Matthias Ettrich <ettrich@kde.org>
******************************************************************/

#include <config.h>

#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>

#include <dcopclient.h>
#include <qmessagebox.h>
#include <qdir.h>

#include <kapplication.h>
#include <kcmdlineargs.h>
#include <kaboutdata.h>
#include <kdebug.h>
#include <klocale.h>
#include <kglobal.h>
#include <kconfig.h>
#include "server.h"


static const char version[] = "0.4";
static const char description[] = I18N_NOOP( "The reliable KDE session manager that talks the standard X11R6 \nsession management protocol (XSMP)." );

static const KCmdLineOptions options[] =
{
   { "r", 0, 0 },
   { "restore", I18N_NOOP("Restores the saved user session if available"), 0},
   { "w", 0, 0 },
   { "windowmanager <wm>", I18N_NOOP("Starts 'wm' in case no other window manager is \nparticipating in the session. Default is 'kwin'"), 0},
   { "nolocal", I18N_NOOP("Also allow remote connections"), 0},
   KCmdLineLastOption
};

extern KSMServer* the_server;

void IoErrorHandler ( IceConn iceConn)
{
    the_server->ioError( iceConn );
}

bool writeTest(QCString path)
{
   path += "/XXXXXX";
   int fd = mkstemp(path.data());
   if (fd == -1)
      return false;
   if (write(fd, "Hello World\n", 12) == -1)
   {
      int save_errno = errno;
      close(fd);
      unlink(path.data());
      errno = save_errno;
      return false;
   }
   close(fd);
   unlink(path.data());
   return true;
}

void sanity_check( int argc, char* argv[] )
{
  QCString msg;
  QCString path = getenv("HOME");
  QCString readOnly = getenv("KDE_HOME_READONLY");
  if (path.isEmpty())
  {
     msg = "$HOME not set!";
  }
  if (msg.isEmpty() && access(path.data(), W_OK))
  {
     if (errno == ENOENT)
        msg = "$HOME directory (%s) does not exist.";
     else if (readOnly.isEmpty())
        msg = "No write access to $HOME directory (%s).";
  }
  if (msg.isEmpty() && access(path.data(), R_OK))
  {
     if (errno == ENOENT)
        msg = "$HOME directory (%s) does not exist.";
     else
        msg = "No read access to $HOME directory (%s).";
  }
  if (msg.isEmpty() && readOnly.isEmpty() && !writeTest(path))
  {
     if (errno == ENOSPC)
        msg = "$HOME directory (%s) is out of disk space.";
     else
        msg = "Writing to the $HOME directory (%s) failed with\n    "
              "the error '"+QCString(strerror(errno))+"'";
  }
  if (msg.isEmpty())
  {
     path = getenv("ICEAUTHORITY");
     if (path.isEmpty())
     {
        path = getenv("HOME");
        path += "/.ICEauthority";
     }

     if (access(path.data(), W_OK) && (errno != ENOENT))
        msg = "No write access to '%s'.";
     else if (access(path.data(), R_OK) && (errno != ENOENT))
        msg = "No read access to '%s'.";
  }
  if (msg.isEmpty())
  {
     path = DCOPClient::dcopServerFile();
     if (access(path.data(), R_OK) && (errno == ENOENT))
     {
        // Check iceauth
        if (DCOPClient::iceauthPath().isEmpty())
           msg = "Could not find 'iceauth' in path.";
     }
  }
  if (msg.isEmpty())
  {
     path = getenv("KDETMP");
     if (path.isEmpty())
        path = "/tmp";
     if (!writeTest(path))
     {
        if (errno == ENOSPC)
           msg = "Temp directory (%s) is out of disk space.";
        else
           msg = "Writing to the temp directory (%s) failed with\n    "
                 "the error '"+QCString(strerror(errno))+"'";
     }
  }
  if (msg.isEmpty() && (path != "/tmp"))
  {
     path = "/tmp";
     if (!writeTest(path))
     {
        if (errno == ENOSPC)
           msg = "Temp directory (%s) is out of disk space.";
        else
           msg = "Writing to the temp directory (%s) failed with\n    "
                 "the error '"+QCString(strerror(errno))+"'";
     }
  }
  if (msg.isEmpty())
  {
     path += ".ICE-unix";
     if (access(path.data(), W_OK) && (errno != ENOENT))
        msg = "No write access to '%s'.";
     else if (access(path.data(), R_OK) && (errno != ENOENT))
        msg = "No read access to '%s'.";
  }
  if (!msg.isEmpty())
  {
    const char *msg_pre =
             "The following installation problem was detected\n"
             "while trying to start KDE:"
             "\n\n    ";
    const char *msg_post = "\n\nKDE is unable to start.\n";
    fputs(msg_pre, stderr);
    fprintf(stderr, msg.data(), path.data());
    fputs(msg_post, stderr);

    QApplication a(argc, argv);
    QCString qmsg(256+path.length());
    qmsg.sprintf(msg.data(), path.data());
    qmsg = msg_pre+qmsg+msg_post;
    QMessageBox::critical(0, "KDE Installation Problem!",
        QString::fromLatin1(qmsg.data()));
    exit(255);
  }
}

extern "C" KDE_EXPORT int kdemain( int argc, char* argv[] )
{
    sanity_check(argc, argv);

    KAboutData aboutData( "ksmserver", I18N_NOOP("The KDE Session Manager"),
       version, description, KAboutData::License_BSD,
       "(C) 2000, The KDE Developers");
    aboutData.addAuthor("Matthias Ettrich",0, "ettrich@kde.org");
    aboutData.addAuthor("Luboš Luňák", I18N_NOOP( "Maintainer" ), "l.lunak@kde.org" );

    KCmdLineArgs::init(argc, argv, &aboutData);
    KCmdLineArgs::addCmdLineOptions( options );

    putenv((char*)"SESSION_MANAGER=");
    KApplication a(false, true); // Disable styles until we need them.
    fcntl(ConnectionNumber(qt_xdisplay()), F_SETFD, 1);


    KCmdLineArgs *args = KCmdLineArgs::parsedArgs();

    kapp->dcopClient()->registerAs("ksmserver", false);
    if (!kapp->dcopClient()->isRegistered())
    {
       qWarning("Could not register with DCOPServer. Aborting.");
       return 1;
    }

    QCString wm = args->getOption("windowmanager");
    if ( wm.isEmpty() )
	wm = "kwin";

    bool only_local = args->isSet("local");
#ifndef HAVE__ICETRANSNOLISTEN
    /* this seems strange, but the default is only_local, so if !only_local
     * the option --nolocal was given, and we warn (the option --nolocal
     * does nothing on this platform, as here the default is reversed)
     */
    if (!only_local) {
        qWarning("--[no]local is not supported on your platform. Sorry.");
    }
    only_local = false;
#endif

    KSMServer *server = new KSMServer( QString::fromLatin1(wm), only_local);
    kapp->dcopClient()->setDefaultObject( server->objId() );

    IceSetIOErrorHandler( IoErrorHandler );

    KConfig *config = KGlobal::config();
    config->setGroup( "General" );

    int realScreenCount = ScreenCount( qt_xdisplay() );
    bool screenCountChanged =
         ( config->readNumEntry( "screenCount", realScreenCount ) != realScreenCount );

    QString loginMode = config->readEntry( "loginMode", "restorePreviousLogout" );

    if ( args->isSet("restore") && ! screenCountChanged )
	server->restoreSession( SESSION_BY_USER );
    else if ( loginMode == "default" || screenCountChanged )
	server->startDefaultSession();
    else if ( loginMode == "restorePreviousLogout" )
	server->restoreSession( SESSION_PREVIOUS_LOGOUT );
    else if ( loginMode == "restoreSavedSession" )
	server->restoreSession( SESSION_BY_USER );
    else
	server->startDefaultSession();
    return a.exec();
}

