Warning, file /network/kio-gopher/kio_gopher.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /*
0002  *   SPDX-FileCopyrightText: 2003-2008 Albert Astals Cid <aacid@kde.org>
0003  *
0004  *   SPDX-License-Identifier: GPL-2.0-or-later
0005  */
0006 
0007 #include "kio_gopher.h"
0008 
0009 #include <QBuffer>
0010 #include <QCoreApplication>
0011 #include <QFile>
0012 #include <QMimeDatabase>
0013 #include <QMimeType>
0014 #include <klocalizedstring.h>
0015 
0016 using namespace KIO;
0017 
0018 class KIOPluginForMetaData : public QObject
0019 {
0020     Q_OBJECT
0021     Q_PLUGIN_METADATA(IID "org.kde.kio.slave.gopher" FILE "gopher.json")
0022 };
0023 
0024 extern "C" {
0025 int Q_DECL_EXPORT kdemain(int argc, char **argv)
0026 {
0027     QCoreApplication app(argc, argv); // needed for QSocketNotifier
0028     app.setApplicationName(QLatin1String("kio_gopher"));
0029 
0030     if (argc != 4) {
0031         fprintf(stderr, "Usage: kio_gopher protocol domain-socket1 domain-socket2\n");
0032         exit(-1);
0033     }
0034 
0035     gopher slave(argv[2], argv[3]);
0036     slave.dispatchLoop();
0037     return 0;
0038 }
0039 }
0040 
0041 /* gopher */
0042 
0043 gopher::gopher(const QByteArray &pool_socket, const QByteArray &app_socket)
0044     : TCPWorkerBase("gopher", pool_socket, app_socket)
0045 {
0046 }
0047 
0048 KIO::WorkerResult gopher::get(const QUrl &url)
0049 {
0050     // gopher urls are
0051     // gopher://<host>:<port>/<gopher-path>
0052     //
0053     //  where <gopher-path> is one of
0054     //
0055     //  <gophertype><selector>
0056     //  <gophertype><selector>%09<search>
0057     //  <gophertype><selector>%09<search>%09<gopher+_string>
0058     int port;
0059     QChar type;
0060     QString path(url.path());
0061     QString query(url.query());
0062 
0063     // determine the type
0064     if (path != "/" && path != "")
0065         type = path[1];
0066     else
0067         type = '1';
0068 
0069     // determine the port
0070     if (url.port() > 0)
0071         port = url.port();
0072     else
0073         port = 70;
0074 
0075     // connect to the host
0076     if (auto result = connectToHost("gopher", url.host(), port); !result.success())
0077         return result;
0078 
0079     setBlocking(true);
0080 
0081     if (type == '7' && query.isNull()) {
0082         disconnectFromHost();
0083         handleSearch(url.host(), path, port);
0084     } else {
0085         int i, bytes;
0086         char aux[10240];
0087         QBuffer received;
0088         received.open(QIODevice::WriteOnly);
0089 
0090         infoMessage(i18n("Connecting to %1...", url.host()));
0091         infoMessage(i18n("%1 contacted. Retrieving data...", url.host()));
0092         bytes = 0;
0093 
0094         // send the selector
0095         path.remove(0, 2);
0096         write(path.toLatin1(), path.length());
0097         write(query.toLatin1(), query.length());
0098         write("\r\n", 2);
0099 
0100         // read the data
0101         while ((i = read(aux, 10240)) > 0) {
0102             bytes += i;
0103             received.write(aux, i);
0104             processedSize(bytes);
0105             infoMessage(i18n("Retrieved %1 bytes from %2...", bytes, url.host()));
0106         }
0107 
0108         if (type == '1' || type == '7')
0109             processDirectory(new QByteArray(received.buffer().data(), bytes + 1), url.host(), url.path());
0110         else {
0111             QMimeDatabase db;
0112             QMimeType result = db.mimeTypeForData(received.buffer());
0113             mimeType(result.name());
0114             data(received.buffer());
0115         }
0116         disconnectFromHost();
0117     }
0118     return KIO::WorkerResult::pass();
0119 }
0120 
0121 void gopher::processDirectory(QByteArray *received, const QString &host, const QString &path)
0122 {
0123     int i, remove;
0124     QString pathToShow;
0125     QByteArray show;
0126     QByteArray info;
0127     if (path == "/" || path == "/1")
0128         pathToShow = "";
0129     else
0130         pathToShow = path;
0131     mimeType("text/html");
0132     show.append("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\" \"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">\n");
0133     show.append("<html xmlns=\"http://www.w3.org/1999/xhtml\">\n");
0134     show.append("\t<head>\n");
0135     show.append("\t\t<title>");
0136     show.append(host.toUtf8());
0137     show.append(pathToShow.toUtf8());
0138     show.append("</title>\n");
0139     show.append("\t\t<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />\n");
0140     show.append("\t\t<style type=\"text/css\">\n");
0141     show.append("\t\t\t.info{ font-size : small; display : block; font-family : monospace; white-space : pre; margin-left : 18px; }\n");
0142     show.append("\t\t</style>\n");
0143     show.append("\t</head>\n");
0144     show.append("\t<body>\n");
0145     show.append("\t\t<h1>");
0146     show.append(host.toUtf8());
0147     show.append(pathToShow.toUtf8());
0148     show.append("</h1>\n");
0149     findLine(received, &i, &remove);
0150     while (i != -1) {
0151         processDirectoryLine(received->left(i), show, info);
0152         received->remove(0, i + remove);
0153         findLine(received, &i, &remove);
0154     }
0155     show.append("\t</body>\n");
0156     show.append("</html>\n");
0157     data(show);
0158     delete received;
0159 }
0160 
0161 void gopher::processDirectoryLine(const QByteArray &d, QByteArray &show, QByteArray &info)
0162 {
0163     // gopher <type><display><tab><selector><tab><server><tab><port><\r><\n>
0164     // gopher+ <type><display><tab><selector><tab><server><tab><port><tab><things><\r><\n>
0165     int i;
0166     QByteArray type, name, url, server, port;
0167     QByteArray data = d;
0168 
0169     type = data.left(1);
0170     data.remove(0, 1);
0171 
0172     i = data.indexOf("\t");
0173     name = data.left(i);
0174     data.remove(0, i + 1);
0175 
0176     i = data.indexOf("\t");
0177     url = data.left(i);
0178     data.remove(0, i + 1);
0179 
0180     i = data.indexOf("\t");
0181     server = data.left(i);
0182     data.remove(0, i + 1);
0183 
0184     port = parsePort(&data);
0185 
0186     if (type == "i") {
0187         if (!info.isEmpty())
0188             info.append("\n");
0189         info.append(name);
0190     } else {
0191         if (!info.isEmpty()) {
0192             show.append("\t\t<div class=\"info\">");
0193             show.append(info);
0194             show.append("</div>\n");
0195             info = "";
0196         }
0197         // it's the final line, ignore it
0198         if (type == ".")
0199             return;
0200         // those are the standard gopher types defined in the rfc
0201         //  0   Item is a file
0202         //  1   Item is a directory
0203         //  2   Item is a CSO phone-book server
0204         //  3   Error
0205         //  4   Item is a BinHexed Macintosh file.
0206         //  5   Item is DOS binary archive of some sort. Client must read until the TCP connection closes.  Beware.
0207         //  6   Item is a UNIX uuencoded file.
0208         //  7   Item is an Index-Search server.
0209         //  8   Item points to a text-based telnet session.
0210         //  9   Item is a binary file! Client must read until the TCP connection closes.  Beware.
0211         //  +   Item is a redundant server
0212         //  T   Item points to a text-based tn3270 session.
0213         //  g   Item is a GIF format graphics file.
0214         //  I   Item is some kind of image file.  Client decides how to display.
0215         show.append("\t\t\t<div>");
0216         // support the non-standard extension for URL to external sites
0217         // in this case, url begins with 'URL:'
0218         QByteArray finalUrl;
0219         QByteArray iconUrl;
0220         if (url.startsWith("URL:")) {
0221             finalUrl = url.mid(4);
0222             iconUrl = finalUrl;
0223         } else {
0224             finalUrl = "gopher://" + server;
0225             if (port != "70") {
0226                 finalUrl.append(":");
0227                 finalUrl.append(port);
0228             }
0229             finalUrl.append('/' + type + url);
0230             iconUrl = url;
0231         }
0232         show.append("\t\t\t\t<a href=\"");
0233         show.append(finalUrl);
0234         show.append("\">");
0235         addIcon(type, iconUrl, show);
0236         show.append(name);
0237         show.append("</a><br />\n");
0238         show.append("\t\t\t</div>");
0239     }
0240 }
0241 
0242 QByteArray gopher::parsePort(QByteArray *received)
0243 {
0244     int i = 0;
0245     QByteArray port;
0246     bool found = false;
0247     QChar c;
0248     while (!found && i < received->size()) {
0249         c = received->at(i);
0250         if (c.isDigit())
0251             i++;
0252         else
0253             found = true;
0254     }
0255     port = received->left(i);
0256     received->remove(0, i);
0257     return port;
0258 }
0259 
0260 void gopher::findLine(QByteArray *received, int *i, int *remove)
0261 {
0262     // it's not in the rfc but most servers don't follow the spec
0263     // find lines ending only in \n and in \r\n
0264     int aux, aux2;
0265     aux = received->indexOf("\r\n");
0266     aux2 = received->indexOf("\n");
0267 
0268     if (aux == -1) {
0269         *i = aux2;
0270         *remove = 1;
0271     } else {
0272         if (aux2 < aux) {
0273             *remove = 1;
0274             *i = aux2;
0275         } else {
0276             *remove = 2;
0277             *i = aux;
0278         }
0279     }
0280 }
0281 
0282 void gopher::handleSearch(const QString &host, const QString &path, int port)
0283 {
0284     QByteArray show;
0285     QString sPort;
0286     if (port != 70)
0287         sPort = ':' + QString::number(port);
0288     mimeType("text/html");
0289     show.append("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\" \"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">\n");
0290     show.append("<html xmlns=\"http://www.w3.org/1999/xhtml\">\n");
0291     show.append("\t<head>\n");
0292     show.append("\t\t<title>");
0293     show.append(host.toUtf8());
0294     show.append(path.toUtf8());
0295     show.append("</title>\n");
0296     show.append("\t\t<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />\n");
0297     show.append("\t\t<script type=\"text/javascript\">\n");
0298     show.append("\t\t\tfunction search()\n");
0299     show.append("\t\t\t{\n");
0300     show.append("\t\t\t\tdocument.location = 'gopher://");
0301     show.append(host.toUtf8());
0302     show.append(sPort.toUtf8());
0303     show.append(path.toUtf8());
0304     show.append("?' + document.getElementById('what').value;\n");
0305     show.append("\t\t\t}\n");
0306     show.append("\t\t</script>\n");
0307     show.append("\t</head>\n");
0308     show.append("\t<body>\n");
0309     show.append("\t\t<h1>");
0310     show.append(host.toUtf8());
0311     show.append(path.toUtf8());
0312     show.append("</h1>\n");
0313     show.append("\t\t");
0314     show.append(i18n("Enter a search term:").toUtf8());
0315     show.append("<br />\n");
0316     show.append("\t\t<input id=\"what\" type=\"text\">\n");
0317     show.append("\t\t<input type=\"button\" value=\"");
0318     show.append(i18nc("Text on a search button, like at a search engine", "Search").toUtf8());
0319     show.append("\" onClick=\"search()\">\n");
0320     show.append("\t</body>\n");
0321     show.append("</html>\n");
0322     data(show);
0323 }
0324 
0325 void gopher::addIcon(const QString &type, const QByteArray &url, QByteArray &show)
0326 {
0327     QString icon;
0328     QMimeDatabase db;
0329     if (type == "1")
0330         icon = "inode-directory";
0331     else if (type == "3")
0332         icon = "dialog-error";
0333     else if (type == "7")
0334         icon = "system-search";
0335     else if (type == "g")
0336         icon = "image-gif";
0337     else if (type == "I")
0338         icon = "image-x-generic";
0339     else {
0340         QMimeType mime = db.mimeTypeForFile(QUrl(url).path(), QMimeDatabase::MatchExtension);
0341         icon = mime.iconName();
0342     }
0343     QFile file(m_iconLoader.iconPath(icon, -16));
0344     file.open(QIODevice::ReadOnly);
0345     const QMimeType iconMime = db.mimeTypeForFile(file.fileName(), QMimeDatabase::MatchExtension);
0346     QByteArray ba = file.readAll();
0347     show.append("<img width=\"16\" height=\"16\" src=\"data:");
0348     show.append(iconMime.name().toLatin1());
0349     show.append(";base64,");
0350     show.append(ba.toBase64());
0351     show.append("\" /> ");
0352 }
0353 
0354 #include "kio_gopher.moc"