Permalink
Browse files

Added custom scheme handlers, i.e. a single one for listing local dir…

…ectories
  • Loading branch information...
jgehring authored and icefox committed Jul 3, 2009
1 parent 23d59c3 commit 9088ac610272235ab79824083e9ed58db8daf397
View
@@ -0,0 +1,75 @@
+<html>
+<head>
+<title>%1</title>
+<style>
+body {
+ padding: 3em 0em;
+ background: #eeeeee;
+}
+hr {
+ color: lightgray;
+ width: 100%;
+}
+img {
+ float: left;
+ opacity: .8;
+}
+#box {
+ background: white;
+ border: 1px solid lightgray;
+ width: 600px;
+ padding: 60px;
+ margin: auto;
+}
+h1 {
+ font-size: 130%;
+ font-weight: bold;
+ border-bottom: 1px solid lightgray;
+ margin-left: 48px;
+}
+h2 {
+ font-size: 100%;
+ font-weight: normal;
+ border-bottom: 1px solid lightgray;
+ margin-left: 48px;
+}
+table {
+ font-size: 80%;
+ padding-left: 48px;
+ padding-top: 3px;
+ padding-bottom: 3px;
+ margin: 5px 0;
+ width: 100%;
+}
+.modified {
+ text-align: left;
+ vertical-align: top;
+ white-space: nowrap;
+}
+.size {
+ text-align: right;
+ vertical-align: top;
+ white-space: nowrap;
+ padding-right: 8px;
+}
+.name {
+ text-align: left;
+ vertical-align: top;
+ white-space: pre-wrap;
+ width: 100%
+}
+%3
+#reloadButton {
+ padding-left: 48px;
+}
+</style>
+</head>
+<body>
+ <div id="box">
+ <h1>%2</h1>
+ <table>
+%4
+ </table>
+ </div>
+</body>
+</html>
View
@@ -1,5 +1,6 @@
<!DOCTYPE RCC><RCC version="1.0">
<qresource>
<file>notfound.html</file>
+ <file>dirlist.html</file>
</qresource>
</RCC>
@@ -65,6 +65,7 @@
#include "acceptlanguagedialog.h"
#include "browserapplication.h"
#include "browsermainwindow.h"
+#include "schemeaccesshandler.h"
#include "ui_passworddialog.h"
#include "ui_proxy.h"
@@ -128,6 +129,14 @@ NetworkAccessManager::NetworkAccessManager(QObject *parent)
connect(BrowserApplication::instance(), SIGNAL(privacyChanged(bool)),
this, SLOT(privacyChanged(bool)));
loadSettings();
+
+ // Register custom scheme handlers
+ setSchemeHandler(QLatin1String("file"), new FileAccessHandler(this));
+}
+
+void NetworkAccessManager::setSchemeHandler(const QString &scheme, SchemeAccessHandler *handler)
+{
+ m_schemeHandlers.insert(scheme, handler);
}
void NetworkAccessManager::loadSettings()
@@ -360,7 +369,16 @@ void NetworkAccessManager::sslErrors(QNetworkReply *reply, const QList<QSslError
QNetworkReply *NetworkAccessManager::createRequest(QNetworkAccessManager::Operation op, const QNetworkRequest &request, QIODevice *outgoingData)
{
- QNetworkReply *reply;
+ QNetworkReply *reply = NULL;
+
+ // Check if there is a valid handler registered for the requested URL scheme
+ if (m_schemeHandlers.contains(request.url().scheme())) {
+ reply = m_schemeHandlers[request.url().scheme()]->createRequest(op, request, outgoingData);
+ }
+ if (reply) {
+ return reply;
+ }
+
if (!m_acceptLanguage.isEmpty()) {
QNetworkRequest req = request;
req.setRawHeader("Accept-Language", m_acceptLanguage);
@@ -67,6 +67,8 @@
#include <qnetworkproxy.h>
#include <qsslconfiguration.h>
+class SchemeAccessHandler;
+
#if QT_VERSION >= 0x040500
class NetworkProxyFactory : public QNetworkProxyFactory
{
@@ -93,6 +95,7 @@ class NetworkAccessManager : public QNetworkAccessManager
public:
NetworkAccessManager(QObject *parent = 0);
+ void setSchemeHandler(const QString &scheme, SchemeAccessHandler *handler);
protected:
QNetworkReply *createRequest(QNetworkAccessManager::Operation op, const QNetworkRequest &request, QIODevice *outgoingData = 0);
@@ -114,6 +117,7 @@ private slots:
#endif
QByteArray m_acceptLanguage;
+ QHash<QString, SchemeAccessHandler *> m_schemeHandlers;
};
#endif // NETWORKACCESSMANAGER_H
View
@@ -0,0 +1,219 @@
+/*
+ * Copyright 2009 Jonas Gehring <jonas.gehring@boolsoft.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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA
+ */
+
+#include "schemeaccesshandler.h"
+
+#include <qapplication.h>
+#include <qcryptographichash.h>
+#include <qdatetime.h>
+#include <qdir.h>
+#include <qfileiconprovider.h>
+#include <qhash.h>
+#include <qstyle.h>
+#include <qtextstream.h>
+#include <qtimer.h>
+#include <qwebsettings.h>
+
+SchemeAccessHandler::SchemeAccessHandler(QObject *parent)
+ : QObject(parent)
+{
+}
+
+
+FileAccessHandler::FileAccessHandler(QObject *parent)
+ : SchemeAccessHandler(parent)
+{
+}
+
+QNetworkReply *FileAccessHandler::createRequest(QNetworkAccessManager::Operation op, const QNetworkRequest &request, QIODevice *outgoingData)
+{
+ Q_UNUSED(outgoingData);
+
+ switch (op) {
+ case QNetworkAccessManager::GetOperation:
+ break;
+ default:
+ return 0;
+ }
+
+ // This handler can only list directories yet, so pass anything
+ // else back to the manager
+ QString path = request.url().toLocalFile();
+ if (!QFileInfo(path).isDir()) {
+ return 0;
+ }
+
+ FileAccessReply *reply = new FileAccessReply(request, this);
+ return reply;
+}
+
+
+FileAccessReply::FileAccessReply(const QNetworkRequest &request, QObject *parent)
+ : QNetworkReply(parent)
+{
+ setOperation(QNetworkAccessManager::GetOperation);
+ setRequest(request);
+ setUrl(request.url());
+
+ buffer.open(QIODevice::ReadWrite);
+ setError(QNetworkReply::NoError, tr("No Error"));
+
+ QTimer::singleShot(0, this, SLOT(listDirectory()));
+
+ open(QIODevice::ReadOnly);
+}
+
+FileAccessReply::~FileAccessReply()
+{
+ close();
+}
+
+qint64 FileAccessReply::bytesAvailable() const
+{
+ return buffer.bytesAvailable() + QNetworkReply::bytesAvailable();
+}
+
+void FileAccessReply::close()
+{
+ buffer.close();
+}
+
+static QString cssLinkClass(const QIcon &icon, int size = 32)
+{
+ // The CSS class generation is a bit tricky, because QIcon/QPixmap's
+ // cacheKey() returns the different values for the same icons on my Windows
+ // box (tested with XP). Thus, the checksum of the actual text of the CSS class
+ // is used for the class name.
+ QString data = QLatin1String("a.%3 {\n\
+ padding-left: %1px;\n\
+ background: transparent url(data:image/png;base64,%2) no-repeat center left;\n\
+ font-weight: bold;\n\
+}\n");
+ QPixmap pixmap = icon.pixmap(QSize(size, size));
+ QBuffer imageBuffer;
+ imageBuffer.open(QBuffer::ReadWrite);
+ if (!pixmap.save(&imageBuffer, "PNG")) {
+ // If an error occured, write a blank pixmap
+ pixmap = QPixmap(size, size);
+ pixmap.fill(Qt::transparent);
+ imageBuffer.buffer().clear();
+ pixmap.save(&imageBuffer, "PNG");
+ }
+ return data.arg(size+4).arg(QLatin1String(imageBuffer.buffer().toBase64()));
+}
+
+void FileAccessReply::listDirectory()
+{
+ QDir dir(url().toLocalFile());
+ if (!dir.exists()) {
+ setError(QNetworkReply::ContentNotFoundError, tr("Error opening: %1: No such file or directory").arg(dir.absolutePath()));
+ emit error(QNetworkReply::ContentNotFoundError);
+ emit finished();
+ return;
+ }
+ if (!dir.isReadable()) {
+ setError(QNetworkReply::ContentAccessDenied, tr("Unable to read %1").arg(dir.absolutePath()));
+ emit error(QNetworkReply::ContentAccessDenied);
+ emit finished();
+ return;
+ }
+
+ // Format a html page for the directory contents
+ QFile dirlistFile(QLatin1String(":/dirlist.html"));
+ if (!dirlistFile.open(QIODevice::ReadOnly))
+ return;
+ QString html = QLatin1String(dirlistFile.readAll());
+ html = html.arg(dir.absolutePath(), tr("Contents of %1").arg(dir.absolutePath()));
+
+ // Templates for the listing
+ QString link = QLatin1String("<a class=\"%1\" href=\"%2\">%3</a>");
+ QString row = QLatin1String("<tr> <td class=\"name\">%1</td> <td class=\"size\">%2</td> <td class=\"modified\">%3</td> </tr>\n");
+
+ QFileIconProvider iconProvider;
+ QHash<QString, bool> existingClasses;
+ int iconSize = QWebSettings::globalSettings()->fontSize(QWebSettings::DefaultFontSize);
+ QFileInfoList list = dir.entryInfoList(QDir::AllEntries | QDir::Hidden, QDir::Name | QDir::DirsFirst);
+ QString dirlist, classes;
+
+ // Write link to parent directory first
+ if (!dir.isRoot()) {
+ QIcon icon = qApp->style()->standardIcon(QStyle::SP_FileDialogToParent);
+ classes += cssLinkClass(icon, iconSize).arg(QLatin1String("link_parent"));
+
+ QString addr = QString::fromUtf8(QUrl::fromLocalFile(QFileInfo(dir.absoluteFilePath(QLatin1String(".."))).canonicalFilePath()).toEncoded());
+ QString size, modified; // Empty by intention
+ dirlist += row.arg(link.arg(QLatin1String("link_parent")).arg(addr).arg(QLatin1String(".."))).arg(size).arg(modified);
+ }
+
+ for (int i = 0; i < list.count(); i++) {
+ // Skip '.' and '..'
+ if (list[i].fileName() == QLatin1String(".") || list[i].fileName() == QLatin1String("..")) {
+ continue;
+ }
+
+ // Fetch file icon and generate a corresponding CSS class if neccessary
+ QIcon icon = iconProvider.icon(list[i]);
+ QString cssClass = cssLinkClass(icon, iconSize);
+ QByteArray cssData = cssClass.toLatin1();
+ QString className = QString(QLatin1String("link_%1")).arg(QLatin1String(QCryptographicHash::hash(cssData, QCryptographicHash::Md4).toHex()));
+ if (!existingClasses.contains(className)) {
+ classes += cssClass.arg(className);
+ existingClasses.insert(className, true);
+ }
+
+ QString addr = QString::fromUtf8(QUrl::fromLocalFile(list[i].canonicalFilePath()).toEncoded());
+ QString size, modified;
+ if (list[i].fileName() != QLatin1String("..")) {
+ if (list[i].isFile())
+ size = tr("%1 KB").arg(QString::number(list[i].size()/1024));
+ modified = list[i].lastModified().toString(Qt::SystemLocaleShortDate);
+ }
+
+ dirlist += row.arg(link.arg(className).arg(addr).arg(list[i].fileName())).arg(size).arg(modified);
+ }
+
+ html = html.arg(classes).arg(dirlist);
+
+ // Save result to buffer
+ QTextStream stream(&buffer);
+ stream << html;
+ stream.flush();
+ buffer.reset();
+
+ // Publish result
+ setHeader(QNetworkRequest::ContentTypeHeader, QByteArray("text/html"));
+ setHeader(QNetworkRequest::ContentLengthHeader, buffer.bytesAvailable());
+ setAttribute(QNetworkRequest::HttpStatusCodeAttribute, 200);
+ setAttribute(QNetworkRequest::HttpReasonPhraseAttribute, QByteArray("Ok"));
+ emit metaDataChanged();
+ emit downloadProgress(buffer.size(), buffer.size());
+ QNetworkReply::NetworkError errorCode = error();
+ if (errorCode != QNetworkReply::NoError) {
+ emit error(errorCode);
+ } else if (buffer.size() > 0) {
+ emit readyRead();
+ }
+
+ emit finished();
+}
+
+qint64 FileAccessReply::readData(char *data, qint64 maxSize)
+{
+ return buffer.read(data, maxSize);
+}
Oops, something went wrong.

0 comments on commit 9088ac6

Please sign in to comment.