slavebase.cpp

00001 /*
00002  *
00003  *  This file is part of the KDE libraries
00004  *  Copyright (c) 2000 Waldo Bastian <bastian@kde.org>
00005  *  Copyright (c) 2000 David Faure <faure@kde.org>
00006  *  Copyright (c) 2000 Stephan Kulow <coolo@kde.org>
00007  *
00008  *  $Id: slavebase.cpp 614636 2006-12-18 14:31:59Z lunakl $
00009  *
00010  *  This library is free software; you can redistribute it and/or
00011  *  modify it under the terms of the GNU Library General Public
00012  *  License version 2 as published by the Free Software Foundation.
00013  *
00014  *  This library is distributed in the hope that it will be useful,
00015  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00016  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00017  *  Library General Public License for more details.
00018  *
00019  *  You should have received a copy of the GNU Library General Public License
00020  *  along with this library; see the file COPYING.LIB.  If not, write to
00021  *  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00022  *  Boston, MA 02110-1301, USA.
00023  *
00024  **/
00025 
00026 #include "slavebase.h"
00027 
00028 #include <config.h>
00029 
00030 #include <sys/time.h>
00031 #ifdef HAVE_SYS_SELECT_H
00032 #include <sys/select.h>     // Needed on some systems.
00033 #endif
00034 
00035 #include <assert.h>
00036 #include <kdebug.h>
00037 #include <stdlib.h>
00038 #include <errno.h>
00039 #include <unistd.h>
00040 #include <signal.h>
00041 #include <time.h>
00042 
00043 #include <qfile.h>
00044 
00045 #include <dcopclient.h>
00046 
00047 #include <kapplication.h>
00048 #include <ksock.h>
00049 #include <kcrash.h>
00050 #include <kdesu/client.h>
00051 #include <klocale.h>
00052 #include <ksocks.h>
00053 
00054 #include "kremoteencoding.h"
00055 
00056 #include "kio/slavebase.h"
00057 #include "kio/connection.h"
00058 #include "kio/ioslave_defaults.h"
00059 #include "kio/slaveinterface.h"
00060 
00061 #include "uiserver_stub.h"
00062 
00063 #ifndef NDEBUG
00064 #ifdef HAVE_BACKTRACE
00065 #include <execinfo.h>
00066 #endif
00067 #endif
00068 
00069 using namespace KIO;
00070 
00071 template class QPtrList<QValueList<UDSAtom> >;
00072 typedef QValueList<QCString> AuthKeysList;
00073 typedef QMap<QString,QCString> AuthKeysMap;
00074 #define KIO_DATA QByteArray data; QDataStream stream( data, IO_WriteOnly ); stream
00075 #define KIO_FILESIZE_T(x) (unsigned long)(x & 0xffffffff) << (unsigned long)(x >> 32)
00076 
00077 namespace KIO {
00078 
00079 class SlaveBaseConfig : public KConfigBase
00080 {
00081 public:
00082    SlaveBaseConfig(SlaveBase *_slave)
00083     : slave(_slave) { }
00084 
00085    bool internalHasGroup(const QCString &) const { qWarning("hasGroup(const QCString &)");
00086 return false; }
00087 
00088    QStringList groupList() const { return QStringList(); }
00089 
00090    QMap<QString,QString> entryMap(const QString &group) const
00091       { Q_UNUSED(group); return QMap<QString,QString>(); }
00092 
00093    void reparseConfiguration() { }
00094 
00095    KEntryMap internalEntryMap( const QString &pGroup) const { Q_UNUSED(pGroup); return KEntryMap(); }
00096 
00097    KEntryMap internalEntryMap() const { return KEntryMap(); }
00098 
00099    void putData(const KEntryKey &_key, const KEntry&_data, bool _checkGroup) 
00100    { Q_UNUSED(_key); Q_UNUSED(_data); Q_UNUSED(_checkGroup); }
00101 
00102    KEntry lookupData(const KEntryKey &_key) const
00103    {
00104      KEntry entry;
00105      QString value = slave->metaData(_key.c_key);
00106      if (!value.isNull())
00107         entry.mValue = value.utf8();
00108      return entry;
00109    }
00110 protected:
00111    SlaveBase *slave;
00112 };
00113 
00114 
00115 class SlaveBasePrivate {
00116 public:
00117     QString slaveid;
00118     bool resume:1;
00119     bool needSendCanResume:1;
00120     bool onHold:1;
00121     bool wasKilled:1;
00122     MetaData configData;
00123     SlaveBaseConfig *config;
00124     KURL onHoldUrl;
00125 
00126     struct timeval last_tv;
00127     KIO::filesize_t totalSize;
00128     KIO::filesize_t sentListEntries;
00129     DCOPClient *dcopClient;
00130     KRemoteEncoding *remotefile;
00131     time_t timeout;
00132     QByteArray timeoutData;
00133 };
00134 
00135 }
00136 
00137 static SlaveBase *globalSlave;
00138 long SlaveBase::s_seqNr;
00139 
00140 static volatile bool slaveWriteError = false;
00141 
00142 static const char *s_protocol;
00143 
00144 #ifdef Q_OS_UNIX
00145 static void genericsig_handler(int sigNumber)
00146 {
00147    signal(sigNumber,SIG_IGN);
00148    //WABA: Don't do anything that requires malloc, we can deadlock on it since
00149    //a SIGTERM signal can come in while we are in malloc/free.
00150    //kdDebug()<<"kioslave : exiting due to signal "<<sigNumber<<endl;
00151    //set the flag which will be checked in dispatchLoop() and which *should* be checked
00152    //in lengthy operations in the various slaves
00153    if (globalSlave!=0)
00154       globalSlave->setKillFlag();
00155    signal(SIGALRM,SIG_DFL);
00156    alarm(5);  //generate an alarm signal in 5 seconds, in this time the slave has to exit
00157 }
00158 #endif
00159 
00161 
00162 SlaveBase::SlaveBase( const QCString &protocol,
00163                       const QCString &pool_socket,
00164                       const QCString &app_socket )
00165     : mProtocol(protocol), m_pConnection(0),
00166       mPoolSocket( QFile::decodeName(pool_socket)),
00167       mAppSocket( QFile::decodeName(app_socket))
00168 {
00169     s_protocol = protocol.data();
00170 #ifdef Q_OS_UNIX
00171     if (!getenv("KDE_DEBUG"))
00172     {
00173         KCrash::setCrashHandler( sigsegv_handler );
00174         signal(SIGILL,&sigsegv_handler);
00175         signal(SIGTRAP,&sigsegv_handler);
00176         signal(SIGABRT,&sigsegv_handler);
00177         signal(SIGBUS,&sigsegv_handler);
00178         signal(SIGALRM,&sigsegv_handler);
00179         signal(SIGFPE,&sigsegv_handler);
00180 #ifdef SIGPOLL
00181         signal(SIGPOLL, &sigsegv_handler);
00182 #endif
00183 #ifdef SIGSYS
00184         signal(SIGSYS, &sigsegv_handler);
00185 #endif
00186 #ifdef SIGVTALRM
00187         signal(SIGVTALRM, &sigsegv_handler);
00188 #endif
00189 #ifdef SIGXCPU
00190         signal(SIGXCPU, &sigsegv_handler);
00191 #endif
00192 #ifdef SIGXFSZ
00193         signal(SIGXFSZ, &sigsegv_handler);
00194 #endif
00195     }
00196 
00197     struct sigaction act;
00198     act.sa_handler = sigpipe_handler;
00199     sigemptyset( &act.sa_mask );
00200     act.sa_flags = 0;
00201     sigaction( SIGPIPE, &act, 0 );
00202 
00203     signal(SIGINT,&genericsig_handler);
00204     signal(SIGQUIT,&genericsig_handler);
00205     signal(SIGTERM,&genericsig_handler);
00206 #endif
00207 
00208     globalSlave=this;
00209 
00210     appconn = new Connection();
00211     listEntryCurrentSize = 100;
00212     struct timeval tp;
00213     gettimeofday(&tp, 0);
00214     listEntry_sec = tp.tv_sec;
00215     listEntry_usec = tp.tv_usec;
00216     mConnectedToApp = true;
00217 
00218     d = new SlaveBasePrivate;
00219     // by kahl for netmgr (need a way to identify slaves)
00220     d->slaveid = protocol;
00221     d->slaveid += QString::number(getpid());
00222     d->resume = false;
00223     d->needSendCanResume = false;
00224     d->config = new SlaveBaseConfig(this);
00225     d->onHold = false;
00226     d->wasKilled=false;
00227     d->last_tv.tv_sec = 0;
00228     d->last_tv.tv_usec = 0;
00229 //    d->processed_size = 0;
00230     d->totalSize=0;
00231     d->sentListEntries=0;
00232     d->timeout = 0;
00233     connectSlave(mAppSocket);
00234 
00235     d->dcopClient = 0;
00236     d->remotefile = 0;
00237 }
00238 
00239 SlaveBase::~SlaveBase()
00240 {
00241     delete d;
00242     s_protocol = "";
00243 }
00244 
00245 DCOPClient *SlaveBase::dcopClient()
00246 {
00247     if (!d->dcopClient)
00248     {
00249        d->dcopClient = KApplication::dcopClient();
00250        if (!d->dcopClient->isAttached())
00251           d->dcopClient->attach();
00252     }
00253     return d->dcopClient;
00254 }
00255 
00256 void SlaveBase::dispatchLoop()
00257 {
00258 #ifdef Q_OS_UNIX //TODO: WIN32
00259     fd_set rfds;
00260     int retval;
00261 
00262     while (true)
00263     {
00264        if (d->timeout && (d->timeout < time(0)))
00265        {
00266           QByteArray data = d->timeoutData;
00267           d->timeout = 0;
00268           d->timeoutData = QByteArray();
00269           special(data);
00270        }
00271        FD_ZERO(&rfds);
00272 
00273        assert(appconn->inited());
00274        FD_SET(appconn->fd_from(), &rfds);
00275 
00276        if (!d->timeout) // we can wait forever
00277        {
00278           retval = select(appconn->fd_from()+ 1, &rfds, NULL, NULL, NULL);
00279        }
00280        else
00281        {
00282           struct timeval tv;
00283           tv.tv_sec = kMax(d->timeout-time(0),(time_t) 1);
00284           tv.tv_usec = 0;
00285           retval = select(appconn->fd_from()+ 1, &rfds, NULL, NULL, &tv);
00286        }
00287        if ((retval>0) && FD_ISSET(appconn->fd_from(), &rfds))
00288        { // dispatch application messages
00289           int cmd;
00290           QByteArray data;
00291           if ( appconn->read(&cmd, data) != -1 )
00292           {
00293              dispatch(cmd, data);
00294           }
00295           else // some error occurred, perhaps no more application
00296           {
00297              // When the app exits, should the slave be put back in the pool ?
00298              if (mConnectedToApp && !mPoolSocket.isEmpty())
00299              {
00300                 disconnectSlave();
00301                 mConnectedToApp = false;
00302                 closeConnection();
00303                 connectSlave(mPoolSocket);
00304              }
00305              else
00306              {
00307                 return;
00308              }
00309           }
00310        }
00311        else if ((retval<0) && (errno != EINTR))
00312        {
00313           kdDebug(7019) << "dispatchLoop(): select returned " << retval << " "
00314             << (errno==EBADF?"EBADF":errno==EINTR?"EINTR":errno==EINVAL?"EINVAL":errno==ENOMEM?"ENOMEM":"unknown")
00315             << " (" << errno << ")" << endl;
00316           return;
00317        }
00318        //I think we get here when we were killed in dispatch() and not in select()
00319        if (wasKilled())
00320        {
00321           kdDebug(7019)<<" dispatchLoop() slave was killed, returning"<<endl;
00322           return;
00323        }
00324     }
00325 #endif
00326 }
00327 
00328 void SlaveBase::connectSlave(const QString& path)
00329 {
00330 #ifdef Q_OS_UNIX //TODO: KSocket not yet available on WIN32
00331     appconn->init(new KSocket(QFile::encodeName(path)));
00332     if (!appconn->inited())
00333     {
00334         kdDebug(7019) << "SlaveBase: failed to connect to " << path << endl;
00335         exit();
00336     }
00337 
00338     setConnection(appconn);
00339 #endif
00340 }
00341 
00342 void SlaveBase::disconnectSlave()
00343 {
00344     appconn->close();
00345 }
00346 
00347 void SlaveBase::setMetaData(const QString &key, const QString &value)
00348 {
00349    mOutgoingMetaData.replace(key, value);
00350 }
00351 
00352 QString SlaveBase::metaData(const QString &key) const
00353 {
00354    if (mIncomingMetaData.contains(key))
00355       return mIncomingMetaData[key];
00356    if (d->configData.contains(key))
00357       return d->configData[key];
00358    return QString::null;
00359 }
00360 
00361 bool SlaveBase::hasMetaData(const QString &key) const
00362 {
00363    if (mIncomingMetaData.contains(key))
00364       return true;
00365    if (d->configData.contains(key))
00366       return true;
00367    return false;
00368 }
00369 
00370 // ### remove the next two methods for KDE4 (they miss the const)
00371 QString SlaveBase::metaData(const QString &key) {
00372    return const_cast<const SlaveBase*>(this)->metaData( key );
00373 }
00374 bool SlaveBase::hasMetaData(const QString &key) {
00375    return const_cast<const SlaveBase*>(this)->hasMetaData( key );
00376 }
00377 
00378 KConfigBase *SlaveBase::config()
00379 {
00380    return d->config;
00381 }
00382 
00383 void SlaveBase::sendMetaData()
00384 {
00385    KIO_DATA << mOutgoingMetaData;
00386 
00387    slaveWriteError = false;
00388    m_pConnection->send( INF_META_DATA, data );
00389    if (slaveWriteError) exit();
00390    mOutgoingMetaData.clear(); // Clear
00391 }
00392 
00393 KRemoteEncoding *SlaveBase::remoteEncoding()
00394 {
00395    if (d->remotefile != 0)
00396       return d->remotefile;
00397 
00398    return d->remotefile = new KRemoteEncoding(metaData("Charset").latin1());
00399 }
00400 
00401 void SlaveBase::data( const QByteArray &data )
00402 {
00403    if (!mOutgoingMetaData.isEmpty())
00404       sendMetaData();
00405    slaveWriteError = false;
00406    m_pConnection->send( MSG_DATA, data );
00407    if (slaveWriteError) exit();
00408 }
00409 
00410 void SlaveBase::dataReq( )
00411 {
00412 /*
00413    if (!mOutgoingMetaData.isEmpty())
00414       sendMetaData();
00415 */
00416    if (d->needSendCanResume)
00417       canResume(0);
00418    m_pConnection->send( MSG_DATA_REQ );
00419 }
00420 
00421 void SlaveBase::error( int _errid, const QString &_text )
00422 {
00423     mIncomingMetaData.clear(); // Clear meta data
00424     mOutgoingMetaData.clear();
00425     KIO_DATA << (Q_INT32) _errid << _text;
00426 
00427     m_pConnection->send( MSG_ERROR, data );
00428     //reset
00429     listEntryCurrentSize = 100;
00430     d->sentListEntries=0;
00431     d->totalSize=0;
00432 }
00433 
00434 void SlaveBase::connected()
00435 {
00436     slaveWriteError = false;
00437     m_pConnection->send( MSG_CONNECTED );
00438     if (slaveWriteError) exit();
00439 }
00440 
00441 void SlaveBase::finished()
00442 {
00443     mIncomingMetaData.clear(); // Clear meta data
00444     if (!mOutgoingMetaData.isEmpty())
00445        sendMetaData();
00446     m_pConnection->send( MSG_FINISHED );
00447 
00448     // reset
00449     listEntryCurrentSize = 100;
00450     d->sentListEntries=0;
00451     d->totalSize=0;
00452 }
00453 
00454 void SlaveBase::needSubURLData()
00455 {
00456     m_pConnection->send( MSG_NEED_SUBURL_DATA );
00457 }
00458 
00459 void SlaveBase::slaveStatus( const QString &host, bool connected )
00460 {
00461     pid_t pid = getpid();
00462     Q_INT8 b = connected ? 1 : 0;
00463     KIO_DATA << pid << mProtocol << host << b;
00464     if (d->onHold)
00465        stream << d->onHoldUrl;
00466     m_pConnection->send( MSG_SLAVE_STATUS, data );
00467 }
00468 
00469 void SlaveBase::canResume()
00470 {
00471     m_pConnection->send( MSG_CANRESUME );
00472 }
00473 
00474 void SlaveBase::totalSize( KIO::filesize_t _bytes )
00475 {
00476     KIO_DATA << KIO_FILESIZE_T(_bytes);
00477     slaveWriteError = false;
00478     m_pConnection->send( INF_TOTAL_SIZE, data );
00479     if (slaveWriteError) exit();
00480 
00481     //this one is usually called before the first item is listed in listDir()
00482     struct timeval tp;
00483     gettimeofday(&tp, 0);
00484     listEntry_sec = tp.tv_sec;
00485     listEntry_usec = tp.tv_usec;
00486     d->totalSize=_bytes;
00487     d->sentListEntries=0;
00488 }
00489 
00490 void SlaveBase::processedSize( KIO::filesize_t _bytes )
00491 {
00492     bool           emitSignal=false;
00493     struct timeval tv;
00494     int            gettimeofday_res=gettimeofday( &tv, 0L );
00495 
00496     if( _bytes == d->totalSize )
00497         emitSignal=true;
00498     else if ( gettimeofday_res == 0 ) {
00499         time_t msecdiff = 2000;
00500         if (d->last_tv.tv_sec) {
00501             // Compute difference, in ms
00502             msecdiff = 1000 * ( tv.tv_sec - d->last_tv.tv_sec );
00503             time_t usecdiff = tv.tv_usec - d->last_tv.tv_usec;
00504             if ( usecdiff < 0 ) {
00505                 msecdiff--;
00506                 msecdiff += 1000;
00507             }
00508             msecdiff += usecdiff / 1000;
00509         }
00510         emitSignal=msecdiff >= 100; // emit size 10 times a second
00511     }
00512 
00513     if( emitSignal ) {
00514         KIO_DATA << KIO_FILESIZE_T(_bytes);
00515         slaveWriteError = false;
00516         m_pConnection->send( INF_PROCESSED_SIZE, data );
00517             if (slaveWriteError) exit();
00518         if ( gettimeofday_res == 0 ) {
00519             d->last_tv.tv_sec = tv.tv_sec;
00520             d->last_tv.tv_usec = tv.tv_usec;
00521         }
00522     }
00523 //    d->processed_size = _bytes;
00524 }
00525 
00526 void SlaveBase::processedPercent( float /* percent */ )
00527 {
00528   kdDebug(7019) << "SlaveBase::processedPercent: STUB" << endl;
00529 }
00530 
00531 
00532 void SlaveBase::speed( unsigned long _bytes_per_second )
00533 {
00534     KIO_DATA << (Q_UINT32) _bytes_per_second;
00535     slaveWriteError = false;
00536     m_pConnection->send( INF_SPEED, data );
00537     if (slaveWriteError) exit();
00538 }
00539 
00540 void SlaveBase::redirection( const KURL& _url )
00541 {
00542     KIO_DATA << _url;
00543     m_pConnection->send( INF_REDIRECTION, data );
00544 }
00545 
00546 void SlaveBase::errorPage()
00547 {
00548     m_pConnection->send( INF_ERROR_PAGE );
00549 }
00550 
00551 static bool isSubCommand(int cmd)
00552 {
00553    return ( (cmd == CMD_REPARSECONFIGURATION) ||
00554             (cmd == CMD_META_DATA) ||
00555             (cmd == CMD_CONFIG) ||
00556             (cmd == CMD_SUBURL) ||
00557             (cmd == CMD_SLAVE_STATUS) ||
00558             (cmd == CMD_SLAVE_CONNECT) ||
00559             (cmd == CMD_SLAVE_HOLD) ||
00560             (cmd == CMD_MULTI_GET));
00561 }
00562 
00563 void SlaveBase::mimeType( const QString &_type)
00564 {
00565   // kdDebug(7019) << "(" << getpid() << ") SlaveBase::mimeType '" << _type << "'" << endl;
00566   int cmd;
00567   do
00568   {
00569     // Send the meta-data each time we send the mime-type.
00570     if (!mOutgoingMetaData.isEmpty())
00571     {
00572       // kdDebug(7019) << "(" << getpid() << ") mimeType: emitting meta data" << endl;
00573       KIO_DATA << mOutgoingMetaData;
00574       m_pConnection->send( INF_META_DATA, data );
00575     }
00576     KIO_DATA << _type;
00577     m_pConnection->send( INF_MIME_TYPE, data );
00578     while(true)
00579     {
00580        cmd = 0;
00581        if ( m_pConnection->read( &cmd, data ) == -1 ) {
00582            kdDebug(7019) << "SlaveBase: mimetype: read error" << endl;
00583            exit();
00584        }
00585        // kdDebug(7019) << "(" << getpid() << ") Slavebase: mimetype got " << cmd << endl;
00586        if ( cmd == CMD_HOST) // Ignore.
00587           continue;
00588        if ( isSubCommand(cmd) )
00589        {
00590           dispatch( cmd, data );
00591           continue; // Disguised goto
00592        }
00593        break;
00594     }
00595   }
00596   while (cmd != CMD_NONE);
00597   mOutgoingMetaData.clear();
00598 }
00599 
00600 void SlaveBase::exit()
00601 {
00602     this->~SlaveBase();
00603     ::exit(255);
00604 }
00605 
00606 void SlaveBase::warning( const QString &_msg)
00607 {
00608     KIO_DATA << _msg;
00609     m_pConnection->send( INF_WARNING, data );
00610 }
00611 
00612 void SlaveBase::infoMessage( const QString &_msg)
00613 {
00614     KIO_DATA << _msg;
00615     m_pConnection->send( INF_INFOMESSAGE, data );
00616 }
00617 
00618 bool SlaveBase::requestNetwork(const QString& host)
00619 {
00620     KIO_DATA << host << d->slaveid;
00621     m_pConnection->send( MSG_NET_REQUEST, data );
00622 
00623     if ( waitForAnswer( INF_NETWORK_STATUS, 0, data ) != -1 )
00624     {
00625         bool status;
00626         QDataStream stream( data, IO_ReadOnly );
00627         stream >> status;
00628         return status;
00629     } else
00630         return false;
00631 }
00632 
00633 void SlaveBase::dropNetwork(const QString& host)
00634 {
00635     KIO_DATA << host << d->slaveid;
00636     m_pConnection->send( MSG_NET_DROP, data );
00637 }
00638 
00639 void SlaveBase::statEntry( const UDSEntry& entry )
00640 {
00641     KIO_DATA << entry;
00642     slaveWriteError = false;
00643     m_pConnection->send( MSG_STAT_ENTRY, data );
00644     if (slaveWriteError) exit();
00645 }
00646 
00647 void SlaveBase::listEntry( const UDSEntry& entry, bool _ready )
00648 {
00649    static struct timeval tp;
00650    static const int maximum_updatetime = 300;
00651    static const int minimum_updatetime = 100;
00652 
00653    if (!_ready) {
00654       pendingListEntries.append(entry);
00655 
00656       if (pendingListEntries.count() > listEntryCurrentSize) {
00657          gettimeofday(&tp, 0);
00658 
00659          long diff = ((tp.tv_sec - listEntry_sec) * 1000000 +
00660                       tp.tv_usec - listEntry_usec) / 1000;
00661          if (diff==0) diff=1;
00662 
00663          if (diff > maximum_updatetime) {
00664             listEntryCurrentSize = listEntryCurrentSize * 3 / 4;
00665             _ready = true;
00666          }
00667 //if we can send all list entries of this dir which have not yet been sent
00668 //within maximum_updatetime, then make listEntryCurrentSize big enough for all of them
00669          else if (((pendingListEntries.count()*maximum_updatetime)/diff) > (d->totalSize-d->sentListEntries))
00670             listEntryCurrentSize=d->totalSize-d->sentListEntries+1;
00671 //if we are below minimum_updatetime, estimate how much we will get within
00672 //maximum_updatetime
00673          else if (diff < minimum_updatetime)
00674             listEntryCurrentSize = (pendingListEntries.count() * maximum_updatetime) / diff;
00675          else
00676             _ready=true;
00677       }
00678    }
00679    if (_ready) { // may happen when we started with !ready
00680       listEntries( pendingListEntries );
00681       pendingListEntries.clear();
00682 
00683       gettimeofday(&tp, 0);
00684       listEntry_sec = tp.tv_sec;
00685       listEntry_usec = tp.tv_usec;
00686    }
00687 }
00688 
00689 void SlaveBase::listEntries( const UDSEntryList& list )
00690 {
00691     KIO_DATA << (Q_UINT32)list.count();
00692     UDSEntryListConstIterator it = list.begin();
00693     UDSEntryListConstIterator end = list.end();
00694     for (; it != end; ++it)
00695       stream << *it;
00696     slaveWriteError = false;
00697     m_pConnection->send( MSG_LIST_ENTRIES, data);
00698     if (slaveWriteError) exit();
00699     d->sentListEntries+=(uint)list.count();
00700 }
00701 
00702 void SlaveBase::sendAuthenticationKey( const QCString& key,
00703                                        const QCString& group,
00704                                        bool keepPass )
00705 {
00706     KIO_DATA << key << group << keepPass;
00707     m_pConnection->send( MSG_AUTH_KEY, data );
00708 }
00709 
00710 void SlaveBase::delCachedAuthentication( const QString& key )
00711 {
00712     KIO_DATA << key.utf8() ;
00713     m_pConnection->send( MSG_DEL_AUTH_KEY, data );
00714 }
00715 
00716 void SlaveBase::sigsegv_handler(int sig)
00717 {
00718 #ifdef Q_OS_UNIX
00719     signal(sig,SIG_DFL); // Next one kills
00720 
00721     //Kill us if we deadlock
00722     signal(SIGALRM,SIG_DFL);
00723     alarm(5);  //generate an alarm signal in 5 seconds, in this time the slave has to exit
00724 
00725     // Debug and printf should be avoided because they might
00726     // call malloc.. and get in a nice recursive malloc loop
00727     char buffer[120];
00728     snprintf(buffer, sizeof(buffer), "kioslave: ####### CRASH ###### protocol = %s pid = %d signal = %d\n", s_protocol, getpid(), sig);
00729     write(2, buffer, strlen(buffer));
00730 #ifndef NDEBUG
00731 #ifdef HAVE_BACKTRACE
00732     void* trace[256];
00733     int n = backtrace(trace, 256);
00734     if (n)
00735       backtrace_symbols_fd(trace, n, 2);
00736 #endif
00737 #endif
00738     ::exit(1);
00739 #endif
00740 }
00741 
00742 void SlaveBase::sigpipe_handler (int)
00743 {
00744     // We ignore a SIGPIPE in slaves.
00745     // A SIGPIPE can happen in two cases:
00746     // 1) Communication error with application.
00747     // 2) Communication error with network.
00748     slaveWriteError = true;
00749 
00750     // Don't add anything else here, especially no debug output
00751 }
00752 
00753 void SlaveBase::setHost(QString const &, int, QString const &, QString const &)
00754 {
00755 }
00756 
00757 void SlaveBase::openConnection(void)
00758 { error(  ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_CONNECT)); }
00759 void SlaveBase::closeConnection(void)
00760 { } // No response!
00761 void SlaveBase::stat(KURL const &)
00762 { error(  ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_STAT)); }
00763 void SlaveBase::put(KURL const &, int, bool, bool)
00764 { error(  ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_PUT)); }
00765 void SlaveBase::special(const QByteArray &)
00766 { error(  ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_SPECIAL)); }
00767 void SlaveBase::listDir(KURL const &)
00768 { error(  ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_LISTDIR)); }
00769 void SlaveBase::get(KURL const & )
00770 { error(  ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_GET)); }
00771 void SlaveBase::mimetype(KURL const &url)
00772 { get(url); }
00773 void SlaveBase::rename(KURL const &, KURL const &, bool)
00774 { error(  ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_RENAME)); }
00775 void SlaveBase::symlink(QString const &, KURL const &, bool)
00776 { error(  ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_SYMLINK)); }
00777 void SlaveBase::copy(KURL const &, KURL const &, int, bool)
00778 { error(  ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_COPY)); }
00779 void SlaveBase::del(KURL const &, bool)
00780 { error(  ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_DEL)); }
00781 void SlaveBase::mkdir(KURL const &, int)
00782 { error(  ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_MKDIR)); }
00783 void SlaveBase::chmod(KURL const &, int)
00784 { error(  ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_CHMOD)); }
00785 void SlaveBase::setSubURL(KURL const &)
00786 { error(  ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_SUBURL)); }
00787 void SlaveBase::multiGet(const QByteArray &)
00788 { error(  ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_MULTI_GET)); }
00789 
00790 
00791 void SlaveBase::slave_status()
00792 { slaveStatus( QString::null, false ); }
00793 
00794 void SlaveBase::reparseConfiguration()
00795 {
00796 }
00797 
00798 bool SlaveBase::dispatch()
00799 {
00800     assert( m_pConnection );
00801 
00802     int cmd;
00803     QByteArray data;
00804     if ( m_pConnection->read( &cmd, data ) == -1 )
00805     {
00806         kdDebug(7019) << "SlaveBase::dispatch() has read error." << endl;
00807         return false;
00808     }
00809 
00810     dispatch( cmd, data );
00811     return true;
00812 }
00813 
00814 bool SlaveBase::openPassDlg( AuthInfo& info )
00815 {
00816     return openPassDlg(info, QString::null);
00817 }
00818 
00819 bool SlaveBase::openPassDlg( AuthInfo& info, const QString &errorMsg )
00820 {
00821     QCString replyType;
00822     QByteArray params;
00823     QByteArray reply;
00824     AuthInfo authResult;
00825     long windowId = metaData("window-id").toLong();
00826     long progressId = metaData("progress-id").toLong();
00827     unsigned long userTimestamp = metaData("user-timestamp").toULong();
00828 
00829     kdDebug(7019) << "SlaveBase::openPassDlg window-id=" << windowId << " progress-id=" << progressId << endl;
00830 
00831     (void) dcopClient(); // Make sure to have a dcop client.
00832 
00833     UIServer_stub uiserver( "kio_uiserver", "UIServer" );
00834     if (progressId)
00835       uiserver.setJobVisible( progressId, false );
00836 
00837     QDataStream stream(params, IO_WriteOnly);
00838 
00839     if (metaData("no-auth-prompt").lower() == "true")
00840        stream << info << QString("<NoAuthPrompt>") << windowId << s_seqNr << userTimestamp;
00841     else
00842        stream << info << errorMsg << windowId << s_seqNr << userTimestamp;
00843 
00844     bool callOK = d->dcopClient->call( "kded", "kpasswdserver", "queryAuthInfo(KIO::AuthInfo, QString, long int, long int, unsigned long int)",
00845                                         params, replyType, reply );
00846 
00847     if (progressId)
00848       uiserver.setJobVisible( progressId, true );
00849 
00850     if (!callOK)
00851     {
00852        kdWarning(7019) << "Can't communicate with kded_kpasswdserver!" << endl;
00853        return false;
00854     }
00855 
00856     if ( replyType == "KIO::AuthInfo" )
00857     {
00858        QDataStream stream2( reply, IO_ReadOnly );
00859        stream2 >> authResult >> s_seqNr;
00860     }
00861     else
00862     {
00863        kdError(7019) << "DCOP function queryAuthInfo(...) returns "
00864                      << replyType << ", expected KIO::AuthInfo" << endl;
00865        return false;
00866     }
00867 
00868     if (!authResult.isModified())
00869        return false;
00870 
00871     info = authResult;
00872 
00873     kdDebug(7019) << "SlaveBase::openPassDlg: username=" << info.username << endl;
00874     kdDebug(7019) << "SlaveBase::openPassDlg: password=[hidden]" << endl;
00875 
00876     return true;
00877 }
00878 
00879 int SlaveBase::messageBox( MessageBoxType type, const QString &text, const QString &caption,
00880                            const QString &buttonYes, const QString &buttonNo )
00881 {
00882     return messageBox( text, type, caption, buttonYes, buttonNo, QString::null );
00883 }
00884 
00885 int SlaveBase::messageBox( const QString &text, MessageBoxType type, const QString &caption,
00886                            const QString &buttonYes, const QString &buttonNo, const QString &dontAskAgainName )
00887 {
00888     kdDebug(7019) << "messageBox " << type << " " << text << " - " << caption << buttonYes << buttonNo << endl;
00889     KIO_DATA << (Q_INT32)type << text << caption << buttonYes << buttonNo << dontAskAgainName;
00890     m_pConnection->send( INF_MESSAGEBOX, data );
00891     if ( waitForAnswer( CMD_MESSAGEBOXANSWER, 0, data ) != -1 )
00892     {
00893         QDataStream stream( data, IO_ReadOnly );
00894         int answer;
00895         stream >> answer;
00896         kdDebug(7019) << "got messagebox answer" << answer << endl;
00897         return answer;
00898     } else
00899         return 0; // communication failure
00900 }
00901 
00902 bool SlaveBase::canResume( KIO::filesize_t offset )
00903 {
00904     kdDebug(7019) << "SlaveBase::canResume offset=" << KIO::number(offset) << endl;
00905     d->needSendCanResume = false;
00906     KIO_DATA << KIO_FILESIZE_T(offset);
00907     m_pConnection->send( MSG_RESUME, data );
00908     if ( offset )
00909     {
00910         int cmd;
00911         if ( waitForAnswer( CMD_RESUMEANSWER, CMD_NONE, data, &cmd ) != -1 )
00912         {
00913             kdDebug(7019) << "SlaveBase::canResume returning " << (cmd == CMD_RESUMEANSWER) << endl;
00914             return cmd == CMD_RESUMEANSWER;
00915         } else
00916             return false;
00917     }
00918     else // No resuming possible -> no answer to wait for
00919         return true;
00920 }
00921 
00922 
00923 
00924 int SlaveBase::waitForAnswer( int expected1, int expected2, QByteArray & data, int *pCmd )
00925 {
00926     int cmd, result;
00927     for (;;)
00928     {
00929         result = m_pConnection->read( &cmd, data );
00930         if ( result == -1 )
00931         {
00932             kdDebug(7019) << "SlaveBase::waitForAnswer has read error." << endl;
00933             return -1;
00934         }
00935         if ( cmd == expected1 || cmd == expected2 )
00936         {
00937             if ( pCmd ) *pCmd = cmd;
00938             return result;
00939         }
00940         if ( isSubCommand(cmd) )
00941         {
00942             dispatch( cmd, data );
00943         }
00944         else
00945         {
00946             kdWarning() << "Got cmd " << cmd << " while waiting for an answer!" << endl;
00947         }
00948     }
00949 }
00950 
00951 
00952 int SlaveBase::readData( QByteArray &buffer)
00953 {
00954    int result = waitForAnswer( MSG_DATA, 0, buffer );
00955    //kdDebug(7019) << "readData: length = " << result << " " << endl;
00956    return result;
00957 }
00958 
00959 void SlaveBase::setTimeoutSpecialCommand(int timeout, const QByteArray &data)
00960 {
00961    if (timeout > 0)
00962       d->timeout = time(0)+(time_t)timeout;
00963    else if (timeout == 0)
00964       d->timeout = 1; // Immediate timeout
00965    else
00966       d->timeout = 0; // Canceled
00967 
00968    d->timeoutData = data;
00969 }
00970 
00971 void SlaveBase::dispatch( int command, const QByteArray &data )
00972 {
00973     QDataStream stream( data, IO_ReadOnly );
00974 
00975     KURL url;
00976     int i;
00977 
00978     switch( command ) {
00979     case CMD_HOST: {
00980         // Reset s_seqNr, see kpasswdserver/DESIGN
00981         s_seqNr = 0;
00982         QString passwd;
00983         QString host, user;
00984         stream >> host >> i >> user >> passwd;
00985         setHost( host, i, user, passwd );
00986     }
00987     break;
00988     case CMD_CONNECT:
00989         openConnection( );
00990         break;
00991     case CMD_DISCONNECT:
00992         closeConnection( );
00993         break;
00994     case CMD_SLAVE_STATUS:
00995         slave_status();
00996         break;
00997     case CMD_SLAVE_CONNECT:
00998     {
00999         d->onHold = false;
01000         QString app_socket;
01001         QDataStream stream( data, IO_ReadOnly);
01002         stream >> app_socket;
01003         appconn->send( MSG_SLAVE_ACK );
01004         disconnectSlave();
01005         mConnectedToApp = true;
01006         connectSlave(app_socket);
01007     } break;
01008     case CMD_SLAVE_HOLD:
01009     {
01010         KURL url;
01011         QDataStream stream( data, IO_ReadOnly);
01012         stream >> url;
01013         d->onHoldUrl = url;
01014         d->onHold = true;
01015         disconnectSlave();
01016         mConnectedToApp = false;
01017         // Do not close connection!
01018         connectSlave(mPoolSocket);
01019     } break;
01020     case CMD_REPARSECONFIGURATION:
01021         reparseConfiguration();
01022         break;
01023     case CMD_CONFIG:
01024         stream >> d->configData;
01025 #ifdef Q_OS_UNIX //TODO: not yet available on WIN32
01026         KSocks::setConfig(d->config);
01027 #endif
01028     delete d->remotefile;
01029     d->remotefile = 0;
01030         break;
01031     case CMD_GET:
01032     {
01033         stream >> url;
01034         get( url );
01035     } break;
01036     case CMD_PUT:
01037     {
01038         int permissions;
01039         Q_INT8 iOverwrite, iResume;
01040         stream >> url >> iOverwrite >> iResume >> permissions;
01041         bool overwrite = ( iOverwrite != 0 );
01042         bool resume = ( iResume != 0 );
01043 
01044         // Remember that we need to send canResume(), TransferJob is expecting
01045         // it. Well, in theory this shouldn't be done if resume is true.
01046         //   (the resume bool is currently unused)
01047         d->needSendCanResume = true   /* !resume */;
01048 
01049         put( url, permissions, overwrite, resume);
01050     } break;
01051     case CMD_STAT:
01052         stream >> url;
01053         stat( url );
01054         break;
01055     case CMD_MIMETYPE:
01056         stream >> url;
01057         mimetype( url );
01058         break;
01059     case CMD_LISTDIR:
01060         stream >> url;
01061         listDir( url );
01062         break;
01063     case CMD_MKDIR:
01064         stream >> url >> i;
01065         mkdir( url, i );
01066         break;
01067     case CMD_RENAME:
01068     {
01069         Q_INT8 iOverwrite;
01070         KURL url2;
01071         stream >> url >> url2 >> iOverwrite;
01072         bool overwrite = (iOverwrite != 0);
01073         rename( url, url2, overwrite );
01074     } break;
01075     case CMD_SYMLINK:
01076     {
01077         Q_INT8 iOverwrite;
01078         QString target;
01079         stream >> target >> url >> iOverwrite;
01080         bool overwrite = (iOverwrite != 0);
01081         symlink( target, url, overwrite );
01082     } break;
01083     case CMD_COPY:
01084     {
01085         int permissions;
01086         Q_INT8 iOverwrite;
01087         KURL url2;
01088         stream >> url >> url2 >> permissions >> iOverwrite;
01089         bool overwrite = (iOverwrite != 0);
01090         copy( url, url2, permissions, overwrite );
01091     } break;
01092     case CMD_DEL:
01093     {
01094         Q_INT8 isFile;
01095         stream >> url >> isFile;
01096         del( url, isFile != 0);
01097     } break;
01098     case CMD_CHMOD:
01099         stream >> url >> i;
01100         chmod( url, i);
01101         break;
01102     case CMD_SPECIAL:
01103         special( data );
01104         break;
01105     case CMD_META_DATA:
01106         //kdDebug(7019) << "(" << getpid() << ") Incoming meta-data..." << endl;
01107         stream >> mIncomingMetaData;
01108         break;
01109     case CMD_SUBURL:
01110         stream >> url;
01111         setSubURL(url);
01112         break;
01113     case CMD_NONE:
01114         fprintf(stderr, "Got unexpected CMD_NONE!\n");
01115         break;
01116     case CMD_MULTI_GET:
01117         multiGet( data );
01118         break;
01119     default:
01120         // Some command we don't understand.
01121         // Just ignore it, it may come from some future version of KDE.
01122         break;
01123     }
01124 }
01125 
01126 QString SlaveBase::createAuthCacheKey( const KURL& url )
01127 {
01128     if( !url.isValid() )
01129         return QString::null;
01130 
01131     // Generate the basic key sequence.
01132     QString key = url.protocol();
01133     key += '-';
01134     key += url.host();
01135     int port = url.port();
01136     if( port )
01137     {
01138       key += ':';
01139       key += QString::number(port);
01140     }
01141 
01142     return key;
01143 }
01144 
01145 bool SlaveBase::pingCacheDaemon() const
01146 {
01147 #ifdef Q_OS_UNIX
01148     // TODO: Ping kded / kpasswdserver
01149     KDEsuClient client;
01150     int success = client.ping();
01151     if( success == -1 )
01152     {
01153         success = client.startServer();
01154         if( success == -1 )
01155         {
01156             kdDebug(7019) << "Cannot start a new deamon!!" << endl;
01157             return false;
01158         }
01159         kdDebug(7019) << "Sucessfully started new cache deamon!!" << endl;
01160     }
01161     return true;
01162 #else
01163     return false;
01164 #endif
01165 }
01166 
01167 bool SlaveBase::checkCachedAuthentication( AuthInfo& info )
01168 {
01169     QCString replyType;
01170     QByteArray params;
01171     QByteArray reply;
01172     AuthInfo authResult;
01173     long windowId = metaData("window-id").toLong();
01174     unsigned long userTimestamp = metaData("user-timestamp").toULong();
01175 
01176     kdDebug(7019) << "SlaveBase::checkCachedAuthInfo window = " << windowId << " url = " << info.url.url() << endl;
01177 
01178     (void) dcopClient(); // Make sure to have a dcop client.
01179 
01180     QDataStream stream(params, IO_WriteOnly);
01181     stream << info << windowId << userTimestamp;
01182 
01183     if ( !d->dcopClient->call( "kded", "kpasswdserver", "checkAuthInfo(KIO::AuthInfo, long int, unsigned long int)",
01184                                params, replyType, reply ) )
01185     {
01186        kdWarning(7019) << "Can't communicate with kded_kpasswdserver!" << endl;
01187        return false;
01188     }
01189 
01190     if ( replyType == "KIO::AuthInfo" )
01191     {
01192        QDataStream stream2( reply, IO_ReadOnly );
01193        stream2 >> authResult;
01194     }
01195     else
01196     {
01197        kdError(7019) << "DCOP function checkAuthInfo(...) returns "
01198                      << replyType << ", expected KIO::AuthInfo" << endl;
01199        return false;
01200     }
01201     if (!authResult.isModified())
01202     {
01203        return false;
01204     }
01205 
01206     info = authResult;
01207     return true;
01208 }
01209 
01210 bool SlaveBase::cacheAuthentication( const AuthInfo& info )
01211 {
01212     QByteArray params;
01213     long windowId = metaData("window-id").toLong();
01214 
01215     (void) dcopClient(); // Make sure to have a dcop client.
01216 
01217     QDataStream stream(params, IO_WriteOnly);
01218     stream << info << windowId;
01219 
01220     d->dcopClient->send( "kded", "kpasswdserver", "addAuthInfo(KIO::AuthInfo, long int)", params );
01221 
01222     return true;
01223 }
01224 
01225 int SlaveBase::connectTimeout()
01226 {
01227     bool ok;
01228     QString tmp = metaData("ConnectTimeout");
01229     int result = tmp.toInt(&ok);
01230     if (ok)
01231        return result;
01232     return DEFAULT_CONNECT_TIMEOUT;
01233 }
01234 
01235 int SlaveBase::proxyConnectTimeout()
01236 {
01237     bool ok;
01238     QString tmp = metaData("ProxyConnectTimeout");
01239     int result = tmp.toInt(&ok);
01240     if (ok)
01241        return result;
01242     return DEFAULT_PROXY_CONNECT_TIMEOUT;
01243 }
01244 
01245 
01246 int SlaveBase::responseTimeout()
01247 {
01248     bool ok;
01249     QString tmp = metaData("ResponseTimeout");
01250     int result = tmp.toInt(&ok);
01251     if (ok)
01252        return result;
01253     return DEFAULT_RESPONSE_TIMEOUT;
01254 }
01255 
01256 
01257 int SlaveBase::readTimeout()
01258 {
01259     bool ok;
01260     QString tmp = metaData("ReadTimeout");
01261     int result = tmp.toInt(&ok);
01262     if (ok)
01263        return result;
01264     return DEFAULT_READ_TIMEOUT;
01265 }
01266 
01267 bool SlaveBase::wasKilled() const
01268 {
01269    return d->wasKilled;
01270 }
01271 
01272 void SlaveBase::setKillFlag()
01273 {
01274    d->wasKilled=true;
01275 }
01276 
01277 void SlaveBase::virtual_hook( int, void* )
01278 { /*BASE::virtual_hook( id, data );*/ }
01279 
KDE Home | KDE Accessibility Home | Description of Access Keys