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