/*
 *   KDE network utilities (knu.cpp)
 *
 *   Copyright (C) 1997-1999  Bertrand Leconte
 *
 *   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., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
/*
 * $Id: knu.cpp,v 1.30 2000/02/07 21:13:14 hadacek Exp $
 *
 * $Log: knu.cpp,v $
 * Revision 1.30  2000/02/07 21:13:14  hadacek
 * KAccel/KStdAccel API cleanup + KAction/KAccel extension
 *
 * Revision 1.29  2000/01/23 18:16:11  charles
 * Forgot to delete a few things.
 *
 * Revision 1.28  2000/01/23 02:35:39  charles
 * KAboutData and I18N_NOOP updates.  KDE-Network is complete.  Or so I hope!
 * KCmdLineArgs is really an obnoxious class, by the way.
 *
 * Revision 1.27  2000/01/14 15:57:33  waba
 * WABA: Use KCmdLineArgs
 *
 * Revision 1.26  1999/11/08 09:56:52  bero
 * Fix compilation
 *
 * Revision 1.25  1999/10/25 00:26:30  tibirna
 * CT: make kdenetwork compile
 *
 * Revision 1.24  1999/10/17 17:16:31  tibirna
 * CT: QString ->QCString where needed.
 *     QScrollbarExtent is now a full QSize
 *
 * Revision 1.23  1999/10/09 00:26:48  kalle
 * The dreaded library cleanup: getConfig() -> config() and friends (see separate mail)
 *
 * Revision 1.22  1999/10/08 22:52:28  bero
 * KTMainWindow
 *
 * Revision 1.21  1999/10/08 04:24:54  corey
 * Added a netstat tab.
 *
 * Revision 1.20  1999/10/01 00:39:52  corey
 * - PNG-ize the .desktop
 * - Remove obsolete deleteAll() calls
 * - Tack a 0 onto the end of the buffer so no more random characters in output.
 *     Is this something we should put in KProcess or leave it to the programs?
 *
 * Revision 1.19  1999/07/25 17:32:46  kulow
 * making it compile again. Kmail I will have to take out ;(
 *
 * Revision 1.18  1999/07/22 16:33:11  kulow
 * some more KDE2 changes. Some left. I'm neither a XDND or QSplitter expert :)
 *
 * Revision 1.17  1999/07/02 15:15:30  bieker
 * Added missing i18n("OK")s etc.
 *
 * Revision 1.16  1999/06/18 21:22:10  leconte
 * . use KTMainWindow insteed of KTMainWindow
 * . myListManager added here (it will perhaps goes to kdelibs)
 * . klistmanagertest added
 * . some cleanup
 *
 * Revision 1.15  1999/05/06 15:57:51  bieker
 * * QString::sprintf -> QString::arg
 * * and removed some .data()s..
 *
 * I hope I didn't break anything.. :)
 *
 * Revision 1.14  1999/04/18 08:12:15  kulow
 * CVS_SILENT fixing some header files (mainly adding klocale.h)
 *
 * Revision 1.13  1999/03/24 21:02:55  leconte
 * . whois command added
 * . finger can now be called with an empty user (asked by
 *         "Raul Dias" <raul@dias.com.br>)
 * . small QStrings mods
 *
 * Revision 1.12  1999/03/13 16:19:43  mario
 * Mario: made it work for Qt 2.0
 *
 * Revision 1.11  1999/03/01 23:39:38  kulow
 * CVS_SILENT ported to Qt 2.0
 *
 * Revision 1.10.4.1  1999/02/22 22:30:04  kulow
 * CVS_SILENT replaced old qt header names with new ones
 *
 * Revision 1.10  1999/01/10 00:33:05  bieker
 * i18n() the about box.
 *
 * Revision 1.9  1998/10/29 21:26:30  leconte
 * Bertrand:
 * . renamed Matt s traceroute to mtr as asked by the new mtr maintainer
 * . mtr-0.25 has the diffs included. No more patch here
 * . updated knu for mtr-0.25
 *
 * Revision 1.8  1998/10/14 19:33:03  leconte
 * Bertrand: Added mtr support (with a patch to mtr-0.21)
 *
 * Revision 1.7  1998/09/23 16:24:15  bieker
 * Use i18n() instead of _().
 *
 * Revision 1.6  1998/06/11 19:20:36  leconte
 * - some accelerators added
 * - strings added to i18n
 *
 * Revision 1.5  1998/06/09 21:18:05  leconte
 * Bertrand: correction of bug #745 (reported by Duncan Haldane):
 * 	arguments are now added to the command line
 *
 * Revision 1.4  1998/03/01 19:30:22  leconte
 * - added a finger tab
 * - internal mods
 *
 * Revision 1.3  1997/12/07 23:44:14  leconte
 * - handle the binary's name modification dynamicaly (problem reported
 *   by Conrad Sanderson)
 * - added browse button to the option dialog (for binary selection)
 * - code clean-up
 * - better fallback to "nslookup" if "host" is not found
 *
 * Revision 1.2  1997/11/23 22:28:17  leconte
 * - Id and Log added in the headers
 * - Patch from C.Czezatke applied (preparation of KProcess new version)
 *
 */

#include <sys/wait.h>
#include <unistd.h>
#include <string.h>

#include <qmessagebox.h>
#include <qfileinfo.h>

#include <kstdaccel.h>

#include "knu.h"
#include "knu.moc"

#include "version.h"
#include "khelpmenu.h"
#include <klocale.h>
#include <kconfig.h>
#include <kaboutdata.h>
#include <kmenubar.h>
#include <kcmdlineargs.h>

#define FRAME_BORDER_WIDTH 6

QString   CaptionString;

static const char *description = 
	I18N_NOOP("KDE Network Utilities");

static const char *version = "v0.0.1";

void quit();

/**
 * check to see if this current tab is enabled in the config file
 */
bool
isTabEnabled(QString tabName, KConfig *kc)
{
  kc->setGroup(tabName);
  if (kc->readNumEntry("enable", 1) == 0) {
    return(FALSE);
  } else {
    return(TRUE);
  }
}
 

/**
 * Constructor 
 */
TopLevel::TopLevel(QWidget *, const char *name)
  : KTMainWindow(name)
{
  KConfig         *kc = kapp->config();
  PingDlg         *pd;
  TracerouteDlg   *td;
  HostDlg         *hd;
  FingerDlg       *fd;
  MtrDlg          *md;
  WhoisDlg        *wd;
  NetstatDlg      *nd;

  /*
   * Set caption string
   */
  setCaption(CaptionString);
  
  kconfig = kc;
    
  /*
   * Create MenuBar
   */
  createMenu();
  setMenu(menuBar);

  
  /*
   * Create the tabctrl widget
   */
  tabCtrl = new KTabCtl(this);
  CHECK_PTR(tabCtrl);
  connect(tabCtrl, SIGNAL(tabSelected(int)), 
	  this, SLOT(slotTabChanged(int)));
  pagesNumber = 0;

  /*********************
   * Don't forget the slotConfig function while adding tabs
   *********************/

  // ping tab
  if (isTabEnabled("Ping", kc)) {
    pd = new PingDlg("ping", tabCtrl, i18n("&Ping"));
    CHECK_PTR(pd);
    tabCtrl->addTab(pd, pd->name());
    pages[pagesNumber] = pd;
    pagesNumber++;
  }

  // traceroute tab 
  if (isTabEnabled("Traceroute", kc)) {
    td = new TracerouteDlg("traceroute", tabCtrl, i18n("&Traceroute"));
    CHECK_PTR(td);
    tabCtrl->addTab(td, td->name());
    pages[pagesNumber] = td;
    pagesNumber++;
  }

  // host tab
  if (isTabEnabled("Host resolution", kc)) {
    hd = new HostDlg("host", tabCtrl, i18n("Host &resolution"));
    /* 
     * the "host" command name can be modified by 
     * HostDlg::search_for_binary if host is not found
     */
    CHECK_PTR(hd);
    tabCtrl->addTab(hd, hd->name());
    pages[pagesNumber] = hd;
    pagesNumber++;
  }

  // finger tab 
  if (isTabEnabled("Finger", kc)) {
    fd = new FingerDlg("finger", tabCtrl, i18n("F&inger"));
    CHECK_PTR(fd);
    tabCtrl->addTab(fd, fd->name());
    pages[pagesNumber] = fd;
    pagesNumber++;
  }

  // mtr tab 
  if (isTabEnabled("Mtr", kc)) {
    md = new MtrDlg("mtr", tabCtrl, i18n("&mtr"));
    CHECK_PTR(md);
    tabCtrl->addTab(md, md->name());
    pages[pagesNumber] = md;
    pagesNumber++;
  }

  // whois tab
  if (isTabEnabled("Whois", kc)) {
    wd = new WhoisDlg("whois", tabCtrl, i18n("&Whois"));
    CHECK_PTR(wd);
    tabCtrl->addTab(wd, wd->name());
    pages[pagesNumber] = wd;
    pagesNumber++;
  }

  // netstat tab
  if (isTabEnabled("Netstat", kc)) {
    nd = new NetstatDlg("netstat", tabCtrl, i18n("Netstat"));
    CHECK_PTR(nd);
    tabCtrl->addTab(nd, nd->name());
    pages[pagesNumber] = nd;
    pagesNumber++;
  }
  if (pagesNumber == 0) {
    // There is *no* command enabled ! 

    QMessageBox::critical(this, caption(),
			  i18n("There is no command enabled\n"
			  "in the configuration file.\n\n"
			  "So I can't do anything...\n"),
			  i18n("OK"));
    exit(-1);
  }
  setFrameBorderWidth(FRAME_BORDER_WIDTH);
  //tabCtrl->adjustSize();
  //tabCtrl->setMinimumSize(tabCtrl->size());
  setView(tabCtrl, true);		// for KTMainWindow
  currentTab = -1;

  /*
   * Enable File->Close if needed
   * (calls each toplevel window's enableFileClose() )
   */
  if (memberList->count() > 1) {
    if (memberList->count() == 2) {
      KTMainWindow *ktmw = memberList->first();
      while (ktmw) {
	((TopLevel *)ktmw)->enableFileClose(true);
	ktmw = memberList->next();
      }
    }
  } else {
    enableFileClose(false);
  }

  menuBar->show();
  adjustSize();

  setMinimumSize(QMAX(tabCtrl->minimumSize().width()
		      +2*FRAME_BORDER_WIDTH, 500), 
		 QMAX(tabCtrl->minimumSize().height()
		      +2*FRAME_BORDER_WIDTH, 300));
}


/**
 * Create the KMenuBar
 */
void
TopLevel::createMenu()
{
  int mi;

  fileMenu = new QPopupMenu;
  CHECK_PTR(fileMenu);
  fileMenu->insertItem(i18n("&New window"), 
				   this, SLOT(slotNew()), KStdAccel::key(KStdAccel::New));
  fileMenu->insertSeparator();
  closeIndex = fileMenu->insertItem(i18n("&Close window"), 
				    this, SLOT(slotClose()), 
					KStdAccel::key(KStdAccel::Close));
  fileMenu->insertItem(i18n("&Quit"), this, SLOT(slotQuit()), 
				   KStdAccel::key(KStdAccel::Quit));
  
  editMenu = new QPopupMenu;
  CHECK_PTR(editMenu);
  mi = editMenu->insertItem(i18n("&Copy"), this, SLOT(slotCopy()), 
					KStdAccel::key(KStdAccel::Copy));
  editMenu->setItemEnabled(mi, FALSE);
  editMenu->insertItem(i18n("Select &all"), 
		       this, SLOT(slotSelectAll()));
  editMenu->insertSeparator();
  editMenu->insertItem(i18n("C&lear output window"), 
		       this, SLOT(slotClear()));
  editMenu->insertSeparator();
  configIndex = editMenu->insertItem(i18n("P&references..."), 
				     this, SLOT(slotConfig()));

  
#if 1
  QString about;
  about = i18n("Knu\nVersion %1\n\nBertrand Leconte\n<B.Leconte@mail.dotcom.fr>\n").arg(KNU_VERSION);
  KHelpMenu *myHelpMenu = new KHelpMenu( this, about);
#else
  // old style
  helpMenu = new QPopupMenu;
  CHECK_PTR(helpMenu);
  helpMenu->insertItem(i18n("&Contents"),
			   this, SLOT(slotHelp()), KStdAccel::key(KStdAccel::Help));
  helpMenu->insertSeparator();
  QString str;
  str = i18n("&About %1").arg(KNU_APPNAME);
  helpMenu->insertItem(str, this, SLOT(slotAbout()), SHIFT+Key_F1);
  helpMenu->insertItem(i18n("About &Qt"), this, SLOT(slotAboutQt()));
#endif
  
  menuBar = new KMenuBar(this);
  CHECK_PTR(menuBar);
  menuBar->insertItem(i18n("&File"), fileMenu);
  menuBar->insertItem(i18n("&Edit"), editMenu);
  menuBar->insertSeparator();
#if 1
  menuBar->insertItem(i18n("&Help"), myHelpMenu->menu());
#else
  menuBar->insertItem(i18n("&Help"), helpMenu);
#endif
}


/** 
 * enableFileClose - called to change the status (enable/disable)
 * of the File-> Close menu
 */
void
TopLevel::enableFileClose(bool toggle)
{
  debug("TopLevel::enableFileClose(%s)", (toggle ? "true" : "false"));
  fileMenu->setItemEnabled(closeIndex, toggle);
}

/**
 * Edit->Preferences menu entry
 */
void 
TopLevel::slotConfig()
{
  CommandCfgDlg   *configPages[10];
  OptionsDlg      *options;
  int n = 0;

  /*
   * Create options dialogBox
   */
  
  /* ping */
  CommandCfgDlg *ccd = new CommandCfgDlg(i18n("&Ping"), 0, "ping_cfg");
  CHECK_PTR(ccd);
  configPages[n] = ccd;
  n++;
  
  /* traceroute */
  ccd = new CommandCfgDlg(i18n("&Traceroute"), 0, "traceroute_cfg");
  CHECK_PTR(ccd);
  configPages[n] = ccd;
  n++;
  
  /* host resolution */
  HostCfgDlg *hcd = new HostCfgDlg(i18n("Host &resolution"), 0, "host_cfg");
  CHECK_PTR(hcd);
  configPages[n] = hcd;
  n++;

  /* finger */
  ccd = new CommandCfgDlg(i18n("&Finger"), 0, "finger_cfg");
  CHECK_PTR(ccd);
  configPages[n] = ccd;
  n++;

  /* mtr */
  ccd = new CommandCfgDlg(i18n("&mtr"), 0, "mtr_cfg");
  CHECK_PTR(ccd);
  configPages[n] = ccd;
  n++;

  /* whois */
  WhoisCfgDlg *wcd = new WhoisCfgDlg(i18n("&Whois"), 0, "whois_cfg");
  CHECK_PTR(wcd);
  configPages[n] = wcd;
  n++;

  /* netstat */
  CommandCfgDlg *ncd = new CommandCfgDlg(i18n("Netstat"), 0, "netstat_cfg");
  CHECK_PTR(ncd);
  configPages[n] = ncd;
  n++;
  
  options = new OptionsDlg(configPages, n, 0);
  CHECK_PTR(options);

  if (options->exec()) {
    kapp->config()->sync();
  } else {
    // nothing to do
  }
  
  // distribute the new configs values among all the toplevel windows
  KTMainWindow *ktmw = memberList->first();
  
  while (ktmw) {
    ((TopLevel *)ktmw)->checkBinaryAndDisplayWidget();
    ktmw = memberList->next();
  }
}

/**
 * Pass checkBinaryAndDisplayWidget to each tab
 */
void 
TopLevel::checkBinaryAndDisplayWidget()
{
  int i;
  for (i=0; i<pagesNumber; i++) {
    pages[i]->checkBinaryAndDisplayWidget();
  }
}


/**
 * Destructor
 */
TopLevel::~TopLevel()
{
  KTMainWindow *ktmw;
  debug("TopLevel::~TopLevel() [memberList->count() = %d]", 
	memberList->count());

  if (memberList->count() == 2) {
    // disable last window File->Close menu entry
    // (I don't know if I'm the first or the last one...)
    ktmw = memberList->first();
    ((TopLevel *)ktmw)->enableFileClose(false);
    ktmw = memberList->next();
    ((TopLevel *)ktmw)->enableFileClose(false);
  }
}


/**
 * called when a tab is changed
 */
void
TopLevel::slotTabChanged(int newTab)
{
  if (newTab != currentTab) {
    if (currentTab >= 0) {
      pages[currentTab]->tabDeselected();
    }
    pages[newTab]->tabSelected();
    currentTab = newTab;
  }
}

/**
 * File->Quit menu entry
 */
void 
TopLevel::slotQuit()
{
  debug("TopLevel::slotQuit()");
  ::quit();
}

/**
 * Edit->Copy menu entry
 */
void 
TopLevel::slotCopy()
{
  debug("TopLevel::slotCopy() is not yet implemented");
}

/**
 * Edit->Clear output window menu entry
 */
void 
TopLevel::slotClear()
{
  pages[currentTab]->clearOutput();
}

/**
 * File->New window menu entry
 */
void 
TopLevel::slotNew()
{
  TopLevel *toplevel = new TopLevel;
  toplevel->show();
}

/**
 * File->Close window menu entry
 */
void 
TopLevel::slotClose()
{
  //debug("TopLevel::slotClose()");

  delete this;			// TopLevel has been allocated by new
}

/**
 * Edit->Select all menu entry
 */
void 
TopLevel::slotSelectAll()
{
  pages[currentTab]->selectAll();
}


/**
 * Help->Contents menu entry
 */
void 
TopLevel::slotHelp()
{
  kapp->invokeHTMLHelp("", "");
}


/*
 * Called when the user is login out.
 */
void
TopLevel::saveProperties(KConfig *kc)
{
  kc->writeEntry("CurrentTab", currentTab);
}

void
TopLevel::readProperties(KConfig *kc)
{
  int entry = kc->readNumEntry("CurrentTab", -1);
  if (entry >= 0) {
    //tabCtrl->setCurrentTab(entry);
    // This is not implemented yet (bug somewhere. Qt ?)
    // FIXME
  }
}



/**
 * Application quit function
 */
void
quit()
{
  debug("quit()");
  kapp->quit();
}


/**
 * Test if a filename is executable.
 * If there is not / in the filename, search in the path.
 */
bool
test_for_exec(QString filename)
{
  bool rc = FALSE;

  if (strchr(filename, '/')) {
    // It's a full path
    if (access(filename, X_OK)) {
      rc = FALSE;
    } else {
      rc = TRUE;
    }
  } else {
    // Check in the path
    char *pathEnv = getenv("PATH");
    char *p;
    char *path = (char *)malloc(strlen(pathEnv)+1);

    strcpy(path, pathEnv);

    p = strtok(path, ":");
    while (p != 0) {
      //debug("  ELEM = \"%s\"", p);
      if (test_for_exec((QString)p + "/" + filename)) {
	rc = TRUE;
	break;
      }
      p = strtok(NULL, ":");
    }

  }
  return(rc);
}


/**
 * Application main function
 */
int
main(int argc, char **argv)
{
  KAboutData aboutData( argv[0], I18N_NOOP("KDE Network Utilities"),
    KNU_VERSION, description, KAboutData::License_GPL,
    "(c) 1999-2000, The KNU developers");
  aboutData.addAuthor("Bertrand Leconte",0, "B.Leconte@mail.dotcom.fr");
  aboutData.addAuthor("Mario Weilgun",0, "mweilguni@kde.org");
  aboutData.addAuthor("Hans Petter Bieker",0, "bieker@kde.org");

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

  KApplication  *a = new KApplication;

  CaptionString = i18n("Network utilities");

  if (strcmp(a->caption(), KNU_APPNAME)) { // I don't want knu as caption
    CaptionString = a->caption();
  }

  // Session Management
  if (a->isRestored()) {
    int n = 1;
    while (KTMainWindow::canBeRestored(n)) {
      TopLevel *toplevel = new TopLevel((QWidget*)0, (const char*) KNU_APPNAME);
      CHECK_PTR(toplevel);
      toplevel->restore(n);
      n++;
    } 
  } else {
    TopLevel *toplevel = new TopLevel((QWidget*)0, (const char*) KNU_APPNAME);
    CHECK_PTR(toplevel);
    
    toplevel->show();
  }
  return a->exec();
}

