/* kpPty.cpp
**
** Copyright (C) 1999,2000 Toivo Pedaste <toivo@ucs.uwa.edu.au>
**
*/

/*
** 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 in a file called COPYING; if not, write to
** the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
** MA 02111-1307, USA.
*/

/*
** Bug reports and questions can be sent to kde-devel@kde.org
*/


#include "../config.h"

#include <qtimer.h>

#include <kprocctrl.h>
#include <kpty.h>
#include <kdebug.h>

#include <kpPty.h>
#include <kpackage.h>
#include <kpTerm.h>
#include <options.h>

#define PROMPT "# "

extern Opts *opts;
//////////////////////////////////////////////////////////////////////////////

kpKProcIO::kpKProcIO ( QTextCodec *_codec)
  : KProcIO(_codec)
{
}

kpKProcIO::~kpKProcIO()
{
}

bool kpKProcIO::sstart (RunMode runmode)
{

  connect (this, SIGNAL (receivedStdout (KProcess *, char *, int)),
	   this, SLOT (received (KProcess *, char *, int)));


  connect (this, SIGNAL (wroteStdin(KProcess *)),
	   this, SLOT (sent (KProcess *)));

  return KProcess::start (runmode,( KProcess::Communication) ( KProcess::Stdin |  KProcess::Stdout));
}

//////////////////////////////////////////////////////////////////////////////

kpPty::kpPty() : QObject()
{
  pty = new kpKProcIO();
  pty->setUsePty(KProcess::All, false);

  connect(pty, SIGNAL(readReady(KProcIO *)), this,
		   SLOT(readLines()));
  connect(pty, SIGNAL(processExited(KProcess *)), this,
		   SLOT(done()));
  pty->pty()->setWinSize(0,80);
  tm = new QTimer(this);
  connect(tm, SIGNAL(timeout()), this, SLOT(slotTimeout()));

  eventLoop = FALSE;
  started = FALSE;
  pUnterm = FALSE;
}


kpPty::~kpPty()
{
}

bool kpPty::start(bool needRoot)
{
  pUnterm = FALSE;

  if (!started && (!hostName.isEmpty() || !( !needRoot))) {
    // Assume !needRoot actions are simple executables
    int ret;
    QString s = "echo START=$?\n";
    //    terminator.setPattern("START=\\d+");

    retList.clear();
    pty->resetAll();

    if (opts->useSSH || !hostName.isEmpty()) {
      kdDebug() << "/usr/bin/ssh\n";
      (*pty) << "/usr/bin/ssh";
      (*pty) << "-t";
      (*pty) << "-l" << "root";
      if (hostName.isEmpty()) {
	(*pty) << "localhost";
      } else {
	(*pty) << hostName;
      }
      (*pty) << "env PATH=/bin:/usr/bin:/sbin:/usr/sbin:/usr/local/bin PS1='" PROMPT "' sh";
    } else {
      kdDebug() << "su\n";
      pty->setEnvironment("PS1", PROMPT);
      (*pty) << "su";
      (*pty) << "-s" << "/bin/sh";
    }
    pty->sstart(KProcess::NotifyOnExit);
    //    pty->start(KProcess::NotifyOnExit, false);

    timeout = FALSE;
    tm->start(3*1000, TRUE);
    eventLoop = TRUE;
    kdDebug() << "Loopst\n";
    kapp->enter_loop();
    kdDebug() << "Loopfn t=" << timeout << " p=" << gotPrompt << "\n";
    tm->stop();
    if (timeout) {
      //      kdDebug() << "Start " << retList.count() <<" \n";
      kpstart->addText(retList);
      kpstart->run("", i18n("ROOT Login"));

      ret = kpstart->exec();
      kdDebug() << "Sret=" << ret << "\n";
      if (ret) {
	started = FALSE;
      } else {
	started = TRUE;
      }
    } else {
      if (!gotPrompt) {
	started = FALSE;
	pty->writeStdin(QCString("\04"), false);  // SU doesn't listen to ^C
      } else {
	started = TRUE;
      }
    }
    kdDebug() << "Started=" << started << "\n";
    return started;
  }
  return TRUE;
}


QStringList kpPty::run(const QString &cmd, bool inLoop, bool needRoot)
{
  bool quote = FALSE;
  Result = 0;
  QString s;

  //  terminator.setPattern("RESULT=\\d+");
  pUnterm = FALSE;
  gotPrompt = FALSE;

  if (hostName.isEmpty() && !started && (!needRoot)) {
    // Assume !needRoot actions are simple executables
    pty->resetAll();
    kdDebug() << " kpPty::run CMD=\""<< cmd <<"\" pty = " << pty << endl;
    QStringList cl = QStringList::split(" ", cmd);
    for ( QStringList::Iterator it = cl.begin(); it != cl.end(); ++it ) {
      int lastPt = (*it).length() - 1;
      if ((*it)[0] == '\'') { // Start of quoted string
	s = *it;
	if ((*it)[lastPt] == '\'') { // Also End of quoted string
	  s.replace("'","");
	  (*pty) << s;
	  //	  kdDebug() << "C1=" << s << "\n";
	  quote = FALSE;
	} else {
	  s += " ";
	  quote = TRUE;
	}
      } else if ((*it)[lastPt] == '\'') { // End of quoted string
	s += *it;
	s.replace("'","");
	(*pty) << s;
	//	kdDebug() << "C=" << s << "\n";
	quote = FALSE;
      }	else if (quote) {
	s += *it;
	s += " ";
      } else {
	(*pty) << (*it);
	//	kdDebug() << "Cc=" << *it << "\n";
      }
    }
    pty->setEnvironment("TERM", "dumb");
    if (!pty->sstart(KProcess::NotifyOnExit)) {
      kdDebug() << " kpPty::run returna=0\n";
      return 0;
    }
  } else {
    if (start(needRoot)) {
      kdDebug() << "CMDroot='"<< cmd <<"'\n";
      QString s = cmd + ";echo RESULT=$?";
      pty->writeStdin(s);
    } else {
      kdDebug() << " kpPty::run returnb=0\n";
      return 0;
    }
  }

  retList.clear();

  if (inLoop) {
    eventLoop = TRUE;
    kapp->enter_loop();

    return retList;
  } else {
    return 0;
  }
}

void kpPty::close() {
  //  kdDebug() << "kpPty::close\n";

  pty->closeAll();
  while(pty->isRunning()) {
    KProcessController::theKProcessController->waitForProcessExit(1);
  }
  started = false;
}

void kpPty::finish(int ret)
{
  //  kdDebug() << "kpPty::finish " << ret << "\n";

  QStringList::Iterator l;

  if (ret == -1) {  // Called program executed
    if (!retList.empty()) {
      l = retList.fromLast();
      if ((*l).right(2) == PROMPT) {
	retList.remove(l);                  // Remove prompt
      }
    }

    if (!retList.empty()) {
      int p;
      l = retList.fromLast();
      if ((p = (*l).find("RESULT=")) >= 0) {
	ret = (*l).mid(p+7).toInt(0,10);
	retList.remove(l);                  // Remove return code
      } else {
	ret = 666;
      }
    }

    if (!retList.empty()) {
      l = retList.begin();
      if ( l !=  retList.end()) {
	if ((*l).find("RESULT=") >= 0) {
	  retList.remove(l);                  // Remove command at start
	}
      }
    }
  }

  Result = ret;
  //  kdDebug() << "Result=" << Result << " C=" << retList.count() << "\n";
  emit result(retList,ret);

  if (eventLoop) {
    eventLoop = FALSE;
    kapp->exit_loop();
  }
}

void kpPty::readLines()
{
  bool unterm = FALSE;

  QString stext;
  while(pty->readln(stext, false, &unterm) >= 0)
  {
    emit textIn(stext, !unterm);
    //        kdDebug() << "[" << stext << ">\n";
    if (pUnterm) {
      QStringList::Iterator lst = retList.fromLast();
      if (lst != retList.end())
      {
        stext = *lst + stext;
        retList.remove(lst);
      }
    }

    if (!unterm)
    {
      if (stext.endsWith("\r"))
        stext.truncate(stext.length()-1);

      int i = stext.findRev('\r');
      if (i > -1)
         stext = stext.mid(i+1);
    }

    pUnterm = unterm;

    retList << stext;

    if (stext.right(2) == PROMPT) {
      //      kdDebug() << "ST=" << stext << "=\n";
      gotPrompt = TRUE;
      emit textIn("\r \n", false);
      finish(-1);
    }
  }
  pty->ackRead();
}

void kpPty::keyOut(char ch)
{
  QCString s(2);
  s[0] = ch;
  s[1] = '\0';
  pty->writeStdin(s, false);
}

void kpPty::done()
{
  int ret = pty->exitStatus();
  QString stext;

  //  kdDebug() << "Done (" << ret << ")" << endl;

  finish(ret);
}

void kpPty::slotTimeout()
{
  kdDebug() << "Timeout..............\n";
  if (eventLoop) {
    timeout = TRUE;
    eventLoop = FALSE;
    kapp->exit_loop();
  }
}
#include "kpPty.moc"
