File indexing completed on 2023-09-24 04:08:43
0001 /* 0002 This file is part of the KDE libraries 0003 SPDX-FileCopyrightText: 2000 David Faure <faure@kde.org> 0004 0005 SPDX-License-Identifier: LGPL-2.0-only 0006 */ 0007 0008 #include "slaveinterface.h" 0009 #include "slaveinterface_p.h" 0010 #include "usernotificationhandler_p.h" 0011 0012 #include "commands_p.h" 0013 #include "connection_p.h" 0014 #include "hostinfo.h" 0015 #include "workerbase.h" 0016 #include <KLocalizedString> 0017 #include <signal.h> 0018 #include <time.h> 0019 0020 #include <QDataStream> 0021 #include <QDateTime> 0022 #include <QDebug> 0023 0024 using namespace KIO; 0025 0026 Q_GLOBAL_STATIC(UserNotificationHandler, globalUserNotificationHandler) 0027 0028 SlaveInterface::SlaveInterface(SlaveInterfacePrivate &dd, QObject *parent) 0029 : QObject(parent) 0030 , d_ptr(&dd) 0031 { 0032 connect(&d_ptr->speed_timer, &QTimer::timeout, this, &SlaveInterface::calcSpeed); 0033 } 0034 0035 SlaveInterface::~SlaveInterface() 0036 { 0037 // Note: no Debug() here (scheduler is deleted very late) 0038 0039 delete d_ptr; 0040 } 0041 0042 #if KIOCORE_BUILD_DEPRECATED_SINCE(5, 0) 0043 void SlaveInterface::setConnection(Connection *connection) 0044 { 0045 Q_D(SlaveInterface); 0046 d->connection = connection; 0047 } 0048 #endif 0049 0050 #if KIOCORE_BUILD_DEPRECATED_SINCE(5, 0) 0051 Connection *SlaveInterface::connection() const 0052 { 0053 const Q_D(SlaveInterface); 0054 return d->connection; 0055 } 0056 #endif 0057 0058 static KIO::filesize_t readFilesize_t(QDataStream &stream) 0059 { 0060 KIO::filesize_t result; 0061 stream >> result; 0062 return result; 0063 } 0064 0065 bool SlaveInterface::dispatch() 0066 { 0067 Q_D(SlaveInterface); 0068 Q_ASSERT(d->connection); 0069 0070 int cmd; 0071 QByteArray data; 0072 0073 int ret = d->connection->read(&cmd, data); 0074 if (ret == -1) { 0075 return false; 0076 } 0077 0078 return dispatch(cmd, data); 0079 } 0080 0081 void SlaveInterface::calcSpeed() 0082 { 0083 Q_D(SlaveInterface); 0084 if (d->slave_calcs_speed || !d->connection->isConnected()) { // killing a job results in disconnection but the timer never stops 0085 d->speed_timer.stop(); 0086 return; 0087 } 0088 0089 const qint64 currentTime = QDateTime::currentMSecsSinceEpoch(); 0090 const qint64 diff = currentTime - d->start_time; 0091 if (diff - d->last_time >= 900) { 0092 d->last_time = diff; 0093 if (d->nums == max_nums) { 0094 // let's hope gcc can optimize that well enough 0095 // otherwise I'd try memcpy :) 0096 for (unsigned int i = 1; i < max_nums; ++i) { 0097 d->times[i - 1] = d->times[i]; 0098 d->sizes[i - 1] = d->sizes[i]; 0099 } 0100 d->nums--; 0101 } 0102 d->times[d->nums] = diff; 0103 d->sizes[d->nums++] = d->filesize - d->offset; 0104 0105 KIO::filesize_t lspeed = 1000 * (d->sizes[d->nums - 1] - d->sizes[0]) / (d->times[d->nums - 1] - d->times[0]); 0106 0107 // qDebug() << (long)d->filesize << diff 0108 // << long(d->sizes[d->nums-1] - d->sizes[0]) 0109 // << d->times[d->nums-1] - d->times[0] 0110 // << long(lspeed) << double(d->filesize) / diff 0111 // << convertSize(lspeed) 0112 // << convertSize(long(double(d->filesize) / diff) * 1000); 0113 0114 if (!lspeed) { 0115 d->nums = 1; 0116 d->times[0] = diff; 0117 d->sizes[0] = d->filesize - d->offset; 0118 } 0119 Q_EMIT speed(lspeed); 0120 } 0121 } 0122 0123 bool SlaveInterface::dispatch(int _cmd, const QByteArray &rawdata) 0124 { 0125 Q_D(SlaveInterface); 0126 // qDebug() << "dispatch " << _cmd; 0127 0128 QDataStream stream(rawdata); 0129 0130 QString str1; 0131 qint32 i; 0132 qint8 b; 0133 quint32 ul; 0134 0135 switch (_cmd) { 0136 case MSG_DATA: 0137 Q_EMIT data(rawdata); 0138 break; 0139 case MSG_DATA_REQ: 0140 Q_EMIT dataReq(); 0141 break; 0142 case MSG_OPENED: 0143 Q_EMIT open(); 0144 break; 0145 case MSG_FINISHED: 0146 // qDebug() << "Finished [this = " << this << "]"; 0147 d->offset = 0; 0148 d->speed_timer.stop(); 0149 Q_EMIT finished(); 0150 break; 0151 case MSG_STAT_ENTRY: { 0152 UDSEntry entry; 0153 stream >> entry; 0154 Q_EMIT statEntry(entry); 0155 break; 0156 } 0157 case MSG_LIST_ENTRIES: { 0158 UDSEntryList list; 0159 UDSEntry entry; 0160 0161 while (!stream.atEnd()) { 0162 stream >> entry; 0163 list.append(entry); 0164 } 0165 0166 Q_EMIT listEntries(list); 0167 break; 0168 } 0169 case MSG_RESUME: { // From the put job 0170 d->offset = readFilesize_t(stream); 0171 Q_EMIT canResume(d->offset); 0172 break; 0173 } 0174 case MSG_CANRESUME: // From the get job 0175 d->filesize = d->offset; 0176 Q_EMIT canResume(0); // the arg doesn't matter 0177 break; 0178 case MSG_ERROR: 0179 stream >> i >> str1; 0180 // qDebug() << "error " << i << " " << str1; 0181 Q_EMIT error(i, str1); 0182 break; 0183 #if KIOCORE_BUILD_DEPRECATED_SINCE(5, 45) 0184 case MSG_SLAVE_STATUS: 0185 #endif 0186 case MSG_SLAVE_STATUS_V2: { 0187 qint64 pid; 0188 QByteArray protocol; 0189 stream >> pid >> protocol >> str1 >> b; 0190 Q_EMIT slaveStatus(pid, protocol, str1, (b != 0)); 0191 break; 0192 } 0193 case MSG_CONNECTED: 0194 Q_EMIT connected(); 0195 break; 0196 case MSG_WRITTEN: { 0197 KIO::filesize_t size = readFilesize_t(stream); 0198 Q_EMIT written(size); 0199 break; 0200 } 0201 case INF_TOTAL_SIZE: { 0202 KIO::filesize_t size = readFilesize_t(stream); 0203 d->start_time = QDateTime::currentMSecsSinceEpoch(); 0204 d->last_time = 0; 0205 d->filesize = d->offset; 0206 d->sizes[0] = d->filesize - d->offset; 0207 d->times[0] = 0; 0208 d->nums = 1; 0209 d->speed_timer.start(1000); 0210 d->slave_calcs_speed = false; 0211 Q_EMIT totalSize(size); 0212 break; 0213 } 0214 case INF_PROCESSED_SIZE: { 0215 KIO::filesize_t size = readFilesize_t(stream); 0216 Q_EMIT processedSize(size); 0217 d->filesize = size; 0218 break; 0219 } 0220 case INF_POSITION: { 0221 KIO::filesize_t pos = readFilesize_t(stream); 0222 Q_EMIT position(pos); 0223 break; 0224 } 0225 case INF_TRUNCATED: { 0226 KIO::filesize_t length = readFilesize_t(stream); 0227 Q_EMIT truncated(length); 0228 break; 0229 } 0230 case INF_SPEED: 0231 stream >> ul; 0232 d->slave_calcs_speed = true; 0233 d->speed_timer.stop(); 0234 Q_EMIT speed(ul); 0235 break; 0236 #if KIOCORE_BUILD_DEPRECATED_SINCE(3, 0) 0237 case INF_GETTING_FILE: 0238 break; 0239 #endif 0240 case INF_ERROR_PAGE: 0241 Q_EMIT errorPage(); 0242 break; 0243 case INF_REDIRECTION: { 0244 QUrl url; 0245 stream >> url; 0246 Q_EMIT redirection(url); 0247 break; 0248 } 0249 case INF_MIME_TYPE: 0250 stream >> str1; 0251 Q_EMIT mimeType(str1); 0252 if (!d->connection->suspended()) { 0253 d->connection->sendnow(CMD_NONE, QByteArray()); 0254 } 0255 break; 0256 case INF_WARNING: 0257 stream >> str1; 0258 Q_EMIT warning(str1); 0259 break; 0260 case INF_MESSAGEBOX: { 0261 // qDebug() << "needs a msg box"; 0262 QString text; 0263 QString title; 0264 QString primaryActionText; 0265 QString secondaryActionText; 0266 QString dontAskAgainName; 0267 int type; 0268 stream >> type >> text >> title >> primaryActionText >> secondaryActionText; 0269 if (stream.atEnd()) { 0270 messageBox(type, text, title, primaryActionText, secondaryActionText); 0271 } else { 0272 stream >> dontAskAgainName; 0273 messageBox(type, text, title, primaryActionText, secondaryActionText, dontAskAgainName); 0274 } 0275 break; 0276 } 0277 case INF_INFOMESSAGE: { 0278 QString msg; 0279 stream >> msg; 0280 Q_EMIT infoMessage(msg); 0281 break; 0282 } 0283 case INF_META_DATA: { 0284 MetaData m; 0285 stream >> m; 0286 if (m.contains(QLatin1String("ssl_in_use"))) { 0287 const QLatin1String ssl_("ssl_"); 0288 const MetaData &constM = m; 0289 for (MetaData::ConstIterator it = constM.lowerBound(ssl_); it != constM.constEnd(); ++it) { 0290 if (it.key().startsWith(ssl_)) { 0291 d->sslMetaData.insert(it.key(), it.value()); 0292 } else { 0293 // we're past the ssl_* entries; remember that QMap is ordered. 0294 break; 0295 } 0296 } 0297 } else if (auto it = m.constFind(QStringLiteral("privilege_conf_details")); it != m.cend()) { 0298 // see WORKER_MESSAGEBOX_DETAILS_HACK 0299 d->messageBoxDetails = it.value(); 0300 } 0301 Q_EMIT metaData(m); 0302 break; 0303 } 0304 case MSG_NET_REQUEST: { 0305 QString host; 0306 QString slaveid; 0307 stream >> host >> slaveid; 0308 requestNetwork(host, slaveid); 0309 break; 0310 } 0311 case MSG_NET_DROP: { 0312 QString host; 0313 QString slaveid; 0314 stream >> host >> slaveid; 0315 dropNetwork(host, slaveid); 0316 break; 0317 } 0318 case MSG_NEED_SUBURL_DATA: { 0319 Q_EMIT needSubUrlData(); 0320 break; 0321 } 0322 case MSG_HOST_INFO_REQ: { 0323 QString hostName; 0324 stream >> hostName; 0325 HostInfo::lookupHost(hostName, this, SLOT(slotHostInfo(QHostInfo))); 0326 break; 0327 } 0328 case MSG_PRIVILEGE_EXEC: 0329 Q_EMIT privilegeOperationRequested(); 0330 break; 0331 default: 0332 qCWarning(KIO_CORE) << "Worker sends unknown command (" << _cmd << "), dropping worker."; 0333 return false; 0334 } 0335 return true; 0336 } 0337 0338 void SlaveInterface::setOffset(KIO::filesize_t o) 0339 { 0340 Q_D(SlaveInterface); 0341 d->offset = o; 0342 } 0343 0344 KIO::filesize_t SlaveInterface::offset() const 0345 { 0346 const Q_D(SlaveInterface); 0347 return d->offset; 0348 } 0349 0350 void SlaveInterface::requestNetwork(const QString &host, const QString &slaveid) 0351 { 0352 Q_D(SlaveInterface); 0353 Q_UNUSED(host); 0354 Q_UNUSED(slaveid); 0355 // qDebug() << "requestNetwork " << host << slaveid; 0356 0357 // This is old stuff. We just always return true... 0358 0359 QByteArray packedArgs; 0360 QDataStream stream(&packedArgs, QIODevice::WriteOnly); 0361 stream << true; 0362 d->connection->sendnow(INF_NETWORK_STATUS, packedArgs); 0363 } 0364 0365 void SlaveInterface::dropNetwork(const QString &host, const QString &slaveid) 0366 { 0367 Q_UNUSED(host); 0368 Q_UNUSED(slaveid); 0369 // qDebug() << "dropNetwork " << host << slaveid; 0370 } 0371 0372 void SlaveInterface::sendResumeAnswer(bool resume) 0373 { 0374 Q_D(SlaveInterface); 0375 // qDebug() << "ok for resuming:" << resume; 0376 d->connection->sendnow(resume ? CMD_RESUMEANSWER : CMD_NONE, QByteArray()); 0377 } 0378 0379 void SlaveInterface::sendMessageBoxAnswer(int result) 0380 { 0381 Q_D(SlaveInterface); 0382 if (!d->connection) { 0383 return; 0384 } 0385 0386 if (d->connection->suspended()) { 0387 d->connection->resume(); 0388 } 0389 QByteArray packedArgs; 0390 QDataStream stream(&packedArgs, QIODevice::WriteOnly); 0391 stream << result; 0392 d->connection->sendnow(CMD_MESSAGEBOXANSWER, packedArgs); 0393 // qDebug() << "message box answer" << result; 0394 } 0395 0396 void SlaveInterface::messageBox(int type, const QString &text, const QString &title, const QString &primaryActionText, const QString &secondaryActionText) 0397 { 0398 messageBox(type, text, title, primaryActionText, secondaryActionText, QString()); 0399 } 0400 0401 void SlaveInterface::messageBox(int type, 0402 const QString &text, 0403 const QString &title, 0404 const QString &primaryActionText, 0405 const QString &secondaryActionText, 0406 const QString &dontAskAgainName) 0407 { 0408 Q_D(SlaveInterface); 0409 if (d->connection) { 0410 d->connection->suspend(); 0411 } 0412 0413 QHash<UserNotificationHandler::MessageBoxDataType, QVariant> data; 0414 data.insert(UserNotificationHandler::MSG_TEXT, text); 0415 data.insert(UserNotificationHandler::MSG_TITLE, title); 0416 data.insert(UserNotificationHandler::MSG_PRIMARYACTION_TEXT, primaryActionText); 0417 data.insert(UserNotificationHandler::MSG_SECONDARYACTION_TEXT, secondaryActionText); 0418 data.insert(UserNotificationHandler::MSG_DONT_ASK_AGAIN, dontAskAgainName); 0419 0420 // SMELL: the braindead way to support button icons 0421 // TODO: Fix this in KIO::WorkerBase. 0422 if (primaryActionText == i18n("&Details")) { 0423 data.insert(UserNotificationHandler::MSG_PRIMARYACTION_ICON, QLatin1String("help-about")); 0424 } else if (primaryActionText == i18n("&Forever")) { 0425 data.insert(UserNotificationHandler::MSG_PRIMARYACTION_ICON, QLatin1String("flag-green")); 0426 } else if (primaryActionText == i18n("C&ontinue Loading")) { 0427 data.insert(UserNotificationHandler::MSG_PRIMARYACTION_ICON, QLatin1String("arrow-right")); 0428 } 0429 0430 if (secondaryActionText == i18n("Co&ntinue")) { 0431 data.insert(UserNotificationHandler::MSG_SECONDARYACTION_ICON, QLatin1String("arrow-right")); 0432 } else if (secondaryActionText == i18n("&Current Session only")) { 0433 data.insert(UserNotificationHandler::MSG_SECONDARYACTION_ICON, QLatin1String("chronometer")); 0434 } 0435 0436 if (type == KIO::WorkerBase::SSLMessageBox) { 0437 data.insert(UserNotificationHandler::MSG_META_DATA, d->sslMetaData.toVariant()); 0438 } else if (type == KIO::WorkerBase::WarningContinueCancelDetailed) { // see WORKER_MESSAGEBOX_DETAILS_HACK 0439 data.insert(UserNotificationHandler::MSG_DETAILS, d->messageBoxDetails); 0440 } 0441 0442 globalUserNotificationHandler()->requestMessageBox(this, type, data); 0443 } 0444 0445 void SlaveInterfacePrivate::slotHostInfo(const QHostInfo &info) 0446 { 0447 QByteArray data; 0448 QDataStream stream(&data, QIODevice::WriteOnly); 0449 stream << info.hostName() << info.addresses() << info.error() << info.errorString(); 0450 connection->send(CMD_HOST_INFO, data); 0451 } 0452 0453 #include "moc_slaveinterface.cpp"