26 #include "slavebase.h"
31 #ifdef HAVE_SYS_SELECT_H
32 #include <sys/select.h>
45 #include <dcopclient.h>
47 #include <kapplication.h>
50 #include <kdesu/client.h>
54 #include "kremoteencoding.h"
56 #include "kio/slavebase.h"
57 #include "kio/connection.h"
58 #include "kio/ioslave_defaults.h"
59 #include "kio/slaveinterface.h"
61 #include "uiserver_stub.h"
71 template class TQPtrList<TQValueList<UDSAtom> >;
72 typedef TQValueList<TQCString> AuthKeysList;
73 typedef TQMap<TQString,TQCString> AuthKeysMap;
74 #define KIO_DATA TQByteArray data; TQDataStream stream( data, IO_WriteOnly ); stream
75 #define KIO_FILESIZE_T(x) (unsigned long)(x & 0xffffffff) << (unsigned long)(x >> 32)
79 class SlaveBaseConfig :
public KConfigBase
85 bool internalHasGroup(
const TQCString &)
const { qWarning(
"hasGroup(const TQCString &)");
88 TQStringList groupList()
const {
return TQStringList(); }
90 TQMap<TQString,TQString> entryMap(
const TQString &group)
const
91 { Q_UNUSED(group);
return TQMap<TQString,TQString>(); }
93 void reparseConfiguration() { }
95 KEntryMap internalEntryMap(
const TQString &pGroup)
const { Q_UNUSED(pGroup);
return KEntryMap(); }
97 KEntryMap internalEntryMap()
const {
return KEntryMap(); }
99 void putData(
const KEntryKey &_key,
const KEntry&_data,
bool _checkGroup)
100 { Q_UNUSED(_key); Q_UNUSED(_data); Q_UNUSED(_checkGroup); }
102 KEntry lookupData(
const KEntryKey &_key)
const
105 TQString value = slave->metaData(_key.c_key);
107 entry.mValue = value.utf8();
115 class SlaveBasePrivate {
119 bool needSendCanResume:1;
123 SlaveBaseConfig *config;
126 struct timeval last_tv;
129 DCOPClient *dcopClient;
132 TQByteArray timeoutData;
138 long SlaveBase::s_seqNr;
140 static volatile bool slaveWriteError =
false;
142 static const char *s_protocol;
145 static void genericsig_handler(
int sigNumber)
147 signal(sigNumber,SIG_IGN);
155 signal(SIGALRM,SIG_DFL);
162 SlaveBase::SlaveBase(
const TQCString &protocol,
163 const TQCString &pool_socket,
164 const TQCString &app_socket )
165 : mProtocol(protocol), m_pConnection(0),
166 mPoolSocket( TQFile::decodeName(pool_socket)),
167 mAppSocket( TQFile::decodeName(app_socket))
169 s_protocol = protocol.data();
171 if (!getenv(
"KDE_DEBUG"))
173 KCrash::setCrashHandler( sigsegv_handler );
174 signal(SIGILL,&sigsegv_handler);
175 signal(SIGTRAP,&sigsegv_handler);
176 signal(SIGABRT,&sigsegv_handler);
177 signal(SIGBUS,&sigsegv_handler);
178 signal(SIGALRM,&sigsegv_handler);
179 signal(SIGFPE,&sigsegv_handler);
181 signal(SIGPOLL, &sigsegv_handler);
184 signal(SIGSYS, &sigsegv_handler);
187 signal(SIGVTALRM, &sigsegv_handler);
190 signal(SIGXCPU, &sigsegv_handler);
193 signal(SIGXFSZ, &sigsegv_handler);
197 struct sigaction act;
198 act.sa_handler = sigpipe_handler;
199 sigemptyset( &act.sa_mask );
201 sigaction( SIGPIPE, &act, 0 );
203 signal(SIGINT,&genericsig_handler);
204 signal(SIGQUIT,&genericsig_handler);
205 signal(SIGTERM,&genericsig_handler);
211 listEntryCurrentSize = 100;
213 gettimeofday(&tp, 0);
214 listEntry_sec = tp.tv_sec;
215 listEntry_usec = tp.tv_usec;
216 mConnectedToApp =
true;
218 d =
new SlaveBasePrivate;
221 d->slaveid += TQString::number(getpid());
223 d->needSendCanResume =
false;
224 d->config =
new SlaveBaseConfig(
this);
227 d->last_tv.tv_sec = 0;
228 d->last_tv.tv_usec = 0;
231 d->sentListEntries=0;
233 connectSlave(mAppSocket);
239 SlaveBase::~SlaveBase()
249 d->dcopClient = KApplication::dcopClient();
250 if (!d->dcopClient->isAttached())
251 d->dcopClient->attach();
252 d->dcopClient->setDaemonMode(
true );
254 return d->dcopClient;
257 void SlaveBase::dispatchLoop()
259 #ifdef Q_OS_UNIX //TODO: WIN32
265 if (d->timeout && (d->timeout < time(0)))
267 TQByteArray
data = d->timeoutData;
269 d->timeoutData = TQByteArray();
274 assert(appconn->
inited());
275 int maxfd = appconn->
fd_from();
276 FD_SET(appconn->
fd_from(), &rfds);
279 FD_SET( d->dcopClient->socket(), &rfds );
280 if( d->dcopClient->socket() > maxfd )
281 maxfd = d->dcopClient->socket();
286 retval = select( maxfd + 1, &rfds, NULL, NULL, NULL);
291 tv.tv_sec = kMax(d->timeout-time(0),(time_t) 1);
293 retval = select( maxfd + 1, &rfds, NULL, NULL, &tv);
295 if ((retval>0) && FD_ISSET(appconn->
fd_from(), &rfds))
299 if ( appconn->
read(&cmd, data) != -1 )
306 if (mConnectedToApp && !mPoolSocket.isEmpty())
309 mConnectedToApp =
false;
319 if( retval > 0 && d->dcopClient && FD_ISSET( d->dcopClient->socket(), &rfds ))
321 d->dcopClient->processSocketData( d->dcopClient->socket());
323 if ((retval<0) && (errno != EINTR))
325 kdDebug(7019) <<
"dispatchLoop(): select returned " << retval <<
" "
326 << (errno==EBADF?
"EBADF":errno==EINTR?
"EINTR":errno==EINVAL?
"EINVAL":errno==ENOMEM?
"ENOMEM":
"unknown")
327 <<
" (" << errno <<
")" << endl;
333 kdDebug(7019)<<
" dispatchLoop() slave was killed, returning"<<endl;
338 #error The KIO slave system only works under UNIX
344 #ifdef Q_OS_UNIX //TODO: KSocket not yet available on WIN32
345 appconn->
init(
new KSocket(TQFile::encodeName(path).
data()));
348 kdDebug(7019) <<
"SlaveBase: failed to connect to " << path << endl;
352 setConnection(appconn);
356 void SlaveBase::disconnectSlave()
363 mOutgoingMetaData.replace(key, value);
368 if (mIncomingMetaData.contains(key))
369 return mIncomingMetaData[key];
370 if (d->configData.contains(key))
371 return d->configData[key];
372 return TQString::null;
377 if (mIncomingMetaData.contains(key))
379 if (d->configData.contains(key))
399 KIO_DATA << mOutgoingMetaData;
401 slaveWriteError =
false;
402 m_pConnection->
send( INF_META_DATA, data );
403 if (slaveWriteError) exit();
404 mOutgoingMetaData.clear();
409 if (d->remotefile != 0)
410 return d->remotefile;
417 if (!mOutgoingMetaData.isEmpty())
419 slaveWriteError =
false;
420 m_pConnection->
send( MSG_DATA, data );
421 if (slaveWriteError) exit();
430 if (d->needSendCanResume)
432 m_pConnection->
send( MSG_DATA_REQ );
437 mIncomingMetaData.clear();
438 mOutgoingMetaData.clear();
439 KIO_DATA << (TQ_INT32) _errid << _text;
441 m_pConnection->
send( MSG_ERROR, data );
443 listEntryCurrentSize = 100;
444 d->sentListEntries=0;
450 slaveWriteError =
false;
451 m_pConnection->
send( MSG_CONNECTED );
452 if (slaveWriteError) exit();
457 mIncomingMetaData.clear();
458 if (!mOutgoingMetaData.isEmpty())
460 m_pConnection->
send( MSG_FINISHED );
463 listEntryCurrentSize = 100;
464 d->sentListEntries=0;
470 m_pConnection->
send( MSG_NEED_SUBURL_DATA );
475 pid_t pid = getpid();
476 TQ_INT8 b = connected ? 1 : 0;
477 KIO_DATA << pid <<
mProtocol << host << b;
479 stream << d->onHoldUrl;
480 m_pConnection->
send( MSG_SLAVE_STATUS, data );
483 void SlaveBase::canResume()
485 m_pConnection->
send( MSG_CANRESUME );
490 KIO_DATA << KIO_FILESIZE_T(_bytes);
491 slaveWriteError =
false;
492 m_pConnection->
send( INF_TOTAL_SIZE, data );
493 if (slaveWriteError) exit();
497 gettimeofday(&tp, 0);
498 listEntry_sec = tp.tv_sec;
499 listEntry_usec = tp.tv_usec;
501 d->sentListEntries=0;
506 bool emitSignal=
false;
508 int gettimeofday_res=gettimeofday( &tv, 0L );
510 if( _bytes == d->totalSize )
512 else if ( gettimeofday_res == 0 ) {
513 time_t msecdiff = 2000;
514 if (d->last_tv.tv_sec) {
516 msecdiff = 1000 * ( tv.tv_sec - d->last_tv.tv_sec );
517 time_t usecdiff = tv.tv_usec - d->last_tv.tv_usec;
518 if ( usecdiff < 0 ) {
522 msecdiff += usecdiff / 1000;
524 emitSignal=msecdiff >= 100;
528 KIO_DATA << KIO_FILESIZE_T(_bytes);
529 slaveWriteError =
false;
530 m_pConnection->
send( INF_PROCESSED_SIZE, data );
531 if (slaveWriteError) exit();
532 if ( gettimeofday_res == 0 ) {
533 d->last_tv.tv_sec = tv.tv_sec;
534 d->last_tv.tv_usec = tv.tv_usec;
542 kdDebug(7019) <<
"SlaveBase::processedPercent: STUB" << endl;
548 KIO_DATA << (TQ_UINT32) _bytes_per_second;
549 slaveWriteError =
false;
550 m_pConnection->
send( INF_SPEED, data );
551 if (slaveWriteError) exit();
557 m_pConnection->
send( INF_REDIRECTION, data );
562 m_pConnection->
send( INF_ERROR_PAGE );
565 static bool isSubCommand(
int cmd)
567 return ( (cmd == CMD_REPARSECONFIGURATION) ||
568 (cmd == CMD_META_DATA) ||
569 (cmd == CMD_CONFIG) ||
570 (cmd == CMD_SUBURL) ||
571 (cmd == CMD_SLAVE_STATUS) ||
572 (cmd == CMD_SLAVE_CONNECT) ||
573 (cmd == CMD_SLAVE_HOLD) ||
574 (cmd == CMD_MULTI_GET));
584 if (!mOutgoingMetaData.isEmpty())
587 KIO_DATA << mOutgoingMetaData;
588 m_pConnection->
send( INF_META_DATA, data );
591 m_pConnection->
send( INF_MIME_TYPE, data );
595 if ( m_pConnection->
read( &cmd, data ) == -1 ) {
596 kdDebug(7019) <<
"SlaveBase: mimetype: read error" << endl;
600 if ( cmd == CMD_HOST)
602 if ( isSubCommand(cmd) )
604 dispatch( cmd, data );
610 while (cmd != CMD_NONE);
611 mOutgoingMetaData.clear();
614 void SlaveBase::exit()
623 m_pConnection->
send( INF_WARNING, data );
629 m_pConnection->
send( INF_INFOMESSAGE, data );
634 KIO_DATA << host << d->slaveid;
635 m_pConnection->
send( MSG_NET_REQUEST, data );
640 TQDataStream stream( data, IO_ReadOnly );
649 KIO_DATA << host << d->slaveid;
650 m_pConnection->
send( MSG_NET_DROP, data );
656 slaveWriteError =
false;
657 m_pConnection->
send( MSG_STAT_ENTRY, data );
658 if (slaveWriteError) exit();
663 static struct timeval tp;
664 static const int maximum_updatetime = 300;
665 static const int minimum_updatetime = 100;
668 pendingListEntries.append(entry);
670 if (pendingListEntries.count() > listEntryCurrentSize) {
671 gettimeofday(&tp, 0);
673 long diff = ((tp.tv_sec - listEntry_sec) * 1000000 +
674 tp.tv_usec - listEntry_usec) / 1000;
677 if (diff > maximum_updatetime) {
678 listEntryCurrentSize = listEntryCurrentSize * 3 / 4;
683 else if (((pendingListEntries.count()*maximum_updatetime)/diff) > (d->totalSize-d->sentListEntries))
684 listEntryCurrentSize=d->totalSize-d->sentListEntries+1;
687 else if (diff < minimum_updatetime)
688 listEntryCurrentSize = (pendingListEntries.count() * maximum_updatetime) / diff;
695 pendingListEntries.clear();
697 gettimeofday(&tp, 0);
698 listEntry_sec = tp.tv_sec;
699 listEntry_usec = tp.tv_usec;
705 KIO_DATA << (TQ_UINT32)list.count();
706 UDSEntryListConstIterator it = list.begin();
707 UDSEntryListConstIterator end = list.end();
708 for (; it != end; ++it)
710 slaveWriteError =
false;
711 m_pConnection->
send( MSG_LIST_ENTRIES, data);
712 if (slaveWriteError) exit();
713 d->sentListEntries+=(uint)list.count();
717 const TQCString& group,
720 KIO_DATA << key << group << keepPass;
721 m_pConnection->
send( MSG_AUTH_KEY, data );
726 KIO_DATA << key.utf8() ;
727 m_pConnection->
send( MSG_DEL_AUTH_KEY, data );
730 void SlaveBase::sigsegv_handler(
int sig)
736 signal(SIGALRM,SIG_DFL);
742 snprintf(buffer,
sizeof(buffer),
"kioslave: ####### CRASH ###### protocol = %s pid = %d signal = %d\n", s_protocol, getpid(), sig);
743 write(2, buffer, strlen(buffer));
745 #ifdef HAVE_BACKTRACE
747 int n = backtrace(trace, 256);
749 backtrace_symbols_fd(trace, n, 2);
756 void SlaveBase::sigpipe_handler (
int)
762 slaveWriteError =
true;
812 bool SlaveBase::dispatch()
814 assert( m_pConnection );
818 if ( m_pConnection->
read( &cmd, data ) == -1 )
820 kdDebug(7019) <<
"SlaveBase::dispatch() has read error." << endl;
824 dispatch( cmd, data );
839 long windowId =
metaData(
"window-id").toLong();
840 long progressId =
metaData(
"progress-id").toLong();
841 unsigned long userTimestamp =
metaData(
"user-timestamp").toULong();
843 kdDebug(7019) <<
"SlaveBase::openPassDlg window-id=" << windowId <<
" progress-id=" << progressId << endl;
847 UIServer_stub uiserver(
"kio_uiserver",
"UIServer" );
849 uiserver.setJobVisible( progressId,
false );
851 TQDataStream stream(params, IO_WriteOnly);
853 if (
metaData(
"no-auth-prompt").lower() ==
"true")
854 stream << info << TQString(
"<NoAuthPrompt>") << windowId << s_seqNr << userTimestamp;
856 stream << info << errorMsg << windowId << s_seqNr << userTimestamp;
858 bool callOK = d->dcopClient->call(
"kded",
"kpasswdserver",
"queryAuthInfo(KIO::AuthInfo, TQString, long int, long int, unsigned long int)",
859 params, replyType, reply );
862 uiserver.setJobVisible( progressId,
true );
866 kdWarning(7019) <<
"Can't communicate with kded_kpasswdserver!" << endl;
870 if ( replyType ==
"KIO::AuthInfo" )
872 TQDataStream stream2( reply, IO_ReadOnly );
873 stream2 >> authResult >> s_seqNr;
877 kdError(7019) <<
"DCOP function queryAuthInfo(...) returns "
878 << replyType <<
", expected KIO::AuthInfo" << endl;
887 kdDebug(7019) <<
"SlaveBase::openPassDlg: username=" << info.
username << endl;
888 kdDebug(7019) <<
"SlaveBase::openPassDlg: password=[hidden]" << endl;
894 const TQString &buttonYes,
const TQString &buttonNo )
896 return messageBox( text, type, caption, buttonYes, buttonNo, TQString::null );
900 const TQString &buttonYes,
const TQString &buttonNo,
const TQString &dontAskAgainName )
902 kdDebug(7019) <<
"messageBox " << type <<
" " << text <<
" - " << caption << buttonYes << buttonNo << endl;
903 KIO_DATA << (TQ_INT32)type << text << caption << buttonYes << buttonNo << dontAskAgainName;
904 m_pConnection->
send( INF_MESSAGEBOX, data );
907 TQDataStream stream( data, IO_ReadOnly );
910 kdDebug(7019) <<
"got messagebox answer" << answer << endl;
918 kdDebug(7019) <<
"SlaveBase::canResume offset=" <<
KIO::number(offset) << endl;
919 d->needSendCanResume =
false;
920 KIO_DATA << KIO_FILESIZE_T(offset);
921 m_pConnection->
send( MSG_RESUME, data );
925 if (
waitForAnswer( CMD_RESUMEANSWER, CMD_NONE, data, &cmd ) != -1 )
927 kdDebug(7019) <<
"SlaveBase::canResume returning " << (cmd == CMD_RESUMEANSWER) << endl;
928 return cmd == CMD_RESUMEANSWER;
943 result = m_pConnection->
read( &cmd, data );
946 kdDebug(7019) <<
"SlaveBase::waitForAnswer has read error." << endl;
949 if ( cmd == expected1 || cmd == expected2 )
951 if ( pCmd ) *pCmd = cmd;
954 if ( isSubCommand(cmd) )
956 dispatch( cmd, data );
960 kdWarning() <<
"Got cmd " << cmd <<
" while waiting for an answer!" << endl;
976 d->timeout = time(0)+(time_t)timeout;
977 else if (timeout == 0)
982 d->timeoutData =
data;
985 void SlaveBase::dispatch(
int command,
const TQByteArray &data )
987 TQDataStream stream( data, IO_ReadOnly );
998 stream >> host >> i >> user >> passwd;
999 setHost( host, i, user, passwd );
1005 case CMD_DISCONNECT:
1008 case CMD_SLAVE_STATUS:
1011 case CMD_SLAVE_CONNECT:
1014 TQString app_socket;
1015 TQDataStream stream( data, IO_ReadOnly);
1016 stream >> app_socket;
1017 appconn->
send( MSG_SLAVE_ACK );
1019 mConnectedToApp =
true;
1022 case CMD_SLAVE_HOLD:
1025 TQDataStream stream( data, IO_ReadOnly);
1030 mConnectedToApp =
false;
1034 case CMD_REPARSECONFIGURATION:
1038 stream >> d->configData;
1039 #ifdef Q_OS_UNIX //TODO: not yet available on WIN32
1040 KSocks::setConfig(d->config);
1042 delete d->remotefile;
1053 TQ_INT8 iOverwrite, iResume;
1054 stream >> url >> iOverwrite >> iResume >> permissions;
1055 bool overwrite = ( iOverwrite != 0 );
1056 bool resume = ( iResume != 0 );
1061 d->needSendCanResume =
true ;
1063 put( url, permissions, overwrite, resume);
1085 stream >> url >> url2 >> iOverwrite;
1086 bool overwrite = (iOverwrite != 0);
1087 rename( url, url2, overwrite );
1093 stream >> target >> url >> iOverwrite;
1094 bool overwrite = (iOverwrite != 0);
1095 symlink( target, url, overwrite );
1102 stream >> url >> url2 >> permissions >> iOverwrite;
1103 bool overwrite = (iOverwrite != 0);
1104 copy( url, url2, permissions, overwrite );
1109 stream >> url >> isFile;
1110 del( url, isFile != 0);
1121 stream >> mIncomingMetaData;
1128 fprintf(stderr,
"Got unexpected CMD_NONE!\n");
1142 if( !url.isValid() )
1143 return TQString::null;
1146 TQString key = url.protocol();
1149 int port = url.port();
1153 key += TQString::number(port);
1164 int success = client.ping();
1167 success = client.startServer();
1170 kdDebug(7019) <<
"Cannot start a new deamon!!" << endl;
1173 kdDebug(7019) <<
"Sucessfully started new cache deamon!!" << endl;
1183 TQCString replyType;
1187 long windowId =
metaData(
"window-id").toLong();
1188 unsigned long userTimestamp =
metaData(
"user-timestamp").toULong();
1190 kdDebug(7019) <<
"SlaveBase::checkCachedAuthInfo window = " << windowId <<
" url = " << info.
url.url() << endl;
1194 TQDataStream stream(params, IO_WriteOnly);
1195 stream << info << windowId << userTimestamp;
1197 if ( !d->dcopClient->call(
"kded",
"kpasswdserver",
"checkAuthInfo(KIO::AuthInfo, long int, unsigned long int)",
1198 params, replyType, reply ) )
1200 kdWarning(7019) <<
"Can't communicate with kded_kpasswdserver!" << endl;
1204 if ( replyType ==
"KIO::AuthInfo" )
1206 TQDataStream stream2( reply, IO_ReadOnly );
1207 stream2 >> authResult;
1211 kdError(7019) <<
"DCOP function checkAuthInfo(...) returns "
1212 << replyType <<
", expected KIO::AuthInfo" << endl;
1227 long windowId =
metaData(
"window-id").toLong();
1231 TQDataStream stream(params, IO_WriteOnly);
1232 stream << info << windowId;
1234 d->dcopClient->send(
"kded",
"kpasswdserver",
"addAuthInfo(KIO::AuthInfo, long int)", params );
1242 TQString tmp =
metaData(
"ConnectTimeout");
1243 int result = tmp.toInt(&ok);
1246 return DEFAULT_CONNECT_TIMEOUT;
1252 TQString tmp =
metaData(
"ProxyConnectTimeout");
1253 int result = tmp.toInt(&ok);
1256 return DEFAULT_PROXY_CONNECT_TIMEOUT;
1263 TQString tmp =
metaData(
"ResponseTimeout");
1264 int result = tmp.toInt(&ok);
1267 return DEFAULT_RESPONSE_TIMEOUT;
1274 TQString tmp =
metaData(
"ReadTimeout");
1275 int result = tmp.toInt(&ok);
1278 return DEFAULT_READ_TIMEOUT;
1283 return d->wasKilled;
1291 void SlaveBase::virtual_hook(
int,
void* )