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