You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdeio-gopher/src/tdeio_gopher.cpp

346 lines
9.2 KiB

/****************************************************************************
* Copyright (C) 2003-2008 by Albert Astals Cid *
* aacid@kde.org *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
****************************************************************************/
#include "tdeio_gopher.h"
#include <stdlib.h>
#include <kurl.h>
#include <kdebug.h>
#include <kmdcodec.h>
#include <kinstance.h>
#include <kdebug.h>
#include <tdelocale.h>
#include <kmimetype.h>
#include <ntqbuffer.h>
#include <ntqfile.h>
using namespace TDEIO;
extern "C"
{
int KDE_EXPORT kdemain( int argc, char **argv )
{
TDEInstance instance( "tdeio_gopher" );
if (argc != 4)
{
fprintf(stderr, "Usage: tdeio_gopher protocol domain-socket1 domain-socket2\n");
exit(-1);
}
GopherProtocol slave(argv[2], argv[3]);
slave.dispatchLoop();
return 0;
}
}
/* gopher */
GopherProtocol::GopherProtocol(const TQCString &pool_socket, const TQCString &app_socket)
: TCPSlaveBase(70, "gopher", pool_socket, app_socket)
{
}
void GopherProtocol::get(const KURL& url )
{
// gopher urls are
// gopher://<host>:<port>/<gopher-path>
//
// where <gopher-path> is one of
//
// <gophertype><selector>
// <gophertype><selector>%09<search>
// <gophertype><selector>%09<search>%09<gopher+_string>
int port;
TQChar type;
TQString path(url.path());
TQString query(url.query());
// determine the type
if (path != "/" && path != "") type = path[1];
else type = '1';
// determine the port
if (url.port() > 0) port = url.port();
else port = 70;
setBlockConnection(true);
// connect to the host
if (!connectToHost(url.host(), port)) return;
if (type == '7' && query.isNull())
{
closeDescriptor();
handleSearch(url.host(), path, port);
}
else
{
int i, bytes;
char aux[10240];
TQBuffer received;
received.open(IO_WriteOnly);
infoMessage(i18n("Connecting to %1...").arg(url.host()));
infoMessage(i18n("%1 contacted. Retrieving data...").arg(url.host()));
bytes = 0;
// send the selector
path.remove(0, 2);
write(path.latin1(), path.length());
write(query.latin1(), query.length());
write("\r\n", 2);
// read the data
while((i = read(aux, 10240)) > 0)
{
bytes += i;
received.writeBlock(aux, i);
processedSize(bytes);
infoMessage(i18n("Retrieved %1 bytes from %2...").arg(bytes).arg(url.host()));
}
if (type == '1' || type =='7')
{
processDirectory(received.buffer().data(), url.host(), url.path());
}
else
{
KMimeType::Ptr result = KMimeType::findByContent(received.buffer());
mimeType(result->name());
data(received.buffer());
}
closeDescriptor();
}
finished();
}
void GopherProtocol::processDirectory(const TQString &received_str, const TQString &host, const TQString &path)
{
TQString received(received_str);
TQString pathToShow;
if (path == "/" || path == "/1")
{
pathToShow = "";
}
else
{
pathToShow = path;
}
mimeType("text/html");
TQString info;
TQString show("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\" \"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">\n"
"<html xmlns=\"http://www.w3.org/1999/xhtml\">\n\t<head>\n\t\t<title>");
show += host.utf8();
show += pathToShow.utf8();
show += TQString("</title>\n\t\t<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />\n"
"\t\t<style type=\"text/css\">\n\t\t\t.info{ font-size : small; display : block; font-family : monospace; "
"white-space : pre; margin-left : 18px; }\n\t\t</style>\n\t</head>\n\t<body>\n\t\t<h1>");
show += host.utf8();
show += pathToShow.utf8();
show += "</h1>\n";
int i, remove;
findLine(received, &i, &remove);
while(i != -1)
{
processDirectoryLine(received.left(i), show, info);
received.remove(0, i + remove);
findLine(received, &i, &remove);
}
show += "\t</body>\n</html>\n";
TQByteArray showdata;
showdata.duplicate(show.utf8(), show.length());
data(showdata);
}
void GopherProtocol::processDirectoryLine(const TQString &d, TQString &show, TQString &info)
{
// gopher <type><display><tab><selector><tab><server><tab><port><\r><\n>
// gopher+ <type><display><tab><selector><tab><server><tab><port><tab><things><\r><\n>
int i;
TQString data(d);
TQString type = data.left(1);
data.remove(0, 1);
i = data.find("\t");
TQString name = data.left(i);
data.remove(0, i + 1);
i = data.find("\t");
TQString url = data.left(i);
data.remove(0, i + 1);
i = data.find("\t");
TQString server = data.left(i);
data.remove(0, i + 1);
TQString port = parsePort(data);
if (type == "i")
{
if (!info.isEmpty())
info.append("\n");
info.append(name);
}
else
{
if (!info.isEmpty())
{
show.append("\t\t<div class=\"info\">");
show.append(info);
show.append("</div>\n");
info = "";
}
// it's the final line, ignore it
if (type == ".") return;
// those are the standard gopher types defined in the rfc
// 0 Item is a file
// 1 Item is a directory
// 2 Item is a CSO phone-book server
// 3 Error
// 4 Item is a BinHexed Macintosh file.
// 5 Item is DOS binary archive of some sort. Client must read until the TCP connection closes. Beware.
// 6 Item is a UNIX uuencoded file.
// 7 Item is an Index-Search server.
// 8 Item points to a text-based telnet session.
// 9 Item is a binary file! Client must read until the TCP connection closes. Beware.
// + Item is a redundant server
// T Item points to a text-based tn3270 session.
// g Item is a GIF format graphics file.
// I Item is some kind of image file. Client decides how to display.
show.append("\t\t\t<div>");
// support the non-standard extension for URL to external sites
// in this case, url begins with 'URL:'
TQString finalUrl;
TQString iconUrl;
if (url.startsWith("URL:"))
{
finalUrl = url.mid(4);
iconUrl = finalUrl;
}
else
{
finalUrl = "gopher://" + server;
if (port != "70")
{
finalUrl.append(":");
finalUrl.append(port);
}
finalUrl.append('/' + type + url);
iconUrl = url;
}
show.append("\t\t\t\t<a href=\"");
show.append(finalUrl);
show.append("\">");
addIcon(type, iconUrl, show);
show.append(name);
show.append("</a><br />\n");
show.append("\t\t\t</div>");
}
}
TQString GopherProtocol::parsePort(TQString &received)
{
uint i = 0;
TQString port;
bool found = false;
TQChar c;
while (!found && i < received.length())
{
c = received[i];
if (c.isDigit())
i++;
else
found = true;
}
port = received.left(i);
received.remove(0, i);
return port;
}
void GopherProtocol::findLine(const TQString &received, int *i, int *remove)
{
// it's not in the rfc but most servers don't follow the spec
// find lines ending only in \n and in \r\n
int aux, aux2;
aux = received.find("\r\n");
aux2 = received.find("\n");
if (aux == -1)
{
*i = aux2;
*remove = 1;
}
else
{
if (aux2 < aux)
{
*remove = 1;
*i = aux2;
}
else
{
*remove = 2;
*i = aux;
}
}
}
void GopherProtocol::handleSearch(const TQString &host, const TQString &path, int port)
{
TQString sPort;
if (port != 70) sPort = ':' + TQString::number(port);
mimeType("text/html");
TQString show("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\" \"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">\n"
"<html xmlns=\"http://www.w3.org/1999/xhtml\">\n\t<head>\n\t\t<title>");
show += host.utf8();
show += path.utf8();
show += "</title>\n\t\t<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />\n"
"\t\t<script type=\"text/javascript\">\n\t\t\tfunction search()\n\t\t\t{\n\t\t\t\tdocument.location = 'gopher://";
show += host.utf8();
show += sPort.utf8();
show += path.utf8();
show += "?' + document.getElementById('what').value;\n\t\t\t}\n\t\t</script>\n\t</head>\n\t<body>\n\t\t<h1>";
show.append(host.utf8());
show.append(path.utf8());
show += "</h1>\n\t\t";
show += i18n("Enter a search term:").utf8();
show += "<br />\n\t\t<input id=\"what\" type=\"text\">\n\t\t<input type=\"button\" value=\"";
show += i18n("Text on a search button, like at a search engine", "Search").utf8();
show += "\" onClick=\"search()\">\n\t</body>\n</html>\n";
TQByteArray showdata;
showdata.duplicate(show.utf8(), show.length());
data(showdata);
}
void GopherProtocol::addIcon(const TQString &type, const TQString &url, TQString &show)
{
TQString icon;
if (type == "1") icon = "inode-directory.png";
else if (type == "3") icon = "dialog-error.png";
else if (type == "7") icon = "system-search.png";
else if (type == "g") icon = "image-gif.png";
else if (type == "I") icon = "image-x-generic.png";
else
{
KMimeType::Ptr mime = KMimeType::findByURL(KURL(url), 0, false, true);
icon = mime->icon(TQString::null, false);
}
TQFile file(m_iconLoader.iconPath(icon, -16));
file.open(IO_ReadOnly);
TQByteArray ba = file.readAll();
show.append("<img width=\"16\" height=\"16\" src=\"data:image/png;base64,");
show.append(KCodecs::base64Encode(ba));
show.append("\" /> ");
}