kio Library API Documentation

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