/* 
        (c) 2006 by Thomas Michel <tom.michel@arcor.de>

        Kwlan is free software; you can redistribute it and/or modify
        it under the terms of the GNU Library General Public License as
        published by the Free Software Foundation; either version 2 of
        the License, or (at your option) any later version.

        Kwlan 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 Library General Public License for more details.

        You should have received a copy of the GNU Library General Public License
        along with this library; see the file COPYING.LIB.  If not, write to
        the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor,
        Boston, MA 02110-1301, USA.
*/
#include <stdlib.h>

#include <qregexp.h>
#include <qstringlist.h>
#include <qtextstream.h>
#include <kdebug.h>
#include <kprocess.h>
#include <kglobal.h>
#include <kconfig.h>
#include <kstandarddirs.h>
#include <ktempfile.h>


#include "suprocessbase.h"
#include "configuration.h"
#include "globals.h"

class SuProcessBase::Private {
  public:
    KProcess *proc;
    int exitStatus, comm;
    bool passwordSent, authFailed, canRestart;
    RunMode runmode;
    
    QCString password;
    QString marker;
    QStringList args;
    QString user;
    
    KTempFile *wrapper;
};

SuProcessBase::SuProcessBase(QObject *parent, const char *name): 
  QObject(parent, name)
{
  d = new Private;
  // First check for su command (sudo or su)
  KConfig* config = KGlobal::config();
  config->setGroup("super-user-command");
  d->proc = new KProcess(this);
  //init start
  d->exitStatus = -1;
  d->passwordSent = false;
  d->authFailed = false;
  d->canRestart = false;
  d->marker = QString("SPB_%1").arg(random(), 8, 16);
  d->wrapper=0;
  // init end
  d->runmode = NotifyOnExit;
  d->comm = 0;
}

SuProcessBase::~SuProcessBase()
{
  d->password.fill(0);
  if (d->wrapper) delete d->wrapper;
  delete d;
}

SuProcessBase& SuProcessBase::operator << (const QString &arg)
{
  d->args.append(arg);
  return *this;
}

void SuProcessBase::clearArguments()
{
    d->args.clear();
}

bool SuProcessBase::start(RunMode runmode , Communication comm)
{
  prepareStart();

  connect(d->proc, SIGNAL(wroteStdin(KProcess *)), 
    this, SLOT(slotWroteStdin(KProcess *))); 
  connect(d->proc, SIGNAL(receivedStdout(KProcess *, char *, int)), 
    this, SLOT(slotReceivedStdout(KProcess *, char *, int)));
  connect(d->proc, SIGNAL(receivedStderr(KProcess *, char *, int)), 
    this, SLOT(slotReceivedStderr(KProcess *, char *, int)));
  connect(d->proc, SIGNAL(processExited(KProcess *)), 
    this, SLOT(slotProcessExited(KProcess *)));
    
  d->proc->setUsePty(KProcess::Communication(KProcess::Stdin + KProcess::Stdout), false);    
  
  d->runmode = runmode;
  d->comm = comm;
  //return d->proc->start(KProcess::NotifyOnExit, KProcess::All);
  return d->proc->start((KProcess::RunMode)runmode, KProcess::All);
}

void SuProcessBase::restart()
{
  if (d->canRestart) {
    d->exitStatus = -1;
    d->passwordSent = false;
    d->authFailed = false;
    d->canRestart = false;
  
    d->proc->start(KProcess::NotifyOnExit, KProcess::All);
  }
}

void SuProcessBase::prepareStart()
{
    //QString superUserCommand;
    KProcess &proc = *d->proc;
    
  // Experimental: Use kdesu to start commands
  proc << "kdesu";
  proc << "-i";
  proc << "kwlan";
  if (! mainConfiguration().readShowCommands()) proc << "-d";
  //proc << "-c";
  
  QString cmdline;// = "\"";
  for(QStringList::Iterator it = d->args.begin(); it != d->args.end(); it++) {
      if (*it != ";") 
          cmdline = cmdline +*it +" ";
      else cmdline += "&& ";
  }
  if (::debugOutput) kdDebug() << "Starting kdesu using following arguments:" << cmdline << endl;
  proc << cmdline;
}

void SuProcessBase::slotWroteStdin(KProcess *)
{
  if (d->passwordSent)
    d->password.fill(0);
}

void SuProcessBase::slotReceivedStdout(KProcess *, char *buffer, int buflen)
{
  handleOutput(TypeStdout, buffer, buflen);
}

void SuProcessBase::slotReceivedStderr(KProcess *, char *buffer, int buflen)
{
  handleOutput(TypeStderr, buffer, buflen);
}

void SuProcessBase::slotProcessExited(KProcess *)
{
    d->exitStatus = d->proc->exitStatus();
    d->canRestart = true;
    if (d->runmode != DontCare)
        emit processExited(this);
}

void SuProcessBase::handleOutput(OutputType type, char *buffer, int buflen)
{
  // Split data into separate lines first, because even when the app is
  // running we need to detect out-of-band data, which is line-based
  // rather than on raw data streams
  QString rawBuffer = QString::fromLocal8Bit( buffer, buflen );
  QStringList lines = QStringList::split( QRegExp( "\n|\n\r|\r\n" ), rawBuffer, true );
  QStringList::Iterator it = lines.begin();    
  
  while ( it != lines.end() ) {
    QString curLine = *it;
    
    if (debugOutput)   kdDebug() << curLine << endl;
    it++;
  }	

  if ( !lines.isEmpty() ) {
    // FIXME: The join removes any distinction between \n and \r. This is
    //        ok for most console applications, but for apps that use real
    //        terminal I/O, like curses apps, this is not good.
    //        Rewrite the algorithm to preserve the used type of newline.
    QCString newBuffer = lines.join( "\r\n" ).local8Bit();

    // FIXME: The const_cast is ugly, to say the least. Either we need to
    //        divert from KProcess compatibility and make the buffer
    //        argument const, or we need to allocate a temporary local
    //        buffer.
    if ( type == TypeStdout ) {
      if (d->comm & Stdout)
        emit receivedStdout( this, const_cast<char *>( ( const char * )( newBuffer ) ), newBuffer.length() );
    }	
    else {
      if (d->comm & Stderr)
        emit receivedStderr( this, const_cast<char *>( ( const char * )( newBuffer ) ), newBuffer.length() );
    }	
  }

}


int SuProcessBase::exitStatus() const
{
  return d->exitStatus;
}

/*
bool SuProcessBase::isRunning() const
{
  return (d->state == Running);
}
*/

QString SuProcessBase::quote(const QString &arg)
{
  return KProcess::quote(arg);
}
