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"
65 template class TQPtrList<TQValueList<UDSAtom> >;
66 typedef TQValueList<TQCString> AuthKeysList;
67 typedef TQMap<TQString,TQCString> AuthKeysMap;
68 #define KIO_DATA TQByteArray data; TQDataStream stream( data, IO_WriteOnly ); stream
69 #define KIO_FILESIZE_T(x) (unsigned long)(x & 0xffffffff) << (unsigned long)(x >> 32)
73 class SlaveBaseConfig :
public KConfigBase
79 bool internalHasGroup(
const TQCString &)
const { qWarning(
"hasGroup(const TQCString &)");
82 TQStringList groupList()
const {
return TQStringList(); }
84 TQMap<TQString,TQString> entryMap(
const TQString &group)
const
85 { Q_UNUSED(group);
return TQMap<TQString,TQString>(); }
87 void reparseConfiguration() { }
89 KEntryMap internalEntryMap(
const TQString &pGroup)
const { Q_UNUSED(pGroup);
return KEntryMap(); }
91 KEntryMap internalEntryMap()
const {
return KEntryMap(); }
93 void putData(
const KEntryKey &_key,
const KEntry&_data,
bool _checkGroup)
94 { Q_UNUSED(_key); Q_UNUSED(_data); Q_UNUSED(_checkGroup); }
96 KEntry lookupData(
const KEntryKey &_key)
const
99 TQString value = slave->metaData(_key.c_key);
101 entry.mValue = value.utf8();
109 class SlaveBasePrivate {
113 bool needSendCanResume:1;
117 SlaveBaseConfig *config;
120 struct timeval last_tv;
123 DCOPClient *dcopClient;
126 TQByteArray timeoutData;
132 long SlaveBase::s_seqNr;
134 static volatile bool slaveWriteError =
false;
136 static const char *s_protocol;
139 static void genericsig_handler(
int sigNumber)
141 signal(sigNumber,SIG_IGN);
149 signal(SIGALRM,SIG_DFL);
156 SlaveBase::SlaveBase(
const TQCString &protocol,
157 const TQCString &pool_socket,
158 const TQCString &app_socket )
159 : mProtocol(protocol), m_pConnection(0),
160 mPoolSocket( TQFile::decodeName(pool_socket)),
161 mAppSocket( TQFile::decodeName(app_socket))
163 s_protocol = protocol.data();
165 if (!getenv(
"KDE_DEBUG"))
167 KCrash::setCrashHandler( sigsegv_handler );
168 signal(SIGILL,&sigsegv_handler);
169 signal(SIGTRAP,&sigsegv_handler);
170 signal(SIGABRT,&sigsegv_handler);
171 signal(SIGBUS,&sigsegv_handler);
172 signal(SIGALRM,&sigsegv_handler);
173 signal(SIGFPE,&sigsegv_handler);
175 signal(SIGPOLL, &sigsegv_handler);
178 signal(SIGSYS, &sigsegv_handler);
181 signal(SIGVTALRM, &sigsegv_handler);
184 signal(SIGXCPU, &sigsegv_handler);
187 signal(SIGXFSZ, &sigsegv_handler);
191 struct sigaction act;
192 act.sa_handler = sigpipe_handler;
193 sigemptyset( &act.sa_mask );
195 sigaction( SIGPIPE, &act, 0 );
197 signal(SIGINT,&genericsig_handler);
198 signal(SIGQUIT,&genericsig_handler);
199 signal(SIGTERM,&genericsig_handler);
205 listEntryCurrentSize = 100;
207 gettimeofday(&tp, 0);
208 listEntry_sec = tp.tv_sec;
209 listEntry_usec = tp.tv_usec;
210 mConnectedToApp =
true;
212 d =
new SlaveBasePrivate;
217 d->needSendCanResume =
false;
218 d->config =
new SlaveBaseConfig(
this);
221 d->last_tv.tv_sec = 0;
222 d->last_tv.tv_usec = 0;
225 d->sentListEntries=0;
227 connectSlave(mAppSocket);
233 SlaveBase::~SlaveBase()
244 if (!d->dcopClient->isAttached())
245 d->dcopClient->attach();
246 d->dcopClient->setDaemonMode(
true );
248 return d->dcopClient;
251 void SlaveBase::dispatchLoop()
253 #ifdef Q_OS_UNIX //TODO: WIN32
259 if (d->timeout && (d->timeout < time(0)))
261 TQByteArray
data = d->timeoutData;
263 d->timeoutData = TQByteArray();
268 assert(appconn->
inited());
269 int maxfd = appconn->
fd_from();
270 FD_SET(appconn->
fd_from(), &rfds);
273 FD_SET( d->dcopClient->socket(), &rfds );
274 if( d->dcopClient->socket() > maxfd )
275 maxfd = d->dcopClient->socket();
280 retval = select( maxfd + 1, &rfds, NULL, NULL, NULL);
285 tv.tv_sec = kMax(d->timeout-time(0),(time_t) 1);
287 retval = select( maxfd + 1, &rfds, NULL, NULL, &tv);
289 if ((retval>0) && FD_ISSET(appconn->
fd_from(), &rfds))
293 if ( appconn->
read(&cmd, data) != -1 )
300 if (mConnectedToApp && !mPoolSocket.isEmpty())
303 mConnectedToApp =
false;
313 if( retval > 0 && d->dcopClient && FD_ISSET( d->dcopClient->socket(), &rfds ))
315 d->dcopClient->processSocketData( d->dcopClient->socket());
317 if ((retval<0) && (errno != EINTR))
319 kdDebug(7019) <<
"dispatchLoop(): select returned " << retval <<
" "
320 << (errno==EBADF?
"EBADF":errno==EINTR?
"EINTR":errno==EINVAL?
"EINVAL":errno==ENOMEM?
"ENOMEM":
"unknown")
321 <<
" (" << errno <<
")" << endl;
327 kdDebug(7019)<<
" dispatchLoop() slave was killed, returning"<<endl;
332 #error The KIO slave system only works under UNIX
338 #ifdef Q_OS_UNIX //TODO: KSocket not yet available on WIN32
339 appconn->
init(
new KSocket(TQFile::encodeName(path).
data()));
342 kdDebug(7019) <<
"SlaveBase: failed to connect to " << path << endl;
346 setConnection(appconn);
350 void SlaveBase::disconnectSlave()
357 mOutgoingMetaData.replace(key, value);
362 if (mIncomingMetaData.contains(key))
363 return mIncomingMetaData[key];
364 if (d->configData.contains(key))
365 return d->configData[key];
366 return TQString::null;
371 if (mIncomingMetaData.contains(key))
373 if (d->configData.contains(key))
393 KIO_DATA << mOutgoingMetaData;
395 slaveWriteError =
false;
396 m_pConnection->
send( INF_META_DATA, data );
397 if (slaveWriteError) exit();
398 mOutgoingMetaData.clear();
403 if (d->remotefile != 0)
404 return d->remotefile;
411 if (!mOutgoingMetaData.isEmpty())
413 slaveWriteError =
false;
414 m_pConnection->
send( MSG_DATA, data );
415 if (slaveWriteError) exit();
424 if (d->needSendCanResume)
426 m_pConnection->
send( MSG_DATA_REQ );
431 mIncomingMetaData.clear();
432 mOutgoingMetaData.clear();
433 KIO_DATA << (TQ_INT32) _errid << _text;
435 m_pConnection->
send( MSG_ERROR, data );
437 listEntryCurrentSize = 100;
438 d->sentListEntries=0;
444 slaveWriteError =
false;
445 m_pConnection->
send( MSG_CONNECTED );
446 if (slaveWriteError) exit();
451 mIncomingMetaData.clear();
452 if (!mOutgoingMetaData.isEmpty())
454 m_pConnection->
send( MSG_FINISHED );
457 listEntryCurrentSize = 100;
458 d->sentListEntries=0;
464 m_pConnection->
send( MSG_NEED_SUBURL_DATA );
469 pid_t pid = getpid();
470 TQ_INT8 b = connected ? 1 : 0;
471 KIO_DATA << pid <<
mProtocol << host << b;
473 stream << d->onHoldUrl;
474 m_pConnection->
send( MSG_SLAVE_STATUS, data );
477 void SlaveBase::canResume()
479 m_pConnection->
send( MSG_CANRESUME );
484 KIO_DATA << KIO_FILESIZE_T(_bytes);
485 slaveWriteError =
false;
486 m_pConnection->
send( INF_TOTAL_SIZE, data );
487 if (slaveWriteError) exit();
491 gettimeofday(&tp, 0);
492 listEntry_sec = tp.tv_sec;
493 listEntry_usec = tp.tv_usec;
495 d->sentListEntries=0;
500 bool emitSignal=
false;
502 int gettimeofday_res=gettimeofday( &tv, 0L );
504 if( _bytes == d->totalSize )
506 else if ( gettimeofday_res == 0 ) {
507 time_t msecdiff = 2000;
508 if (d->last_tv.tv_sec) {
510 msecdiff = 1000 * ( tv.tv_sec - d->last_tv.tv_sec );
511 time_t usecdiff = tv.tv_usec - d->last_tv.tv_usec;
512 if ( usecdiff < 0 ) {
516 msecdiff += usecdiff / 1000;
518 emitSignal=msecdiff >= 100;
522 KIO_DATA << KIO_FILESIZE_T(_bytes);
523 slaveWriteError =
false;
524 m_pConnection->
send( INF_PROCESSED_SIZE, data );
525 if (slaveWriteError) exit();
526 if ( gettimeofday_res == 0 ) {
527 d->last_tv.tv_sec = tv.tv_sec;
528 d->last_tv.tv_usec = tv.tv_usec;
536 kdDebug(7019) <<
"SlaveBase::processedPercent: STUB" << endl;
542 KIO_DATA << (TQ_UINT32) _bytes_per_second;
543 slaveWriteError =
false;
544 m_pConnection->
send( INF_SPEED, data );
545 if (slaveWriteError) exit();
551 m_pConnection->
send( INF_REDIRECTION, data );
556 m_pConnection->
send( INF_ERROR_PAGE );
559 static bool isSubCommand(
int cmd)
561 return ( (cmd == CMD_REPARSECONFIGURATION) ||
562 (cmd == CMD_META_DATA) ||
563 (cmd == CMD_CONFIG) ||
564 (cmd == CMD_SUBURL) ||
565 (cmd == CMD_SLAVE_STATUS) ||
566 (cmd == CMD_SLAVE_CONNECT) ||
567 (cmd == CMD_SLAVE_HOLD) ||
568 (cmd == CMD_MULTI_GET));
578 if (!mOutgoingMetaData.isEmpty())
581 KIO_DATA << mOutgoingMetaData;
582 m_pConnection->
send( INF_META_DATA, data );
585 m_pConnection->
send( INF_MIME_TYPE, data );
589 if ( m_pConnection->
read( &cmd, data ) == -1 ) {
590 kdDebug(7019) <<
"SlaveBase: mimetype: read error" << endl;
594 if ( cmd == CMD_HOST)
596 if ( isSubCommand(cmd) )
598 dispatch( cmd, data );
604 while (cmd != CMD_NONE);
605 mOutgoingMetaData.clear();
608 void SlaveBase::exit()
617 m_pConnection->
send( INF_WARNING, data );
623 m_pConnection->
send( INF_INFOMESSAGE, data );
628 KIO_DATA << host << d->slaveid;
629 m_pConnection->
send( MSG_NET_REQUEST, data );
634 TQDataStream stream( data, IO_ReadOnly );
643 KIO_DATA << host << d->slaveid;
644 m_pConnection->
send( MSG_NET_DROP, data );
650 slaveWriteError =
false;
651 m_pConnection->
send( MSG_STAT_ENTRY, data );
652 if (slaveWriteError) exit();
657 static struct timeval tp;
658 static const int maximum_updatetime = 300;
659 static const int minimum_updatetime = 100;
662 pendingListEntries.append(entry);
664 if (pendingListEntries.count() > listEntryCurrentSize) {
665 gettimeofday(&tp, 0);
667 long diff = ((tp.tv_sec - listEntry_sec) * 1000000 +
668 tp.tv_usec - listEntry_usec) / 1000;
671 if (diff > maximum_updatetime) {
672 listEntryCurrentSize = listEntryCurrentSize * 3 / 4;
677 else if (((pendingListEntries.count()*maximum_updatetime)/diff) > (d->totalSize-d->sentListEntries))
678 listEntryCurrentSize=d->totalSize-d->sentListEntries+1;
681 else if (diff < minimum_updatetime)
682 listEntryCurrentSize = (pendingListEntries.count() * maximum_updatetime) / diff;
689 pendingListEntries.clear();
691 gettimeofday(&tp, 0);
692 listEntry_sec = tp.tv_sec;
693 listEntry_usec = tp.tv_usec;
699 KIO_DATA << (TQ_UINT32)list.count();
700 UDSEntryListConstIterator it = list.begin();
701 UDSEntryListConstIterator end = list.end();
702 for (; it != end; ++it)
704 slaveWriteError =
false;
705 m_pConnection->
send( MSG_LIST_ENTRIES, data);
706 if (slaveWriteError) exit();
707 d->sentListEntries+=(uint)list.count();
711 const TQCString& group,
714 KIO_DATA << key << group << keepPass;
715 m_pConnection->
send( MSG_AUTH_KEY, data );
720 KIO_DATA << key.utf8() ;
721 m_pConnection->
send( MSG_DEL_AUTH_KEY, data );
724 void SlaveBase::sigsegv_handler(
int sig)
730 signal(SIGALRM,SIG_DFL);
736 snprintf(buffer,
sizeof(buffer),
"kioslave: ####### CRASH ###### protocol = %s pid = %d signal = %d\n", s_protocol, getpid(), sig);
737 if (write(2, buffer, strlen(buffer)) >= 0) {
740 #else // SECURE_DEBUG
749 TQString backtrace = kdBacktrace();
750 if (write(2, backtrace.ascii(), backtrace.length()) < 0) {
754 #endif // SECURE_DEBUG
760 void SlaveBase::sigpipe_handler (
int)
766 slaveWriteError =
true;
816 bool SlaveBase::dispatch()
818 assert( m_pConnection );
822 if ( m_pConnection->
read( &cmd, data ) == -1 )
824 kdDebug(7019) <<
"SlaveBase::dispatch() has read error." << endl;
828 dispatch( cmd, data );
843 long windowId =
metaData(
"window-id").toLong();
844 long progressId =
metaData(
"progress-id").toLong();
845 unsigned long userTimestamp =
metaData(
"user-timestamp").toULong();
847 kdDebug(7019) <<
"SlaveBase::openPassDlg window-id=" << windowId <<
" progress-id=" << progressId << endl;
851 UIServer_stub uiserver(
"kio_uiserver",
"UIServer" );
853 uiserver.setJobVisible( progressId,
false );
855 TQDataStream stream(params, IO_WriteOnly);
857 if (
metaData(
"no-auth-prompt").lower() ==
"true")
858 stream << info << TQString(
"<NoAuthPrompt>") << windowId << s_seqNr << userTimestamp;
860 stream << info << errorMsg << windowId << s_seqNr << userTimestamp;
862 bool callOK = d->dcopClient->call(
"kded",
"kpasswdserver",
"queryAuthInfo(KIO::AuthInfo, TQString, long int, long int, unsigned long int)",
863 params, replyType, reply );
866 uiserver.setJobVisible( progressId,
true );
870 kdWarning(7019) <<
"Can't communicate with kded_kpasswdserver (openPassDlg)!" << endl;
874 if ( replyType ==
"KIO::AuthInfo" )
876 TQDataStream stream2( reply, IO_ReadOnly );
877 stream2 >> authResult >> s_seqNr;
881 kdError(7019) <<
"DCOP function queryAuthInfo(...) returns "
882 << replyType <<
", expected KIO::AuthInfo" << endl;
891 kdDebug(7019) <<
"SlaveBase::openPassDlg: username=" << info.
username << endl;
892 kdDebug(7019) <<
"SlaveBase::openPassDlg: password=[hidden]" << endl;
898 const TQString &buttonYes,
const TQString &buttonNo )
900 return messageBox( text, type, caption, buttonYes, buttonNo, TQString::null );
904 const TQString &buttonYes,
const TQString &buttonNo,
const TQString &dontAskAgainName )
906 kdDebug(7019) <<
"messageBox " << type <<
" " << text <<
" - " << caption << buttonYes << buttonNo << endl;
907 KIO_DATA << (TQ_INT32)type << text << caption << buttonYes << buttonNo << dontAskAgainName;
908 m_pConnection->
send( INF_MESSAGEBOX, data );
911 TQDataStream stream( data, IO_ReadOnly );
914 kdDebug(7019) <<
"got messagebox answer" << answer << endl;
922 kdDebug(7019) <<
"SlaveBase::canResume offset=" <<
KIO::number(offset) << endl;
923 d->needSendCanResume =
false;
924 KIO_DATA << KIO_FILESIZE_T(offset);
925 m_pConnection->
send( MSG_RESUME, data );
929 if (
waitForAnswer( CMD_RESUMEANSWER, CMD_NONE, data, &cmd ) != -1 )
931 kdDebug(7019) <<
"SlaveBase::canResume returning " << (cmd == CMD_RESUMEANSWER) << endl;
932 return cmd == CMD_RESUMEANSWER;
947 result = m_pConnection->
read( &cmd, data );
950 kdDebug(7019) <<
"SlaveBase::waitForAnswer has read error." << endl;
953 if ( cmd == expected1 || cmd == expected2 )
955 if ( pCmd ) *pCmd = cmd;
958 if ( isSubCommand(cmd) )
960 dispatch( cmd, data );
964 kdWarning() <<
"Got cmd " << cmd <<
" while waiting for an answer!" << endl;
980 d->timeout = time(0)+(time_t)timeout;
981 else if (timeout == 0)
986 d->timeoutData =
data;
989 void SlaveBase::dispatch(
int command,
const TQByteArray &data )
991 TQDataStream stream( data, IO_ReadOnly );
1001 TQString host, user;
1002 stream >> host >> i >> user >> passwd;
1003 setHost( host, i, user, passwd );
1009 case CMD_DISCONNECT:
1012 case CMD_SLAVE_STATUS:
1015 case CMD_SLAVE_CONNECT:
1018 TQString app_socket;
1019 TQDataStream stream( data, IO_ReadOnly);
1020 stream >> app_socket;
1021 appconn->
send( MSG_SLAVE_ACK );
1023 mConnectedToApp =
true;
1026 case CMD_SLAVE_HOLD:
1029 TQDataStream stream( data, IO_ReadOnly);
1034 mConnectedToApp =
false;
1038 case CMD_REPARSECONFIGURATION:
1042 stream >> d->configData;
1043 #ifdef Q_OS_UNIX //TODO: not yet available on WIN32
1044 KSocks::setConfig(d->config);
1046 delete d->remotefile;
1057 TQ_INT8 iOverwrite, iResume;
1058 stream >> url >> iOverwrite >> iResume >> permissions;
1059 bool overwrite = ( iOverwrite != 0 );
1060 bool resume = ( iResume != 0 );
1065 d->needSendCanResume =
true ;
1067 put( url, permissions, overwrite, resume);
1089 stream >> url >> url2 >> iOverwrite;
1090 bool overwrite = (iOverwrite != 0);
1091 rename( url, url2, overwrite );
1097 stream >> target >> url >> iOverwrite;
1098 bool overwrite = (iOverwrite != 0);
1099 symlink( target, url, overwrite );
1106 stream >> url >> url2 >> permissions >> iOverwrite;
1107 bool overwrite = (iOverwrite != 0);
1108 copy( url, url2, permissions, overwrite );
1113 stream >> url >> isFile;
1114 del( url, isFile != 0);
1125 stream >> mIncomingMetaData;
1132 fprintf(stderr,
"Got unexpected CMD_NONE!\n");
1146 if( !url.isValid() )
1147 return TQString::null;
1150 TQString key = url.protocol();
1153 int port = url.port();
1168 int success = client.ping();
1171 success = client.startServer();
1174 kdDebug(7019) <<
"Cannot start a new deamon!!" << endl;
1177 kdDebug(7019) <<
"Sucessfully started new cache deamon!!" << endl;
1187 TQCString replyType;
1191 long windowId =
metaData(
"window-id").toLong();
1192 unsigned long userTimestamp =
metaData(
"user-timestamp").toULong();
1194 kdDebug(7019) <<
"SlaveBase::checkCachedAuthInfo window = " << windowId <<
" url = " << info.
url.url() << endl;
1198 TQDataStream stream(params, IO_WriteOnly);
1199 stream << info << windowId << userTimestamp;
1201 if ( !d->dcopClient->call(
"kded",
"kpasswdserver",
"checkAuthInfo(KIO::AuthInfo, long int, unsigned long int)",
1202 params, replyType, reply ) )
1204 kdWarning(7019) <<
"Can't communicate with kded_kpasswdserver (checkCachedAuthentication)!" << endl;
1208 if ( replyType ==
"KIO::AuthInfo" )
1210 TQDataStream stream2( reply, IO_ReadOnly );
1211 stream2 >> authResult;
1215 kdError(7019) <<
"DCOP function checkAuthInfo(...) returns "
1216 << replyType <<
", expected KIO::AuthInfo" << endl;
1231 long windowId =
metaData(
"window-id").toLong();
1235 TQDataStream stream(params, IO_WriteOnly);
1236 stream << info << windowId;
1238 d->dcopClient->send(
"kded",
"kpasswdserver",
"addAuthInfo(KIO::AuthInfo, long int)", params );
1246 TQString tmp =
metaData(
"ConnectTimeout");
1247 int result = tmp.toInt(&ok);
1250 return DEFAULT_CONNECT_TIMEOUT;
1256 TQString tmp =
metaData(
"ProxyConnectTimeout");
1257 int result = tmp.toInt(&ok);
1260 return DEFAULT_PROXY_CONNECT_TIMEOUT;
1267 TQString tmp =
metaData(
"ResponseTimeout");
1268 int result = tmp.toInt(&ok);
1271 return DEFAULT_RESPONSE_TIMEOUT;
1278 TQString tmp =
metaData(
"ReadTimeout");
1279 int result = tmp.toInt(&ok);
1282 return DEFAULT_READ_TIMEOUT;
1287 return d->wasKilled;
1295 void SlaveBase::virtual_hook(
int,
void* )