slaveinterface.cpp
00001 /* This file is part of the KDE libraries 00002 Copyright (C) 2000 David Faure <faure@kde.org> 00003 00004 This library is free software; you can redistribute it and/or 00005 modify it under the terms of the GNU Library General Public 00006 License version 2 as published by the Free Software Foundation. 00007 00008 This library is distributed in the hope that it will be useful, 00009 but WITHOUT ANY WARRANTY; without even the implied warranty of 00010 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00011 Library General Public License for more details. 00012 00013 You should have received a copy of the GNU Library General Public License 00014 along with this library; see the file COPYING.LIB. If not, write to 00015 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00016 Boston, MA 02110-1301, USA. 00017 */ 00018 00019 #include "tdeio/slaveinterface.h" 00020 #include "tdeio/slavebase.h" 00021 #include "tdeio/connection.h" 00022 #include <errno.h> 00023 #include <assert.h> 00024 #include <kdebug.h> 00025 #include <stdlib.h> 00026 #include <sys/time.h> 00027 #include <unistd.h> 00028 #include <signal.h> 00029 #include <tdeio/observer.h> 00030 #include <tdeapplication.h> 00031 #include <dcopclient.h> 00032 #include <time.h> 00033 #include <tqtimer.h> 00034 00035 using namespace TDEIO; 00036 00037 00038 TQDataStream &operator <<(TQDataStream &s, const TDEIO::UDSEntry &e ) 00039 { 00040 // On 32-bit platforms we send UDS_SIZE with UDS_SIZE_LARGE in front 00041 // of it to carry the 32 msb. We can't send a 64 bit UDS_SIZE because 00042 // that would break the compatibility of the wire-protocol with KDE 2. 00043 // We do the same on 64-bit platforms in case we run in a mixed 32/64bit 00044 // environment. 00045 00046 TQ_UINT32 size = 0; 00047 TDEIO::UDSEntry::ConstIterator it = e.begin(); 00048 for( ; it != e.end(); ++it ) 00049 { 00050 size++; 00051 if ((*it).m_uds == TDEIO::UDS_SIZE) 00052 size++; 00053 } 00054 s << size; 00055 it = e.begin(); 00056 for( ; it != e.end(); ++it ) 00057 { 00058 if ((*it).m_uds == TDEIO::UDS_SIZE) 00059 { 00060 TDEIO::UDSAtom a; 00061 a.m_uds = TDEIO::UDS_SIZE_LARGE; 00062 a.m_long = (*it).m_long >> 32; 00063 s << a; 00064 } 00065 s << *it; 00066 } 00067 return s; 00068 } 00069 00070 TQDataStream &operator >>(TQDataStream &s, TDEIO::UDSEntry &e ) 00071 { 00072 e.clear(); 00073 TQ_UINT32 size; 00074 s >> size; 00075 00076 // On 32-bit platforms we send UDS_SIZE with UDS_SIZE_LARGE in front 00077 // of it to carry the 32 msb. We can't send a 64 bit UDS_SIZE because 00078 // that would break the compatibility of the wire-protocol with KDE 2. 00079 // We do the same on 64-bit platforms in case we run in a mixed 32/64bit 00080 // environment. 00081 TQ_LLONG msb = 0; 00082 for(TQ_UINT32 i = 0; i < size; i++) 00083 { 00084 TDEIO::UDSAtom a; 00085 s >> a; 00086 if (a.m_uds == TDEIO::UDS_SIZE_LARGE) 00087 { 00088 msb = a.m_long; 00089 } 00090 else 00091 { 00092 if (a.m_uds == TDEIO::UDS_SIZE) 00093 { 00094 if (a.m_long < 0) 00095 a.m_long += (TQ_LLONG) 1 << 32; 00096 a.m_long += msb << 32; 00097 } 00098 e.append(a); 00099 msb = 0; 00100 } 00101 } 00102 return s; 00103 } 00104 00105 static const unsigned int max_nums = 8; 00106 00107 class TDEIO::SlaveInterfacePrivate 00108 { 00109 public: 00110 SlaveInterfacePrivate() { 00111 slave_calcs_speed = false; 00112 start_time.tv_sec = 0; 00113 start_time.tv_usec = 0; 00114 last_time = 0; 00115 nums = 0; 00116 filesize = 0; 00117 offset = 0; 00118 } 00119 bool slave_calcs_speed; 00120 struct timeval start_time; 00121 uint nums; 00122 long times[max_nums]; 00123 TDEIO::filesize_t sizes[max_nums]; 00124 size_t last_time; 00125 TDEIO::filesize_t filesize, offset; 00126 00127 TQTimer speed_timer; 00128 }; 00129 00131 00132 SlaveInterface::SlaveInterface( Connection * connection ) 00133 { 00134 m_pConnection = connection; 00135 m_progressId = 0; 00136 00137 d = new SlaveInterfacePrivate; 00138 connect(&d->speed_timer, TQT_SIGNAL(timeout()), TQT_SLOT(calcSpeed())); 00139 } 00140 00141 SlaveInterface::~SlaveInterface() 00142 { 00143 // Note: no kdDebug() here (scheduler is deleted very late) 00144 m_pConnection = 0; // a bit like the "wasDeleted" of TQObject... 00145 00146 delete d; 00147 } 00148 00149 static TDEIO::filesize_t readFilesize_t(TQDataStream &stream) 00150 { 00151 TDEIO::filesize_t result; 00152 unsigned long ul; 00153 stream >> ul; 00154 result = ul; 00155 if (stream.atEnd()) 00156 return result; 00157 stream >> ul; 00158 result += ((TDEIO::filesize_t)ul) << 32; 00159 return result; 00160 } 00161 00162 00163 bool SlaveInterface::dispatch() 00164 { 00165 assert( m_pConnection ); 00166 00167 int cmd; 00168 TQByteArray data; 00169 00170 if (m_pConnection->read( &cmd, data ) == -1) 00171 return false; 00172 00173 return dispatch( cmd, data ); 00174 } 00175 00176 void SlaveInterface::calcSpeed() 00177 { 00178 if (d->slave_calcs_speed) { 00179 d->speed_timer.stop(); 00180 return; 00181 } 00182 00183 struct timeval tv; 00184 gettimeofday(&tv, 0); 00185 00186 long diff = ((tv.tv_sec - d->start_time.tv_sec) * 1000000 + 00187 tv.tv_usec - d->start_time.tv_usec) / 1000; 00188 if (diff - d->last_time >= 900) { 00189 d->last_time = diff; 00190 if (d->nums == max_nums) { 00191 // let's hope gcc can optimize that well enough 00192 // otherwise I'd try memcpy :) 00193 for (unsigned int i = 1; i < max_nums; ++i) { 00194 d->times[i-1] = d->times[i]; 00195 d->sizes[i-1] = d->sizes[i]; 00196 } 00197 d->nums--; 00198 } 00199 d->times[d->nums] = diff; 00200 d->sizes[d->nums++] = d->filesize - d->offset; 00201 00202 TDEIO::filesize_t lspeed = 1000 * (d->sizes[d->nums-1] - d->sizes[0]) / (d->times[d->nums-1] - d->times[0]); 00203 00204 // kdDebug() << "proceeed " << (long)d->filesize << " " << diff << " " 00205 // << long(d->sizes[d->nums-1] - d->sizes[0]) << " " 00206 // << d->times[d->nums-1] - d->times[0] << " " 00207 // << long(lspeed) << " " << double(d->filesize) / diff 00208 // << " " << convertSize(lspeed) << " " 00209 // << convertSize(long(double(d->filesize) / diff) * 1000) << " " 00210 // << endl ; 00211 00212 if (!lspeed) { 00213 d->nums = 1; 00214 d->times[0] = diff; 00215 d->sizes[0] = d->filesize - d->offset; 00216 } 00217 emit speed(lspeed); 00218 } 00219 } 00220 00221 bool SlaveInterface::dispatch( int _cmd, const TQByteArray &rawdata ) 00222 { 00223 //kdDebug(7007) << "dispatch " << _cmd << endl; 00224 00225 TQDataStream stream( rawdata, IO_ReadOnly ); 00226 00227 TQString str1; 00228 TQ_INT32 i; 00229 TQ_INT8 b; 00230 TQ_UINT32 ul; 00231 00232 switch( _cmd ) { 00233 case MSG_DATA: 00234 emit data( rawdata ); 00235 break; 00236 case MSG_DATA_REQ: 00237 emit dataReq(); 00238 break; 00239 case MSG_FINISHED: 00240 //kdDebug(7007) << "Finished [this = " << this << "]" << endl; 00241 d->offset = 0; 00242 d->speed_timer.stop(); 00243 emit finished(); 00244 break; 00245 case MSG_STAT_ENTRY: 00246 { 00247 UDSEntry entry; 00248 stream >> entry; 00249 emit statEntry(entry); 00250 } 00251 break; 00252 case MSG_LIST_ENTRIES: 00253 { 00254 TQ_UINT32 count; 00255 stream >> count; 00256 00257 UDSEntryList list; 00258 UDSEntry entry; 00259 for (uint i = 0; i < count; i++) { 00260 stream >> entry; 00261 list.append(entry); 00262 } 00263 emit listEntries(list); 00264 00265 } 00266 break; 00267 case MSG_RESUME: // From the put job 00268 { 00269 d->offset = readFilesize_t(stream); 00270 emit canResume( d->offset ); 00271 } 00272 break; 00273 case MSG_CANRESUME: // From the get job 00274 d->filesize = d->offset; 00275 emit canResume(0); // the arg doesn't matter 00276 break; 00277 case MSG_ERROR: 00278 stream >> i >> str1; 00279 kdDebug(7007) << "error " << i << " " << str1 << endl; 00280 emit error( i, str1 ); 00281 break; 00282 case MSG_SLAVE_STATUS: 00283 { 00284 pid_t pid; 00285 TQCString protocol; 00286 stream >> pid >> protocol >> str1 >> b; 00287 emit slaveStatus(pid, protocol, str1, (b != 0)); 00288 } 00289 break; 00290 case MSG_CONNECTED: 00291 emit connected(); 00292 break; 00293 00294 case INF_TOTAL_SIZE: 00295 { 00296 TDEIO::filesize_t size = readFilesize_t(stream); 00297 gettimeofday(&d->start_time, 0); 00298 d->last_time = 0; 00299 d->filesize = d->offset; 00300 d->sizes[0] = d->filesize - d->offset; 00301 d->times[0] = 0; 00302 d->nums = 1; 00303 d->speed_timer.start(1000); 00304 d->slave_calcs_speed = false; 00305 emit totalSize( size ); 00306 } 00307 break; 00308 case INF_PROCESSED_SIZE: 00309 { 00310 TDEIO::filesize_t size = readFilesize_t(stream); 00311 emit processedSize( size ); 00312 d->filesize = size; 00313 } 00314 break; 00315 case INF_SPEED: 00316 stream >> ul; 00317 d->slave_calcs_speed = true; 00318 d->speed_timer.stop(); 00319 00320 emit speed( ul ); 00321 break; 00322 case INF_GETTING_FILE: 00323 break; 00324 case INF_ERROR_PAGE: 00325 emit errorPage(); 00326 break; 00327 case INF_REDIRECTION: 00328 { 00329 KURL url; 00330 stream >> url; 00331 00332 emit redirection( url ); 00333 } 00334 break; 00335 case INF_MIME_TYPE: 00336 stream >> str1; 00337 00338 emit mimeType( str1 ); 00339 if (!m_pConnection->suspended()) 00340 m_pConnection->sendnow( CMD_NONE, TQByteArray() ); 00341 break; 00342 case INF_WARNING: 00343 stream >> str1; 00344 00345 emit warning( str1 ); 00346 break; 00347 case INF_NEED_PASSWD: { 00348 AuthInfo info; 00349 stream >> info; 00350 openPassDlg( info ); 00351 break; 00352 } 00353 case INF_MESSAGEBOX: { 00354 kdDebug(7007) << "needs a msg box" << endl; 00355 TQString text, caption, buttonYes, buttonNo, dontAskAgainName; 00356 int type; 00357 stream >> type >> text >> caption >> buttonYes >> buttonNo; 00358 if (stream.atEnd()) 00359 messageBox(type, text, caption, buttonYes, buttonNo); 00360 else { 00361 stream >> dontAskAgainName; 00362 messageBox(type, text, caption, buttonYes, buttonNo, dontAskAgainName); 00363 } 00364 break; 00365 } 00366 case INF_INFOMESSAGE: { 00367 TQString msg; 00368 stream >> msg; 00369 infoMessage(msg); 00370 break; 00371 } 00372 case INF_META_DATA: { 00373 MetaData meta_data; 00374 stream >> meta_data; 00375 metaData(meta_data); 00376 break; 00377 } 00378 case INF_LOCALURL: { 00379 TQ_INT8 islocal; 00380 KURL url; 00381 stream >> islocal >> url; 00382 emit localURL( url, islocal ); 00383 break; 00384 } 00385 case MSG_NET_REQUEST: { 00386 TQString host; 00387 TQString slaveid; 00388 stream >> host >> slaveid; 00389 requestNetwork(host, slaveid); 00390 break; 00391 } 00392 case MSG_NET_DROP: { 00393 TQString host; 00394 TQString slaveid; 00395 stream >> host >> slaveid; 00396 dropNetwork(host, slaveid); 00397 break; 00398 } 00399 case MSG_NEED_SUBURL_DATA: { 00400 emit needSubURLData(); 00401 break; 00402 } 00403 case MSG_AUTH_KEY: { 00404 bool keep; 00405 TQCString key, group; 00406 stream >> key >> group >> keep; 00407 kdDebug(7007) << "Got auth-key: " << key << endl 00408 << " group-key: " << group << endl 00409 << " keep password: " << keep << endl; 00410 emit authorizationKey( key, group, keep ); 00411 break; 00412 } 00413 case MSG_DEL_AUTH_KEY: { 00414 TQCString key; 00415 stream >> key; 00416 kdDebug(7007) << "Delete auth-key: " << key << endl; 00417 emit delAuthorization( key ); 00418 } 00419 default: 00420 kdWarning(7007) << "Slave sends unknown command (" << _cmd << "), dropping slave" << endl; 00421 return false; 00422 } 00423 return true; 00424 } 00425 00426 void SlaveInterface::setOffset( TDEIO::filesize_t o) 00427 { 00428 d->offset = o; 00429 } 00430 00431 TDEIO::filesize_t SlaveInterface::offset() const { return d->offset; } 00432 00433 void SlaveInterface::requestNetwork(const TQString &host, const TQString &slaveid) 00434 { 00435 kdDebug(7007) << "requestNetwork " << host << slaveid << endl; 00436 TQByteArray packedArgs; 00437 TQDataStream stream( packedArgs, IO_WriteOnly ); 00438 stream << true; 00439 m_pConnection->sendnow( INF_NETWORK_STATUS, packedArgs ); 00440 } 00441 00442 void SlaveInterface::dropNetwork(const TQString &host, const TQString &slaveid) 00443 { 00444 kdDebug(7007) << "dropNetwork " << host << slaveid << endl; 00445 } 00446 00447 void SlaveInterface::sendResumeAnswer( bool resume ) 00448 { 00449 kdDebug(7007) << "SlaveInterface::sendResumeAnswer ok for resuming :" << resume << endl; 00450 m_pConnection->sendnow( resume ? CMD_RESUMEANSWER : CMD_NONE, TQByteArray() ); 00451 } 00452 00453 void SlaveInterface::openPassDlg( const TQString& prompt, const TQString& user, bool readOnly ) 00454 { 00455 AuthInfo info; 00456 info.prompt = prompt; 00457 info.username = user; 00458 info.readOnly = readOnly; 00459 openPassDlg( info ); 00460 } 00461 00462 void SlaveInterface::openPassDlg( const TQString& prompt, const TQString& user, 00463 const TQString& caption, const TQString& comment, 00464 const TQString& label, bool readOnly ) 00465 { 00466 AuthInfo info; 00467 info.prompt = prompt; 00468 info.username = user; 00469 info.caption = caption; 00470 info.comment = comment; 00471 info.commentLabel = label; 00472 info.readOnly = readOnly; 00473 openPassDlg( info ); 00474 } 00475 00476 void SlaveInterface::openPassDlg( AuthInfo& info ) 00477 { 00478 kdDebug(7007) << "SlaveInterface::openPassDlg: " 00479 << "User= " << info.username 00480 << ", Message= " << info.prompt << endl; 00481 bool result = Observer::self()->openPassDlg( info ); 00482 if ( m_pConnection ) 00483 { 00484 TQByteArray data; 00485 TQDataStream stream( data, IO_WriteOnly ); 00486 if ( result ) 00487 { 00488 stream << info; 00489 kdDebug(7007) << "SlaveInterface:::openPassDlg got: " 00490 << "User= " << info.username 00491 << ", Password= [hidden]" << endl; 00492 m_pConnection->sendnow( CMD_USERPASS, data ); 00493 } 00494 else 00495 m_pConnection->sendnow( CMD_NONE, data ); 00496 } 00497 } 00498 00499 void SlaveInterface::messageBox( int type, const TQString &text, const TQString &_caption, 00500 const TQString &buttonYes, const TQString &buttonNo ) 00501 { 00502 messageBox( type, text, _caption, buttonYes, buttonNo, TQString::null ); 00503 } 00504 00505 void SlaveInterface::messageBox( int type, const TQString &text, const TQString &_caption, 00506 const TQString &buttonYes, const TQString &buttonNo, const TQString &dontAskAgainName ) 00507 { 00508 kdDebug(7007) << "messageBox " << type << " " << text << " - " << _caption << " " << dontAskAgainName << endl; 00509 TQByteArray packedArgs; 00510 TQDataStream stream( packedArgs, IO_WriteOnly ); 00511 00512 TQString caption( _caption ); 00513 if ( type == TDEIO::SlaveBase::SSLMessageBox ) 00514 caption = TQString::fromUtf8(kapp->dcopClient()->appId()); // hack, see observer.cpp 00515 00516 emit needProgressId(); 00517 kdDebug(7007) << "SlaveInterface::messageBox m_progressId=" << m_progressId << endl; 00518 TQGuardedPtr<SlaveInterface> me = this; 00519 m_pConnection->suspend(); 00520 int result = Observer::/*self()->*/messageBox( m_progressId, type, text, caption, buttonYes, buttonNo, dontAskAgainName ); 00521 if ( me && m_pConnection ) // Don't do anything if deleted meanwhile 00522 { 00523 m_pConnection->resume(); 00524 kdDebug(7007) << this << " SlaveInterface result=" << result << endl; 00525 stream << result; 00526 m_pConnection->sendnow( CMD_MESSAGEBOXANSWER, packedArgs ); 00527 } 00528 } 00529 00530 // No longer used. 00531 // Remove in KDE 4.0 00532 void SlaveInterface::sigpipe_handler(int) 00533 { 00534 int saved_errno = errno; 00535 // Using kdDebug from a signal handler is not a good idea. 00536 #ifndef NDEBUG 00537 char msg[1000]; 00538 sprintf(msg, "*** SIGPIPE *** (ignored, pid = %ld)\n", (long) getpid()); 00539 if (write(2, msg, strlen(msg)) < 0) { 00540 // FIXME 00541 // Could not write error message 00542 // Triple fault? ;-) 00543 } 00544 #endif 00545 00546 // Do nothing. 00547 // dispatch will return false and that will trigger ERR_SLAVE_DIED in slave.cpp 00548 errno = saved_errno; 00549 } 00550 00551 void SlaveInterface::virtual_hook( int, void* ) 00552 { /*BASE::virtual_hook( id, data );*/ } 00553 00554 #include "slaveinterface.moc"