/* ** Copyright (C) 1999,2000 Toivo Pedaste ** */ /* ** 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., 51 Franklin Street, Fifth Floor, Boston, ** MA 02110-1301, USA. */ /* ** Bug reports and questions can be sent to kde-devel@kde.org */ #include "../config.h" #include #include #include #include #include #include #include #include #include #include #include #define SHPROMPT "# " const int TIMEOUT = -3; const int PASSWORD = -2; const int PROMPT = -1; extern Opts *opts; ////////////////////////////////////////////////////////////////////////////// kpKProcIO::kpKProcIO ( TQTextCodec *_codec) : KProcIO(_codec) { } kpKProcIO::~kpKProcIO() { } bool kpKProcIO::sstart (RunMode runmode) { connect (this, TQT_SIGNAL (receivedStdout (TDEProcess *, char *, int)), this, TQT_SLOT (received (TDEProcess *, char *, int))); connect (this, TQT_SIGNAL (wroteStdin(TDEProcess *)), this, TQT_SLOT (sent (TDEProcess *))); return TDEProcess::start (runmode,( TDEProcess::Communication) ( TDEProcess::Stdin | TDEProcess::Stdout)); } ////////////////////////////////////////////////////////////////////////////// kpPty::kpPty() : TQObject() { pty = new kpKProcIO(); pty->setUsePty(TDEProcess::All, false); connect(pty, TQT_SIGNAL(readReady(KProcIO *)), this, TQT_SLOT(readLines())); connect(pty, TQT_SIGNAL(processExited(TDEProcess *)), this, TQT_SLOT(done())); pty->pty()->setWinSize(0,80); tm = new TQTimer(this); connect(tm, TQT_SIGNAL(timeout()), this, TQT_SLOT(slotTimeout())); eventLoop = FALSE; inSession = FALSE; pUnterm = FALSE; loginSession = FALSE; codec = TQTextCodec::codecForLocale(); TQMap passwords; } kpPty::~kpPty() { } void kpPty::startSu() { kdDebug() << "startSu()\n"; pty->setEnvironment("PS1", SHPROMPT); #if defined(__FreeBSD__) || defined(__bsdi__) (*pty) << "su"; #else (*pty) << "su" << "-s" << "/bin/sh"; #endif } void kpPty::startSudo() { kdDebug() << "startSudo()\n"; pty->setEnvironment("PS1", SHPROMPT); (*pty) << "sudo" << "-p" << "Password: " << "/bin/sh"; } void kpPty::startSsh() { kdDebug() << "startSsh()\n"; (*pty) << "/usr/bin/ssh" << "-t" << "-l" << "root"; if (hostName.isEmpty()) { (*pty) << "-o" << "StrictHostKeyChecking=no" << "localhost"; } else { (*pty) << hostName; } (*pty) << "env PATH=/bin:/usr/bin:/sbin:/usr/sbin:/usr/local/bin PS1='" SHPROMPT "' sh"; } bool kpPty::needSession(bool needRoot) { return (!hostName.isEmpty() || needRoot); } bool kpPty::startSession(bool needRoot) { bool interact = FALSE; // Have interacted with user, prevents loops bool passwordTried = FALSE; // Have tried the current save password, so need to put up dialog pUnterm = FALSE; kdDebug() << "kpPty::startSession\n"; if (!inSession && needSession(needRoot)) { // Assume !needRoot actions are simple executables kdDebug() << "kpPty::startSession TRUE\n"; loginSession = TRUE; int ret; TQString s = "echo START=$?\n"; FULL_RESTART: interact = FALSE; retList.clear(); pty->resetAll(); TQString passMsg; kdDebug() << "privCmd=" << opts->privCmd << "\n"; if (opts->privCmd == Opts::SSHcmd || !hostName.isEmpty()) { passMsg = i18n("The action you requested uses ssh. Please enter the password or pass phrase.\n"); startSsh(); } else if (opts->privCmd == Opts::SUcmd) { passMsg = i18n("The action you requested needs root privileges. Please enter root's password.\n"); startSu(); } else if (opts->privCmd == Opts::SUDOcmd) { passMsg = i18n("The action you requested needs root privileges. Please enter your SUDO password.\n"); startSudo(); } pty->sstart(TDEProcess::NotifyOnExit); RESTART: tm->start(6*1000, TRUE); eventLoop = TRUE; kdDebug() << "Loopst\n"; kapp->enter_loop(); kdDebug() << "Loopfn Result=" << Result << "\n"; tm->stop(); if (Result == TIMEOUT) { // timeout interact = TRUE; // kdDebug() << "Line=" << retList.last() << "\n"; kpstart->addText(retList); kpstart->run("", i18n("Login Problem: Please login manually")); ret = kpstart->exec(); kdDebug() << "Sret=" << ret << "\n"; if (ret) { inSession = FALSE; } else { inSession = TRUE; } } else if (Result == PASSWORD) { // We got a password prompt TQString pass; int res; interact = TRUE; // kdDebug() << "H=" << hostName << " PH=" << passwords[hostName] << " PT=" << passwordTried <<"\n"; if (passwords[hostName] != 0 && !passwordTried) { pass = passwords[hostName]; res = 1; } else { kdDebug() << "Passwd=" << retList.last() << "\n"; TQString msg = passMsg; // kdDebug() << "privCmd=" << opts->privCmd << " host=" << hostName.isEmpty() << "\n"; if (opts->privCmd == Opts::SSHcmd || !hostName.isEmpty()) { msg += retList.last(); } int keep = 1; res = KPasswordDialog::getPassword(pass,msg,&keep); // kdDebug() << "Pass=" << pass << " Keep=" << keep << " Res=" << res << "\n"; if (keep) { passwords[hostName] = pass; } else { passwords.remove(hostName); } } pty->writeStdin(pass.append("\n"), false); passwordTried = TRUE; if (res) { retList.clear(); goto RESTART; } else { inSession = FALSE; } } else if (Result == PROMPT) { // Got Prompt inSession = TRUE; kdDebug() << "kpPty::startSession TRUE\n"; } else { // process return code pty->writeStdin(TQCString("\04"), false); // SU doesn't listen to ^C if (interact) { goto FULL_RESTART; } else { TQString errMsg = retList.join(" "); KpMsgE(errMsg, TRUE); inSession = FALSE; } } } else { kdDebug() << "kpPty::startSession Not needed\n"; } loginSession = FALSE; if (!inSession) close(); return inSession; } void kpPty::breakUpCmd(const TQString &cmd) { kdDebug() << " kpPty::run CMD=\""<< cmd <<"\" pty = " << pty << endl; bool quote = FALSE; TQString s; TQStringList cl = TQStringList::split(" ", cmd); for ( TQStringList::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; quote = FALSE; } else { s += " "; quote = TRUE; } } else if ((*it)[lastPt] == '\'') { // End of quoted string s += *it; s.replace("'",""); (*pty) << s; quote = FALSE; } else if (quote) { s += *it; s += " "; } else { (*pty) << (*it); } } } TQStringList kpPty::run(const TQString &cmd, bool inLoop, bool needRoot) { Result = 0; pUnterm = FALSE; if (!inSession && !needSession(needRoot)) { // Assume !needRoot actions are simple executables pty->resetAll(); breakUpCmd(cmd); pty->setEnvironment("TERM", "dumb"); if (!pty->sstart(TDEProcess::NotifyOnExit)) { kdDebug() << " kpPty::run execute=0\n"; return 0; } } else { if (startSession(needRoot)) { kdDebug() << "CMDroot='"<< cmd <<"'\n"; TQString s = cmd + ";echo RESULT=$?"; pty->writeStdin(s); kdDebug() << " kpPty::run session\n"; } else { kdDebug() << " kpPty::run other=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()) { TDEProcessController::theTDEProcessController->waitForProcessExit(1); } inSession = false; } void kpPty::finish(int ret) { kdDebug() << "kpPty::finish " << ret << "\n"; TQStringList::Iterator l; Result = ret; if (ret == PROMPT) { // Called program executed in session if (!retList.empty()) { l = retList.fromLast(); if ((*l).right(2) == SHPROMPT) { 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 } } } } emit result(retList,ret); if (eventLoop) { eventLoop = FALSE; kapp->exit_loop(); } } void kpPty::readLines() { bool unterm = FALSE; TQString stext; while(pty->readln(stext, false, &unterm) >= 0) { stext = codec->toUnicode(stext.ascii(), stext.length()); emit textIn(stext, !unterm); // kdDebug() << "[" << pUnterm << "-" << unterm << "-" << stext << ">\n"; if (pUnterm) { TQStringList::Iterator lst = retList.fromLast(); if (lst != retList.end()) { stext = *lst + stext; retList.remove(lst); } } int i; if (!unterm) { while (stext.endsWith("\r")) { stext.truncate(stext.length()-1); } i = stext.findRev('\r'); if (i > -1) { stext = stext.mid(i+1); } } pUnterm = unterm; retList << stext; // kdDebug() << "++" << stext << "\n"; if (stext.right(2) == SHPROMPT) { // Shell prompt emit textIn("\r \n", false); finish(PROMPT); } else if (loginSession) { TQRegExp rx( "^[^:]+:[\\s]*$"); // Password prompt if (rx.search(retList.last()) >= 0) { kdDebug() << loginSession << " " <ackRead(); } void kpPty::keyOut(char ch) { TQCString s(2); s[0] = ch; s[1] = '\0'; pty->writeStdin(s, false); } void kpPty::done() { int ret = pty->exitStatus(); TQString stext; //kdDebug() << "Done (" << ret << ")" << endl; finish(ret); } void kpPty::slotTimeout() { kdDebug() << "Timeout..............\n"; finish(TIMEOUT); } #include "kpPty.moc"