File indexing completed on 2024-04-21 04:57:30
0001 // SPDX-License-Identifier: MIT 0002 0003 #include "info.h" 0004 0005 #include <stdio.h> 0006 #include <stdlib.h> 0007 #include <sys/stat.h> 0008 #ifdef Q_OS_WIN 0009 #include <process.h> 0010 #define getpid _getpid 0011 #define popen _popen 0012 #define pclose _pclose 0013 #else 0014 #include <unistd.h> // getpid() 0015 #endif 0016 0017 #include <QCoreApplication> 0018 #include <QFile> 0019 #include <QStandardPaths> 0020 #include <QUrl> 0021 0022 #include <KLocalizedString> 0023 #include <KShell> 0024 0025 using namespace KIO; 0026 0027 // Pseudo plugin class to embed meta data 0028 class KIOPluginForMetaData : public QObject 0029 { 0030 Q_OBJECT 0031 Q_PLUGIN_METADATA(IID "org.kde.kio.worker.info" FILE "info.json") 0032 }; 0033 0034 InfoProtocol::InfoProtocol(const QByteArray &pool, const QByteArray &app) 0035 : WorkerBase("info", pool, app) 0036 { 0037 qCDebug(LOG_KIO_INFO); 0038 0039 m_cssLocation = QStandardPaths::locate(QStandardPaths::GenericDataLocation, "kio_docfilter/kio_docfilter.css"); 0040 if (m_cssLocation.isEmpty()) 0041 m_missingFiles.append("kio_docfilter/kio_docfilter.css"); 0042 m_perl = QStandardPaths::findExecutable("perl"); 0043 if (m_perl.isEmpty()) 0044 m_missingFiles.append("perl"); 0045 m_infoScript = QStandardPaths::locate(QStandardPaths::GenericDataLocation, "kio_info/kde-info2html"); 0046 if (m_infoScript.isEmpty()) 0047 m_missingFiles.append("kio_info/kde-info2html"); 0048 m_infoConf = QStandardPaths::locate(QStandardPaths::GenericDataLocation, "kio_info/kde-info2html.conf"); 0049 if (m_infoConf.isEmpty()) 0050 m_missingFiles.append("kio_info/kde-info2html.conf"); 0051 0052 if (!m_missingFiles.isEmpty()) { 0053 qCCritical(LOG_KIO_INFO) << "Cannot locate files for HTML conversion," << qPrintable(m_missingFiles.join(' ')); 0054 } 0055 0056 qCDebug(LOG_KIO_INFO) << "done"; 0057 } 0058 0059 KIO::WorkerResult InfoProtocol::missingFilesReult() const 0060 { 0061 const QString errorStr = i18n( 0062 "Unable to locate files which are necessary to run this service:<br>%1<br>" 0063 "Please check your software installation.", 0064 m_missingFiles.join(' ')); 0065 return KIO::WorkerResult::fail(KIO::ERR_WORKER_DEFINED, errorStr); 0066 } 0067 0068 KIO::WorkerResult InfoProtocol::get(const QUrl &url) 0069 { 0070 qCDebug(LOG_KIO_INFO) << "URL" << url.toDisplayString() << "path" << url.path(); 0071 0072 if (!m_missingFiles.isEmpty()) { 0073 return missingFilesReult(); 0074 } 0075 0076 if (url.path() == "/") { 0077 QUrl newUrl("info:/dir"); 0078 redirection(newUrl); 0079 return KIO::WorkerResult::pass(); 0080 ; 0081 }; 0082 0083 // some people write info://autoconf instead of info:/autoconf 0084 if (!url.host().isEmpty()) { 0085 QUrl newURl(url); 0086 newURl.setPath(url.host() + url.path()); 0087 newURl.setHost(QString()); 0088 redirection(newURl); 0089 return KIO::WorkerResult::pass(); 0090 ; 0091 } 0092 0093 if (url.path().right(1) == "/") { 0094 // Trailing / are not supported, so we need to remove them. 0095 QUrl newUrl(url); 0096 QString newPath(url.path()); 0097 newPath.chop(1); 0098 newUrl.setPath(newPath); 0099 redirection(newUrl); 0100 return KIO::WorkerResult::pass(); 0101 ; 0102 } 0103 0104 // '<' in the path looks suspicious, someone is trying info:/dir/<script>alert('xss')</script> 0105 if (url.path().contains('<')) { 0106 return KIO::WorkerResult::fail(KIO::ERR_MALFORMED_URL, url.url()); 0107 } 0108 0109 mimeType("text/html"); 0110 // extract the path and node from url 0111 decodeURL(url); 0112 0113 QString cmd = KShell::quoteArg(m_perl); 0114 cmd += ' '; 0115 cmd += KShell::quoteArg(m_infoScript); 0116 cmd += ' '; 0117 cmd += KShell::quoteArg(m_infoConf); 0118 cmd += ' '; 0119 cmd += KShell::quoteArg(m_cssLocation); 0120 cmd += ' '; 0121 cmd += KShell::quoteArg(m_page); 0122 cmd += ' '; 0123 cmd += KShell::quoteArg(m_node); 0124 0125 qCDebug(LOG_KIO_INFO) << "cmd" << cmd; 0126 0127 FILE *file = popen(QFile::encodeName(cmd).constData(), "r"); 0128 if (!file) { 0129 qCDebug(LOG_KIO_INFO) << "popen failed"; 0130 return KIO::WorkerResult::fail(ERR_CANNOT_LAUNCH_PROCESS, cmd); 0131 } 0132 0133 char buffer[4096]; 0134 0135 bool empty = true; 0136 while (!feof(file)) { 0137 int n = fread(buffer, 1, sizeof(buffer), file); 0138 if (!n && feof(file) && empty) { 0139 return KIO::WorkerResult::fail(ERR_CANNOT_LAUNCH_PROCESS, cmd); 0140 } 0141 if (n < 0) { 0142 // ERROR 0143 qCWarning(LOG_KIO_INFO) << "read error!"; 0144 pclose(file); 0145 return KIO::WorkerResult::fail(); 0146 } 0147 0148 empty = false; 0149 data(QByteArray::fromRawData(buffer, n)); 0150 } 0151 0152 pclose(file); 0153 0154 qCDebug(LOG_KIO_INFO) << "done"; 0155 0156 return KIO::WorkerResult::pass(); 0157 } 0158 0159 KIO::WorkerResult InfoProtocol::mimetype(const QUrl & /* url */) 0160 { 0161 qCDebug(LOG_KIO_INFO); 0162 0163 if (!m_missingFiles.isEmpty()) { 0164 return missingFilesReult(); 0165 } 0166 0167 // to get rid of those "Open with" dialogs... 0168 mimeType("text/html"); 0169 0170 // finish action 0171 return KIO::WorkerResult::pass(); 0172 } 0173 0174 void InfoProtocol::decodeURL(const QUrl &url) 0175 { 0176 qCDebug(LOG_KIO_INFO) << url; 0177 0178 /* Notes: 0179 * 0180 * I cleaned up the URL decoding and chose not to support URLs in the 0181 * form "info:/usr/local/share/info/libc.info.gz" or similar which the 0182 * older code attempted (and failed, maybe it had worked once) to do. 0183 * 0184 * The reason is that an obvious use such as viewing a info file off your 0185 * infopath would work for the first page, but then all the links would be 0186 * wrong. Of course, one could change kde-info2html to make it work, but I don't 0187 * think it worthy, others are free to disagree and write the necessary code ;) 0188 * 0189 * luis pedro 0190 */ 0191 0192 if (url == QUrl("info:/browse_by_file?special=yes")) { 0193 m_page = "#special#"; 0194 m_node = "browse_by_file"; 0195 qCDebug(LOG_KIO_INFO) << "InfoProtocol::decodeURL - special - browse by file"; 0196 return; 0197 } 0198 0199 decodePath(url.path()); 0200 } 0201 0202 void InfoProtocol::decodePath(QString path) 0203 { 0204 qCDebug(LOG_KIO_INFO) << path; 0205 0206 m_page = "dir"; // default 0207 m_node = ""; 0208 0209 // remove leading slash 0210 if ('/' == path[0]) { 0211 path = path.mid(1); 0212 } 0213 // qCDebug(LOG_KIO_INFO) << "Path: " << path; 0214 0215 int slashPos = path.indexOf("/"); 0216 0217 if (slashPos < 0) { 0218 m_page = path; 0219 m_node = "Top"; 0220 return; 0221 } 0222 0223 m_page = path.left(slashPos); 0224 0225 // remove leading+trailing whitespace 0226 m_node = path.right(path.length() - slashPos - 1).trimmed(); 0227 0228 qCDebug(LOG_KIO_INFO) << "-> page" << m_page << "node" << m_node; 0229 } 0230 0231 // A minimalistic stat with only the file type 0232 // This seems to be enough for konqueror 0233 KIO::WorkerResult InfoProtocol::stat(const QUrl &) 0234 { 0235 if (!m_missingFiles.isEmpty()) { 0236 return missingFilesReult(); 0237 } 0238 0239 UDSEntry uds_entry; 0240 0241 #ifdef Q_OS_WIN 0242 // Regular file with rwx permission for all 0243 uds_entry.fastInsert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFREG); 0244 #else 0245 // Regular file with rwx permission for all 0246 uds_entry.fastInsert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFREG | S_IRWXU | S_IRWXG | S_IRWXO); 0247 #endif 0248 0249 statEntry(uds_entry); 0250 return KIO::WorkerResult::pass(); 0251 } 0252 0253 extern "C" { 0254 int Q_DECL_EXPORT kdemain(int argc, char **argv); 0255 } 0256 0257 int kdemain(int argc, char **argv) 0258 { 0259 QCoreApplication app(argc, argv); // needed for QSocketNotifier 0260 app.setApplicationName(QLatin1String("kio_info")); 0261 0262 qCDebug(LOG_KIO_INFO) << "kio_info starting" << getpid(); 0263 0264 if (argc != 4) { 0265 fprintf(stderr, "Usage: kio_info protocol domain-socket1 domain-socket2\n"); 0266 exit(-1); 0267 } 0268 0269 InfoProtocol worker(argv[2], argv[3]); 0270 worker.dispatchLoop(); 0271 0272 return 0; 0273 } 0274 0275 #include "info.moc"