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