summaryrefslogtreecommitdiffstats
path: root/kpf/src
diff options
context:
space:
mode:
Diffstat (limited to 'kpf/src')
-rw-r--r--kpf/src/ActiveMonitor.cpp226
-rw-r--r--kpf/src/ActiveMonitor.h165
-rw-r--r--kpf/src/ActiveMonitorItem.cpp199
-rw-r--r--kpf/src/ActiveMonitorItem.h144
-rw-r--r--kpf/src/ActiveMonitorWindow.cpp90
-rw-r--r--kpf/src/ActiveMonitorWindow.h104
-rw-r--r--kpf/src/Applet.cpp488
-rw-r--r--kpf/src/Applet.h180
-rw-r--r--kpf/src/AppletItem.cpp385
-rw-r--r--kpf/src/AppletItem.h167
-rw-r--r--kpf/src/BandwidthGraph.cpp335
-rw-r--r--kpf/src/BandwidthGraph.h163
-rw-r--r--kpf/src/ByteRange.cpp176
-rw-r--r--kpf/src/ByteRange.h128
-rw-r--r--kpf/src/ConfigDialogPage.cpp318
-rw-r--r--kpf/src/ConfigDialogPage.h107
-rw-r--r--kpf/src/Defaults.cpp97
-rw-r--r--kpf/src/Defaults.h71
-rw-r--r--kpf/src/Defines.h33
-rw-r--r--kpf/src/DirSelectWidget.cpp115
-rw-r--r--kpf/src/DirSelectWidget.h66
-rw-r--r--kpf/src/DirectoryLister.cpp345
-rw-r--r--kpf/src/DirectoryLister.h72
-rw-r--r--kpf/src/ErrorMessageConfigDialog.cpp154
-rw-r--r--kpf/src/ErrorMessageConfigDialog.h88
-rw-r--r--kpf/src/Help.cpp62
-rw-r--r--kpf/src/Help.h44
-rw-r--r--kpf/src/KPFInterface.cpp130
-rw-r--r--kpf/src/KPFInterface.h68
-rw-r--r--kpf/src/Makefile.am89
-rw-r--r--kpf/src/PortValidator.cpp57
-rw-r--r--kpf/src/PortValidator.h47
-rw-r--r--kpf/src/PropertiesDialogPlugin.cpp890
-rw-r--r--kpf/src/PropertiesDialogPlugin.h82
-rw-r--r--kpf/src/Request.cpp377
-rw-r--r--kpf/src/Request.h252
-rw-r--r--kpf/src/Resource.cpp346
-rw-r--r--kpf/src/Resource.h149
-rw-r--r--kpf/src/Response.cpp194
-rw-r--r--kpf/src/Response.h102
-rw-r--r--kpf/src/RootValidator.cpp66
-rw-r--r--kpf/src/RootValidator.h47
-rw-r--r--kpf/src/Server.cpp1137
-rw-r--r--kpf/src/Server.h186
-rw-r--r--kpf/src/ServerPrivate.cpp54
-rw-r--r--kpf/src/ServerPrivate.h78
-rw-r--r--kpf/src/ServerSocket.cpp46
-rw-r--r--kpf/src/ServerSocket.h47
-rw-r--r--kpf/src/ServerWizard.cpp410
-rw-r--r--kpf/src/ServerWizard.h90
-rw-r--r--kpf/src/SingleServerConfigDialog.cpp98
-rw-r--r--kpf/src/SingleServerConfigDialog.h68
-rw-r--r--kpf/src/StartingKPFDialog.cpp139
-rw-r--r--kpf/src/StartingKPFDialog.h62
-rw-r--r--kpf/src/System.cpp71
-rw-r--r--kpf/src/System.h35
-rw-r--r--kpf/src/Utils.cpp414
-rw-r--r--kpf/src/Utils.h113
-rw-r--r--kpf/src/WebServer.cpp644
-rw-r--r--kpf/src/WebServer.h346
-rw-r--r--kpf/src/WebServerManager.cpp295
-rw-r--r--kpf/src/WebServerManager.h173
-rw-r--r--kpf/src/WebServerSocket.cpp44
-rw-r--r--kpf/src/WebServerSocket.h51
64 files changed, 12019 insertions, 0 deletions
diff --git a/kpf/src/ActiveMonitor.cpp b/kpf/src/ActiveMonitor.cpp
new file mode 100644
index 00000000..8bd0c34a
--- /dev/null
+++ b/kpf/src/ActiveMonitor.cpp
@@ -0,0 +1,226 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include <qlayout.h>
+
+#include <kiconloader.h>
+#include <klocale.h>
+
+#include "Defines.h"
+#include "ActiveMonitorItem.h"
+#include "ActiveMonitor.h"
+#include "WebServer.h"
+#include "Server.h"
+#include "Utils.h"
+
+namespace KPF
+{
+ ActiveMonitor::ActiveMonitor
+ (
+ WebServer * server,
+ QWidget * parent,
+ const char * name
+ )
+ : QWidget (parent, name),
+ server_ (server)
+ {
+ view_ = new QListView(this);
+
+ view_->setAllColumnsShowFocus(true);
+ view_->setSelectionMode(QListView::Extended);
+
+ view_->addColumn(i18n("Status"));
+ view_->addColumn(i18n("Progress"));
+ view_->addColumn(i18n("File Size"));
+ view_->addColumn(i18n("Bytes Sent"));
+ view_->addColumn(i18n("Response"));
+ view_->addColumn(i18n("Resource"));
+ view_->addColumn(i18n("Host"));
+
+ QVBoxLayout * layout = new QVBoxLayout(this);
+
+ layout->addWidget(view_);
+
+ connect
+ (
+ view_,
+ SIGNAL(selectionChanged()),
+ SLOT(slotSelectionChanged())
+ );
+
+ connect
+ (
+ server_,
+ SIGNAL(connection(Server *)),
+ SLOT(slotConnection(Server *))
+ );
+
+ connect
+ (
+ server_,
+ SIGNAL(output(Server *, ulong)),
+ SLOT(slotOutput(Server *, ulong))
+ );
+
+ connect(server_, SIGNAL(finished(Server *)), SLOT(slotFinished(Server *)));
+ connect(server_, SIGNAL(request(Server *)), SLOT(slotRequest(Server *)));
+ connect(server_, SIGNAL(response(Server *)), SLOT(slotResponse(Server *)));
+
+ connect(&cullTimer_, SIGNAL(timeout()), SLOT(slotCull()));
+
+ cullTimer_.start(1000);
+
+ // Tell whoever cares about our selection status.
+ slotSelectionChanged();
+ }
+
+ ActiveMonitor::~ActiveMonitor()
+ {
+ }
+
+ void
+ ActiveMonitor::slotConnection(Server * s)
+ {
+ ActiveMonitorItem * i = new ActiveMonitorItem(s, view_);
+ itemMap_[s] = i;
+ }
+
+ void
+ ActiveMonitor::slotOutput(Server * s, ulong l)
+ {
+ ActiveMonitorItem * i = itemMap_[s];
+
+ if (0 != i)
+ i->output(l);
+ }
+
+ void
+ ActiveMonitor::slotFinished(Server * s)
+ {
+ ActiveMonitorItem * i = itemMap_[s];
+
+ if (0 != i)
+ i->finished();
+
+ itemMap_.remove(s);
+ }
+
+ void
+ ActiveMonitor::slotRequest(Server * s)
+ {
+ ActiveMonitorItem * i = itemMap_[s];
+
+ if (0 != i)
+ i->request();
+ }
+
+ void
+ ActiveMonitor::slotResponse(Server * s)
+ {
+ ActiveMonitorItem * i = itemMap_[s];
+
+ if (0 != i)
+ i->response();
+ }
+
+ void
+ ActiveMonitor::slotCull()
+ {
+ QDateTime dt = QDateTime::currentDateTime();
+
+ QListViewItemIterator it(view_);
+
+ for (; it.current(); ++it)
+ {
+ ActiveMonitorItem * i = static_cast<ActiveMonitorItem *>(it.current());
+
+ if ((0 == i->server()) && (i->death().secsTo(dt) > 60))
+ {
+ delete i;
+ ++it;
+ }
+ }
+ }
+
+ void
+ ActiveMonitor::slotSelectionChanged()
+ {
+ for (QListViewItemIterator it(view_); it.current(); ++it)
+ {
+ ActiveMonitorItem * i = static_cast<ActiveMonitorItem *>(it.current());
+
+ if
+ (
+ view_->isSelected(i)
+ &&
+ (0 != i->server())
+ &&
+ (Server::Finished != i->server()->state())
+ )
+ {
+ emit(selection(true));
+ return;
+ }
+ }
+
+ emit(selection(false));
+ }
+
+ void
+ ActiveMonitor::slotKillSelected()
+ {
+ for (QListViewItemIterator it(view_); it.current(); ++it)
+ {
+ ActiveMonitorItem * i = static_cast<ActiveMonitorItem *>(it.current());
+
+ if
+ (
+ view_->isSelected(i)
+ &&
+ (0 != i->server())
+ &&
+ (Server::Finished != i->server()->state())
+ )
+ {
+ i->server()->cancel();
+ }
+ }
+ }
+
+ WebServer *
+ ActiveMonitor::server()
+ {
+ return server_;
+ }
+
+ void
+ ActiveMonitor::closeEvent(QCloseEvent * e)
+ {
+ QWidget::closeEvent(e);
+ emit(dying(this));
+ }
+
+} // End namespace KPF
+
+#include "ActiveMonitor.moc"
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/ActiveMonitor.h b/kpf/src/ActiveMonitor.h
new file mode 100644
index 00000000..f0f62265
--- /dev/null
+++ b/kpf/src/ActiveMonitor.h
@@ -0,0 +1,165 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef KPF_ACTIVE_MONITOR_H
+#define KPF_ACTIVE_MONITOR_H
+
+#include <qmap.h>
+#include <qtimer.h>
+#include <qwidget.h>
+
+class QListView;
+class QPainter;
+class QPushButton;
+
+namespace KPF
+{
+ class WebServer;
+ class Server;
+ class ActiveMonitorItem;
+
+ /**
+ * Shows a list of ActiveMonitorItem objects.
+ *
+ * Proxies signals from Server objects to ActiveMonitorItem objects.
+ * This is done to avoid making ActiveMonitorItem inherit QObject.
+ */
+ class ActiveMonitor : public QWidget
+ {
+ Q_OBJECT
+
+ public:
+
+ /**
+ * @param server WebServer which we should connect to in order to
+ * receive signals.
+ */
+ ActiveMonitor
+ (
+ WebServer * server,
+ QWidget * parent = 0,
+ const char * name = 0
+ );
+
+ virtual ~ActiveMonitor();
+
+ /**
+ * @return WebServer object we were given at construction.
+ */
+ WebServer * server();
+
+ public slots:
+
+ /**
+ * Look for selected ActiveMonitorItem objects and kill their
+ * connections immediately.
+ */
+ void slotKillSelected();
+
+ protected slots:
+
+ /**
+ * Called when a Server object has been created to handle an incoming
+ * connection.
+ * Creates an ActiveMonitorItem and associates it with the server.
+ * @param server The Server which was created.
+ */
+ void slotConnection(Server * server);
+
+ /**
+ * Called when a Server object has sent data to the remote client.
+ * Updates the associated ActiveMonitorItem.
+ * @param server The Server which is handling the connection.
+ * @param bytes Number of bytes sent by the server object.
+ */
+ void slotOutput(Server * server, ulong bytes);
+
+ /**
+ * Called when a Server object has finished all transactions with its
+ * remote client and is about to die. Marks the associated
+ * ActiveMonitorItem as defunct, for later culling via slotCull.
+ * @param server The Server which is handling the connection.
+ */
+ void slotFinished(Server *);
+
+ /**
+ * Called when a Server object has received a response from its remote
+ * client. Updates the associated ActiveMonitorItem.
+ * @param server The Server which is handling the connection.
+ */
+ void slotRequest(Server *);
+
+ /**
+ * Called when a Server object has sent a response to its remote
+ * client. Updates the associated ActiveMonitorItem.
+ * @param server The Server which is handling the connection.
+ */
+ void slotResponse(Server *);
+
+ /**
+ * Called periodically to remove ActiveMonitorItem objects which are no
+ * longer associated with a Server object.
+ */
+ void slotCull();
+
+ /**
+ * Connected to the relevant signal of the contained QListView and used
+ * to update the enabled/disabled state of the button which allows
+ * killing connections.
+ */
+ void slotSelectionChanged();
+
+ protected:
+
+ /**
+ * Overridden to emit a signal when this window is closed.
+ */
+ virtual void closeEvent(QCloseEvent *);
+
+ signals:
+
+ /**
+ * Emitted when this window is closed.
+ */
+ void dying(ActiveMonitor *);
+
+ /**
+ * Emitted when the selection of the contained QListView has changed.
+ * @param selectionExists true if there is a selection.
+ */
+ void selection(bool selectionExists);
+
+ private:
+
+ QListView * view_;
+ WebServer * server_;
+
+ QMap<Server *, ActiveMonitorItem *> itemMap_;
+
+ QTimer cullTimer_;
+ };
+
+} // End namespace KPF
+
+#endif
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/ActiveMonitorItem.cpp b/kpf/src/ActiveMonitorItem.cpp
new file mode 100644
index 00000000..1cc8fdcb
--- /dev/null
+++ b/kpf/src/ActiveMonitorItem.cpp
@@ -0,0 +1,199 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include <qpainter.h>
+#include <kiconloader.h>
+
+#include "Defines.h"
+#include "ActiveMonitorItem.h"
+#include "Server.h"
+#include "Utils.h"
+
+namespace KPF
+{
+ ActiveMonitorItem::ActiveMonitorItem(Server * server, QListView * parent)
+ : QListViewItem (parent),
+ server_ (server),
+ size_ (0),
+ sent_ (0)
+ {
+ setText(Host, server_->peerAddress().toString());
+ setText(Resource, "...");
+ setText(Response, "...");
+ setText(Size, "...");
+ setText(Sent, "...");
+
+ updateState();
+ }
+
+ ActiveMonitorItem::~ActiveMonitorItem()
+ {
+ // Empty.
+ }
+
+ void
+ ActiveMonitorItem::paintCell
+ (
+ QPainter * p,
+ const QColorGroup & g,
+ int c,
+ int w,
+ int a
+ )
+ {
+ if (c != Progress)
+ {
+ QListViewItem::paintCell(p, g, c, w, a);
+ return;
+ }
+
+ p->setPen(g.dark());
+
+ p->setPen(g.base());
+
+ p->drawRect(0, 0, w, height());
+
+ int maxBarLength = w - 4;
+
+ int barLength = maxBarLength;
+
+ if (0 != size_)
+ barLength = int((sent_ / double(size_)) * maxBarLength);
+
+ p->fillRect(2, 2, barLength, height() - 4, g.highlight());
+ }
+
+ int
+ ActiveMonitorItem::width
+ (
+ const QFontMetrics & fm,
+ const QListView * lv,
+ int c
+ ) const
+ {
+ switch (c)
+ {
+ case Status:
+ return 16;
+ break;
+
+ case Progress:
+ return 32;
+ break;
+
+ default:
+ return QListViewItem::width(fm, lv, c);
+ break;
+ }
+ }
+
+ void
+ ActiveMonitorItem::updateState()
+ {
+ if (0 != server_)
+ {
+ switch (server_->state())
+ {
+ case Server::WaitingForRequest:
+ setPixmap(Status, SmallIcon("connect_creating"));
+ break;
+
+ case Server::WaitingForHeaders:
+ setPixmap(Status, SmallIcon("connect_creating"));
+ break;
+
+ case Server::Responding:
+ setPixmap(Status, SmallIcon("connect_established"));
+ break;
+
+ case Server::Finished:
+ setPixmap(Status, SmallIcon("connect_no"));
+ break;
+ }
+ }
+ }
+
+ Server *
+ ActiveMonitorItem::server()
+ {
+ return server_;
+ }
+
+ QDateTime
+ ActiveMonitorItem::death() const
+ {
+ return death_;
+ }
+
+ void
+ ActiveMonitorItem::request()
+ {
+ if (0 != server_)
+ {
+ setText(Resource, server_->request().path());
+ updateState();
+ }
+ }
+
+ void
+ ActiveMonitorItem::response()
+ {
+ if (0 != server_)
+ {
+ setText(Response, translatedResponseName(server_->response().code()));
+
+ size_ = server_->response().size();
+
+ setText(Size, QString::number(size_));
+
+ updateState();
+ }
+ }
+
+ void
+ ActiveMonitorItem::output(ulong l)
+ {
+ if (0 != server_)
+ {
+ sent_ += l;
+ setText(Sent, QString::number(sent_));
+ updateState();
+ repaint();
+ }
+ }
+
+ void
+ ActiveMonitorItem::finished()
+ {
+ if (0 != server_)
+ {
+ death_ = server_->death();
+ updateState();
+ }
+
+ server_ = 0L;
+ }
+
+} // End namespace KPF
+
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/ActiveMonitorItem.h b/kpf/src/ActiveMonitorItem.h
new file mode 100644
index 00000000..99cc13db
--- /dev/null
+++ b/kpf/src/ActiveMonitorItem.h
@@ -0,0 +1,144 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef KPF_ACTIVE_MONITOR_ITEM_H
+#define KPF_ACTIVE_MONITOR_ITEM_H
+
+#include <qlistview.h>
+#include <qdatetime.h>
+#include <qfontmetrics.h>
+#include <qpalette.h>
+
+class QPainter;
+
+namespace KPF
+{
+ class Server;
+
+ /**
+ * Used to display the status of a Server object.
+ * Created and managed by an ActiveMonitor object.
+ * Provides some textual information, including the requested filename
+ * and the response code, plus a simple graph displaying the data transfer
+ * progress of any response.
+ */
+ class ActiveMonitorItem : public QListViewItem
+ {
+ public:
+
+ enum Column
+ {
+ Status,
+ Progress,
+ Size,
+ Sent,
+ Response,
+ Resource,
+ Host
+ };
+
+ /**
+ * @param server the associated Server object.
+ */
+ ActiveMonitorItem(Server * server, QListView * parent);
+ virtual ~ActiveMonitorItem();
+
+ /**
+ * @return the Server object passed on construction.
+ */
+ Server * server();
+
+ /**
+ * Called by the controlling ActiveMonitor object when the associated
+ * Server object has received a request from its remote client. Queries
+ * the Server object for the Request object.
+ *
+ * May be called more than once, if a Server object handles more than
+ * one request (i.e. is working in `persistent' mode.)
+ */
+ void request();
+
+ /**
+ * Called by the controlling ActiveMonitor object when the associated
+ * Server object has sent a response to its remote client. Queries the
+ * Server object for the Response object.
+ *
+ * May be called more than once, if a Server object handles more than
+ * one request (i.e. is working in `persistent' mode.)
+ */
+ void response();
+
+ /**
+ * Called by the controlling ActiveMonitor object when the associated
+ * Server object has sent data to its remote client.
+ *
+ * This is called every time output is sent, with the total output
+ * since the request began.
+ */
+ void output(ulong);
+
+ /**
+ * Called by the controlling ActiveMonitor object when the associated
+ * Server object has completed all transactions with its remote client.
+ */
+ void finished();
+
+ /**
+ * @return the time of death (end of transactions with remote client)
+ * of the associated Server object.
+ */
+ QDateTime death() const;
+
+ protected:
+
+ /**
+ * Updates the display to reflect the current state of the connection
+ * held by the associated Server object.
+ */
+ virtual void updateState();
+
+ /**
+ * Overridden to provide for drawing a graph in the cell which displays
+ * the number of bytes sent to the remote client by the associated
+ * Server object.
+ */
+ virtual void paintCell(QPainter *, const QColorGroup &, int, int, int);
+
+ /**
+ * Overridden to provide for giving reasonable sizes for columns which
+ * do not contain text.
+ */
+ virtual int width(const QFontMetrics &, const QListView *, int) const;
+
+ private:
+
+ Server * server_;
+ QDateTime death_;
+ ulong size_;
+ ulong sent_;
+ };
+
+} // End namespace KPF
+
+#endif // KPF_ACTIVE_MONITOR_ITEM_H
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/ActiveMonitorWindow.cpp b/kpf/src/ActiveMonitorWindow.cpp
new file mode 100644
index 00000000..ae44e4be
--- /dev/null
+++ b/kpf/src/ActiveMonitorWindow.cpp
@@ -0,0 +1,90 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include <kaction.h>
+#include <klocale.h>
+
+#include "ActiveMonitor.h"
+#include "ActiveMonitorWindow.h"
+#include "ActiveMonitorWindow.moc"
+#include "WebServer.h"
+
+namespace KPF
+{
+ ActiveMonitorWindow::ActiveMonitorWindow
+ (
+ WebServer * server,
+ QWidget * parent,
+ const char * name
+ )
+ : KMainWindow(parent, name)
+ {
+ setCaption(i18n("Monitoring %1 - kpf").arg(server->root()));
+
+ monitor_ = new ActiveMonitor(server, this, "ActiveMonitor");
+
+ setCentralWidget(monitor_);
+
+ killAction_ =
+ new KAction
+ (
+ i18n("&Cancel Selected Transfers"),
+ "stop",
+ 0,
+ monitor_,
+ SLOT(slotKillSelected()),
+ actionCollection(),
+ "kill"
+ );
+
+ killAction_->setEnabled(false);
+
+ killAction_->plug(toolBar());
+ }
+
+ ActiveMonitorWindow::~ActiveMonitorWindow()
+ {
+ // Empty.
+ }
+
+ WebServer *
+ ActiveMonitorWindow::server()
+ {
+ return monitor_->server();
+ }
+
+ void
+ ActiveMonitorWindow::slotMayKill(bool b)
+ {
+ killAction_->setEnabled(b);
+ }
+
+ void
+ ActiveMonitorWindow::closeEvent(QCloseEvent *)
+ {
+ emit(dying(this));
+ }
+
+} // End namespace KPF
+
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/ActiveMonitorWindow.h b/kpf/src/ActiveMonitorWindow.h
new file mode 100644
index 00000000..eddf1113
--- /dev/null
+++ b/kpf/src/ActiveMonitorWindow.h
@@ -0,0 +1,104 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef KPF_ACTIVE_MONITOR_WINDOW_H
+#define KPF_ACTIVE_MONITOR_WINDOW_H
+
+#include <kmainwindow.h>
+
+class KAction;
+
+namespace KPF
+{
+ class ActiveMonitor;
+ class WebServer;
+
+ /**
+ * Wraps an ActiveMonitor (widget) in a toplevel window.
+ *
+ * A wrapper window is used to avoid forcing ActiveMonitor to be
+ * toplevel, so it can be used elsewhere if desired.
+ */
+ class ActiveMonitorWindow : public KMainWindow
+ {
+ Q_OBJECT
+
+ public:
+
+ /**
+ * @param server WebServer which we should connect to in order to
+ * receive signals.
+ */
+ ActiveMonitorWindow
+ (
+ WebServer * server,
+ QWidget * parent = 0,
+ const char * name = 0
+ );
+
+ virtual ~ActiveMonitorWindow();
+
+ /**
+ * @return WebServer object we were given at construction.
+ */
+ WebServer * server();
+
+ protected slots:
+
+ /**
+ * Connected to ActiveMonitor::selection, which tells us whether we
+ * should enable the 'kill connection' action.
+ */
+ void slotMayKill(bool);
+
+ protected:
+
+ /**
+ * Overridden to emit a signal when this window is closed.
+ */
+ virtual void closeEvent(QCloseEvent *);
+
+ signals:
+
+ /**
+ * Emitted when this window is closed.
+ */
+ void dying(ActiveMonitorWindow *);
+
+ /**
+ * Emitted when the selection of the contained QListView has changed.
+ * @param selectionExists true if there is a selection.
+ */
+ void selection(bool selectionExists);
+
+ private:
+
+ ActiveMonitor * monitor_;
+
+ KAction * killAction_;
+ };
+
+} // End namespace KPF
+
+#endif
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/Applet.cpp b/kpf/src/Applet.cpp
new file mode 100644
index 00000000..edd7a652
--- /dev/null
+++ b/kpf/src/Applet.cpp
@@ -0,0 +1,488 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include <qpainter.h>
+#include <qtimer.h>
+#include <qlayout.h>
+#include <qlabel.h>
+#include <qtoolbutton.h>
+#include <qpopupmenu.h>
+#include <qfileinfo.h>
+#include <qcursor.h>
+
+#include <dcopclient.h>
+#include <kiconloader.h>
+#include <kglobal.h>
+#include <kconfig.h>
+#include <kaboutapplication.h>
+#include <kaboutdata.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kapplication.h>
+#include <kurldrag.h>
+
+#include "System.h"
+#include "Defines.h"
+#include "Applet.h"
+#include "AppletItem.h"
+#include "WebServerManager.h"
+#include "WebServer.h"
+#include "ServerWizard.h"
+
+static const char kpfVersion[] = "1.0.1";
+
+extern "C"
+{
+ KDE_EXPORT KPanelApplet *
+ init(QWidget * parent, const QString & configFile)
+ {
+ if (0 == kpf::userId() || 0 == kpf::effectiveUserId())
+ {
+ // Don't run as root.
+ KMessageBox::detailedError
+ ( 0,
+ i18n("You cannot run KPF as root."),
+ i18n("Running as root exposes the whole system to "
+ "external attackers."),
+ i18n("Running as root.")
+ );
+ return NULL;
+ }
+ else
+ {
+ kpf::blockSigPipe();
+
+ KGlobal::locale()->insertCatalogue("kpf");
+
+ return new KPF::Applet
+ (
+ configFile,
+ KPanelApplet::Normal,
+ KPanelApplet::About|KPanelApplet::Help,
+ parent,
+ "kpf"
+ );
+ }
+ }
+}
+
+namespace KPF
+{
+ Applet::Applet
+ (
+ const QString & configFile,
+ Type type,
+ int actions,
+ QWidget * parent,
+ const char * name
+ )
+ : KPanelApplet (configFile, type, actions, parent, name),
+ wizard_ (0L),
+ popup_ (0L),
+ dcopClient_ (0L)
+ {
+ setAcceptDrops(true);
+
+ //setFrameStyle(QFrame::Panel | QFrame::Sunken);
+ //setLineWidth(1);
+
+ connect
+ (
+ WebServerManager::instance(),
+ SIGNAL(serverCreated(WebServer *)),
+ SLOT(slotServerCreated(WebServer *))
+ );
+
+ connect
+ (
+ WebServerManager::instance(),
+ SIGNAL(serverDisabled(WebServer *)),
+ SLOT(slotServerDisabled(WebServer *))
+ );
+
+ WebServerManager::instance()->loadConfig();
+
+ popup_ = new QPopupMenu(this);
+
+ popup_->insertItem
+ (BarIcon("filenew"), i18n("New Server..."), NewServer, NewServer);
+
+// popup_->insertItem
+// (BarIcon("quit"), i18n("Quit"), Quit, Quit);
+
+ dcopClient_ = new DCOPClient;
+ dcopClient_->registerAs("kpf", false);
+ }
+
+ Applet::~Applet()
+ {
+ delete dcopClient_;
+ WebServerManager::instance()->shutdown();
+ }
+
+ int
+ Applet::widthForHeight(int h) const
+ {
+ uint serverCount = itemList_.count();
+
+ if (0 == serverCount)
+ serverCount = 1;
+
+ if (Vertical == orientation())
+ return h / serverCount;
+ else
+ return h * serverCount;
+ }
+
+ int
+ Applet::heightForWidth(int w) const
+ {
+ uint serverCount = itemList_.count();
+
+ if (0 == serverCount)
+ serverCount = 1;
+
+ if (Vertical == orientation())
+ return w * serverCount;
+ else
+ return w / serverCount;
+ }
+
+ void
+ Applet::help()
+ {
+ kapp->invokeHelp( QString::null, "kpf" );
+ }
+
+ void
+ Applet::about()
+ {
+ KAboutData about
+ (
+ "kpf",
+ I18N_NOOP("kpf"),
+ kpfVersion,
+ I18N_NOOP("KDE public fileserver"),
+ KAboutData::License_Custom,
+ "(C) 2001 Rik Hemsley (rikkus) <rik@kde.org>",
+ I18N_NOOP(
+ "File sharing applet, using the HTTP (Hyper Text Transfer Protocol)"
+ " standard to serve files."
+ ),
+ "http://rikkus.info/kpf.html"
+ );
+
+ about.setLicenseText
+ (
+ I18N_NOOP
+ (
+"Permission is hereby granted, free of charge, to any person obtaining a copy\n"
+"of this software and associated documentation files (the \"Software\"), to\n"
+"deal in the Software without restriction, including without limitation the\n"
+"rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n"
+"sell copies of the Software, and to permit persons to whom the Software is\n"
+"furnished to do so, subject to the following conditions:\n"
+"\n"
+"The above copyright notice and this permission notice shall be included in\n"
+"all copies or substantial portions of the Software.\n"
+"\n"
+"THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n"
+"IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n"
+"FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n"
+"AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\n"
+"ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n"
+"WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n"
+ )
+ );
+
+ KAboutApplication a(&about, this);
+ a.exec();
+ }
+
+ void
+ Applet::orientationChange(Orientation)
+ {
+ resetLayout();
+ }
+
+ void
+ Applet::resizeEvent(QResizeEvent *)
+ {
+ resetLayout();
+ }
+
+ void
+ Applet::moveEvent(QMoveEvent *)
+ {
+ QPtrListIterator<AppletItem> it(itemList_);
+
+ for (uint i = 0; it.current(); ++it, ++i)
+ it.current()->setBackground();
+ }
+
+ void
+ Applet::resetLayout()
+ {
+ if (0 == itemList_.count())
+ return;
+
+ switch (orientation())
+ {
+ case Vertical:
+ {
+ uint itemHeight = height() / itemList_.count();
+
+ QPtrListIterator<AppletItem> it(itemList_);
+
+ for (uint i = 0; it.current(); ++it, ++i)
+ {
+ it.current()->resize(width(), itemHeight);
+ it.current()->move(0, i * itemHeight);
+ }
+ }
+ break;
+
+ case Horizontal:
+ {
+ uint itemWidth = width() / itemList_.count();
+
+ QPtrListIterator<AppletItem> it(itemList_);
+
+ for (uint i = 0; it.current(); ++it, ++i)
+ {
+ it.current()->resize(itemWidth, height());
+ it.current()->move(i * itemWidth, 0);
+ }
+ }
+ default:
+ break;
+ }
+ }
+
+ void
+ Applet::mousePressEvent(QMouseEvent * ev)
+ {
+ if (Qt::RightButton != ev->button() && Qt::LeftButton != ev->button())
+ return;
+
+ switch (popup_->exec(QCursor::pos()))
+ {
+ case NewServer:
+ slotNewServer();
+ break;
+
+ case Quit:
+ slotQuit();
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ void
+ Applet::slotNewServerAtLocation(const QString & location)
+ {
+ if (0 != wizard_)
+ {
+ wizard_->setLocation(location);
+ wizard_->show();
+ }
+
+ else
+ {
+ wizard_ = new ServerWizard;
+
+ connect
+ (
+ wizard_,
+ SIGNAL(dying(ServerWizard *)),
+ SLOT(slotWizardDying(ServerWizard *))
+ );
+
+ wizard_->setLocation(location);
+ wizard_->show();
+ }
+ }
+
+ void
+ Applet::slotNewServer()
+ {
+ if (0 != wizard_)
+ wizard_->show();
+
+ else
+ {
+ wizard_ = new ServerWizard;
+
+ connect
+ (
+ wizard_,
+ SIGNAL(dying(ServerWizard *)),
+ SLOT(slotWizardDying(ServerWizard *))
+ );
+
+ wizard_->show();
+ }
+ }
+
+ void
+ Applet::slotWizardDying(ServerWizard * wiz)
+ {
+ if (QDialog::Accepted == wiz->result())
+ {
+ WebServerManager::instance()->createServerLocal
+ (
+ wiz->root(),
+ wiz->listenPort(),
+ wiz->bandwidthLimit(),
+ wiz->connectionLimit(),
+ Config::DefaultFollowSymlinks,
+ wiz->serverName()
+ );
+ }
+
+ delete wizard_;
+ wizard_ = 0;
+ }
+
+ void
+ Applet::drawContents(QPainter * p)
+ {
+ QPixmap px;
+
+ if (width() > 48)
+ px = KGlobal::iconLoader()->loadIcon("kpf", KIcon::Panel, 48);
+ else if (width() > 32)
+ px = KGlobal::iconLoader()->loadIcon("kpf", KIcon::Panel, 32);
+ else if (width() > 16)
+ px = KGlobal::iconLoader()->loadIcon("kpf", KIcon::Panel, 16);
+ else
+ return;
+
+ QRect r(contentsRect());
+
+ p->drawPixmap
+ (
+ r.x() + r.width() / 2 - px.width() / 2,
+ r.y() + r.height() / 2 - px.height() / 2,
+ px
+ );
+ }
+
+ void
+ Applet::dragEnterEvent(QDragEnterEvent * e)
+ {
+ KURL::List l;
+
+ if (!KURLDrag::decode(e, l))
+ return;
+
+ if (l.count() != 1)
+ return;
+
+ const KURL &url = l[0];
+
+ if (!url.isLocalFile() || !QFileInfo(url.path()).isDir())
+ return;
+
+ e->accept();
+ }
+
+ void
+ Applet::dropEvent(QDropEvent * e)
+ {
+ KURL::List l;
+
+ if (!KURLDrag::decode(e, l))
+ return;
+
+ if (l.count() != 1)
+ return;
+
+ const KURL &url = l[0];
+
+ if (!url.isLocalFile() || !QFileInfo(url.path()).isDir())
+ return;
+
+ e->accept();
+
+ slotNewServerAtLocation(url.path());
+ }
+
+ void
+ Applet::slotServerCreated(WebServer * server)
+ {
+ AppletItem * i = new AppletItem(server, this);
+
+ connect
+ (
+ i,
+ SIGNAL(newServer()),
+ SLOT(slotNewServer())
+ );
+
+ connect
+ (
+ i,
+ SIGNAL(newServerAtLocation(const QString &)),
+ SLOT(slotNewServerAtLocation(const QString &))
+ );
+
+ itemList_.append(i);
+ i->show();
+ emit(updateLayout());
+ resetLayout();
+ }
+
+ void
+ Applet::slotServerDisabled(WebServer * server)
+ {
+ QPtrListIterator<AppletItem> it(itemList_);
+
+ for (; it.current(); ++it)
+ {
+ AppletItem * i = it.current();
+
+ if (i->server() == server)
+ {
+ itemList_.removeRef(i);
+ delete i;
+ emit(updateLayout());
+ resetLayout();
+ return;
+ }
+ }
+ }
+
+ void
+ Applet::slotQuit()
+ {
+ // How ?
+ }
+
+} // End namespace KPF
+
+#include "Applet.moc"
+
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/Applet.h b/kpf/src/Applet.h
new file mode 100644
index 00000000..15802a37
--- /dev/null
+++ b/kpf/src/Applet.h
@@ -0,0 +1,180 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef KPF_APPLET_H
+#define KPF_APPLET_H
+
+#include <qptrlist.h>
+#include <kpanelapplet.h>
+
+class QPopupMenu;
+class QPainter;
+class DCOPClient;
+
+namespace KPF
+{
+ class AppletItem;
+ class ServerWizard;
+ class WebServer;
+
+ /**
+ * Main `application' class, providing an implementation of KPanelApplet
+ * and managing AppletItem objects. Also provides a popup (context) menu of
+ * its own, to allow the user to add WebServer (and therefore AppletItem)
+ * objects.
+ */
+ class Applet : public KPanelApplet
+ {
+ Q_OBJECT
+
+ public:
+
+ Applet
+ (
+ const QString & configFile,
+ Type = Normal,
+ int = 0,
+ QWidget * = 0,
+ const char * = 0
+ );
+
+ ~Applet();
+
+ /**
+ * Overridden to give correct sizing according to orientation and number
+ * of contains AppletItem objects.
+ */
+ virtual int widthForHeight(int h) const;
+
+ /**
+ * Overridden to give correct sizing according to orientation and number
+ * of contains AppletItem objects.
+ */
+ virtual int heightForWidth(int w) const;
+
+ protected slots:
+
+ /**
+ * Called to create a new server when the path to the server is already
+ * known.
+ */
+ void slotNewServerAtLocation(const QString &);
+
+ /**
+ * Called to create a new server when the path to the server is unknown.
+ */
+ void slotNewServer();
+
+ /**
+ * Called when a ServerWizard is about to close.
+ */
+ void slotWizardDying(ServerWizard *);
+
+ /**
+ * Called when a WebServer object has been created. Creates an
+ * AppletItem, associates it with the former, and updates the layout.
+ */
+ void slotServerCreated(WebServer *);
+
+ /**
+ * Called when a WebServer object has been disabled.
+ * Deletes the associated AppletItem and updates the layout.
+ */
+ void slotServerDisabled(WebServer *);
+
+ /**
+ * Called when user asks for quit (via popup menu).
+ */
+ void slotQuit();
+
+ protected:
+
+ /**
+ * Overridden to display help window
+ */
+ virtual void help();
+
+ /**
+ * Overridden to provide an `about' dialog.
+ */
+ virtual void about();
+
+ /**
+ * Overridden to keep track of orientation change and update layout
+ * accordingly.
+ */
+ virtual void orientationChange(Orientation);
+
+ /**
+ * Overridden to update layout accordingly.
+ */
+ virtual void moveEvent(QMoveEvent *);
+ virtual void resizeEvent(QResizeEvent *);
+
+ /**
+ * Overridden to provide a context menu.
+ */
+ virtual void mousePressEvent(QMouseEvent *);
+
+ /**
+ * Updates the layout, moving AppletItem objects into proper positions.
+ */
+ virtual void resetLayout();
+
+ /**
+ * Overridden to provide something other than a blank display when there
+ * are no existing AppletItem objects contained.
+ */
+ virtual void drawContents(QPainter *);
+
+ /**
+ * Overridden to allow testing whether the dragged object points to a
+ * local directory.
+ */
+ virtual void dragEnterEvent(QDragEnterEvent *);
+
+ /**
+ * Overridden to allow creating a new WebServer when the dropped object
+ * points to a local directory.
+ */
+ virtual void dropEvent(QDropEvent *);
+
+ private:
+
+ enum
+ {
+ NewServer,
+ Quit
+ };
+
+ ServerWizard * wizard_;
+ QPopupMenu * popup_;
+ DCOPClient * dcopClient_;
+
+ QPtrList<AppletItem> itemList_;
+ };
+}
+
+#endif // KPF_APPLET_H
+
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/AppletItem.cpp b/kpf/src/AppletItem.cpp
new file mode 100644
index 00000000..e605f692
--- /dev/null
+++ b/kpf/src/AppletItem.cpp
@@ -0,0 +1,385 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qtimer.h>
+#include <qfileinfo.h>
+#include <qcursor.h>
+
+#include <kiconloader.h>
+#include <klocale.h>
+#include <kpopupmenu.h>
+#include <kurldrag.h>
+#include <kapplication.h>
+
+#include "Defines.h"
+#include "AppletItem.h"
+#include "WebServerManager.h"
+#include "WebServer.h"
+#include "BandwidthGraph.h"
+#include "ActiveMonitorWindow.h"
+#include "SingleServerConfigDialog.h"
+
+namespace KPF
+{
+ AppletItem::AppletItem(WebServer * server, QWidget * parent)
+ : QWidget (parent, "KPF::AppletItem"),
+ server_ (server),
+ configDialog_ (0L),
+ monitorWindow_ (0L),
+ graph_ (0L),
+ popup_ (0L)
+ {
+ setBackgroundOrigin(AncestorOrigin);
+ setAcceptDrops(true);
+
+ graph_ = new BandwidthGraph(server_, BandwidthGraph::UseOverlays, this);
+
+ graph_->setAcceptDrops(true);
+
+ graph_->installEventFilter(this);
+
+ (new QVBoxLayout(this))->addWidget(graph_);
+
+ QString popupTitle(i18n("kpf - %1").arg(server_->root()));
+
+ popup_ = new KPopupMenu(this);
+
+ popup_->insertTitle
+ (SmallIcon("kpf"), popupTitle, Title, Title);
+
+ popup_->insertItem
+ (SmallIcon("filenew"), i18n("New Server..."), NewServer, NewServer);
+
+ popup_->insertSeparator(Separator);
+
+ popup_->insertItem
+ (SmallIcon("viewmag"), i18n("Monitor"), Monitor, Monitor);
+
+ popup_->insertItem
+ (SmallIcon("configure"), i18n("Preferences..."), Configure, Configure);
+
+ popup_->insertItem
+ (SmallIcon("remove"), i18n("Remove"), Remove, Remove);
+
+ popup_->insertItem
+ (SmallIcon("reload"), i18n("Restart"), Restart, Restart);
+
+ popup_->insertItem
+ (SmallIcon("player_pause"), i18n("Pause"), Pause, Pause);
+
+ monitorWindow_ = new ActiveMonitorWindow(server_);
+
+ connect
+ (
+ monitorWindow_,
+ SIGNAL(dying(ActiveMonitorWindow *)),
+ SLOT(slotActiveMonitorWindowDying(ActiveMonitorWindow *))
+ );
+ }
+
+ AppletItem::~AppletItem()
+ {
+ delete configDialog_;
+ configDialog_ = 0;
+
+ delete monitorWindow_;
+ monitorWindow_ = 0;
+ }
+
+ void AppletItem::setBackground()
+ {
+ QResizeEvent e(size(), size());
+ kapp->sendEvent(graph_, &e);
+ graph_->update();
+ }
+
+ bool
+ AppletItem::eventFilter(QObject *, QEvent * ev)
+ {
+ switch (ev->type())
+ {
+
+ case QEvent::MouseButtonRelease:
+ {
+ QMouseEvent * e = static_cast<QMouseEvent *>(ev);
+
+ if (0 == e)
+ {
+ kpfDebug
+ << "Hmm, should have been able to static_cast here." << endl;
+ break;
+ }
+
+ if (!rect().contains(e->pos()))
+ {
+ break;
+ }
+
+ if (Qt::LeftButton == e->button())
+ {
+ // Show monitor.
+
+ if (0 == monitorWindow_)
+ monitorServer();
+
+ else
+ {
+ if (monitorWindow_->isVisible())
+ monitorWindow_->hide();
+ else
+ monitorWindow_->show();
+ }
+ }
+
+ return true;
+ }
+ break;
+
+ case QEvent::MouseButtonPress:
+ {
+ QMouseEvent * e = static_cast<QMouseEvent *>(ev);
+
+ if (0 == e)
+ {
+ kpfDebug
+ << "Hmm, should have been able to static_cast here." << endl;
+ break;
+ }
+
+ if (Qt::RightButton != e->button() && Qt::LeftButton != e->button())
+ break;
+
+ if (server_->paused())
+ popup_->changeItem
+ (Pause, SmallIcon("1rightarrow"), i18n("Unpause"));
+ else
+ popup_->changeItem
+ (Pause, SmallIcon("player_pause"), i18n("Pause"));
+
+ switch (popup_->exec(QCursor::pos()))
+ {
+ case NewServer:
+ emit(newServer());
+ break;
+
+ case Monitor:
+ monitorServer();
+ break;
+
+ case Configure:
+ configureServer();
+ break;
+
+ case Remove:
+ removeServer();
+ break;
+
+ case Restart:
+ restartServer();
+ break;
+
+ case Pause:
+ pauseServer();
+ break;
+
+ default:
+ break;
+ }
+
+ return true;
+ }
+ break;
+
+ case QEvent::DragEnter:
+ {
+ QDragEnterEvent * e = static_cast<QDragEnterEvent *>(ev);
+
+ if (0 == e)
+ {
+ kpfDebug
+ << "Hmm, should have been able to static_cast here." << endl;
+ break;
+ }
+
+ KURL::List l;
+
+ if (!KURLDrag::decode(e, l))
+ break;
+
+ if (l.count() != 1)
+ break;
+
+ const KURL &url = l[0];
+
+ if (!url.isLocalFile() || !QFileInfo(url.path()).isDir())
+ break;
+
+ e->accept();
+ return true;
+ }
+ break;
+
+ case QEvent::Drop:
+ {
+ QDropEvent * e = static_cast<QDropEvent *>(ev);
+
+ if (0 == e)
+ {
+ kpfDebug
+ << "Hmm, should have been able to static_cast here." << endl;
+ break;
+ }
+
+ KURL::List l;
+
+ if (!KURLDrag::decode(e, l))
+ break;
+
+ if (l.count() != 1)
+ break;
+
+ const KURL &url = l[0];
+
+ if (!url.isLocalFile() || !QFileInfo(url.path()).isDir())
+ break;
+
+ e->accept();
+ emit(newServerAtLocation(url.path()));
+ return true;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return false;
+ }
+
+ void
+ AppletItem::slotActiveMonitorWindowDying(ActiveMonitorWindow *)
+ {
+ // We used to delete it here, but let's not. See if this is a CPU hog.
+#if 0
+ delete monitorWindow_;
+ monitorWindow_ = 0;
+#endif
+ monitorWindow_->hide();
+ }
+
+ void
+ AppletItem::slotConfigDialogDying(SingleServerConfigDialog *)
+ {
+ graph_->setTooltip();
+
+ configDialog_->delayedDestruct();
+ configDialog_ = 0;
+ }
+
+ void
+ AppletItem::slotNewServer()
+ {
+ emit(newServer());
+ }
+
+ void
+ AppletItem::monitorServer()
+ {
+ // We used to delete it here, but let's not. See if this is a CPU hog.
+#if 0
+ if (0 != monitorWindow_)
+ {
+ monitorWindow_->show();
+ return;
+ }
+
+ monitorWindow_ = new ActiveMonitorWindow(server_);
+
+ connect
+ (
+ monitorWindow_,
+ SIGNAL(dying(ActiveMonitorWindow *)),
+ SLOT(slotActiveMonitorWindowDying(ActiveMonitorWindow *))
+ );
+#endif
+
+ monitorWindow_->show();
+ monitorWindow_->raise();
+ }
+
+ void
+ AppletItem::removeServer()
+ {
+ QTimer::singleShot(0, this, SLOT(slotSuicide()));
+ }
+
+ void
+ AppletItem::configureServer()
+ {
+ if (0 != configDialog_)
+ {
+ configDialog_->show();
+ return;
+ }
+
+ configDialog_ = new SingleServerConfigDialog(server_, 0);
+
+ connect
+ (
+ configDialog_,
+ SIGNAL(dying(SingleServerConfigDialog *)),
+ SLOT(slotConfigDialogDying(SingleServerConfigDialog *))
+ );
+
+ configDialog_->show();
+ }
+
+ void
+ AppletItem::slotSuicide()
+ {
+ WebServerManager::instance()->disableServer(server_->root());
+ }
+
+ void
+ AppletItem::restartServer()
+ {
+ server_->restart();
+ }
+
+ void
+ AppletItem::pauseServer()
+ {
+ server_->pause(!server_->paused());
+ }
+
+ WebServer *
+ AppletItem::server()
+ {
+ return server_;
+ }
+}
+
+#include "AppletItem.moc"
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/AppletItem.h b/kpf/src/AppletItem.h
new file mode 100644
index 00000000..013a6b0a
--- /dev/null
+++ b/kpf/src/AppletItem.h
@@ -0,0 +1,167 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef KPF_APPLET_ITEM_H
+#define KPF_APPLET_ITEM_H
+
+#include <qptrlist.h>
+#include <qwidget.h>
+
+class KPopupMenu;
+
+namespace KPF
+{
+ class ConfigDialog;
+ class BandwidthGraph;
+ class ActiveMonitorWindow;
+ class SingleServerConfigDialog;
+ class WebServer;
+
+ /**
+ * Provides control of, and coarse-grained activity display for, a WebServer
+ * object. Contains a BandwidthGraph widget and provides a context menu
+ * which allows WebServer object control, plus creation of a new WebServer,
+ * for user convenience.
+ */
+ class AppletItem : public QWidget
+ {
+ Q_OBJECT
+
+ public:
+
+ /**
+ * @param server The WebServer object which will be monitored and
+ * controlled by this object.
+ */
+ AppletItem(WebServer * server, QWidget * parent);
+
+ ~AppletItem();
+
+ /**
+ * @return the WebServer object given on construction.
+ */
+ WebServer * server();
+
+ void setBackground();
+
+ protected slots:
+
+ /**
+ * Called when an ActiveMonitorWindow (created by this object) is
+ * about to close.
+ */
+ void slotActiveMonitorWindowDying(ActiveMonitorWindow *);
+
+ /**
+ * Called when a SingleServerConfigDialog (created by this object) is
+ * about to close.
+ */
+ void slotConfigDialogDying(SingleServerConfigDialog *);
+
+ /**
+ * Called when the user requests a new WebServer via the context menu.
+ */
+ void slotNewServer();
+
+ /**
+ * Called by a timer after removeServer has been called and the event
+ * loop has been processed once.
+ */
+ void slotSuicide();
+
+ signals:
+
+ /**
+ * Emitted when a new WebServer is requested from the context menu.
+ */
+ void newServer();
+
+ /**
+ * Emitted when an URL pointing to a local directory has been dropped.
+ */
+ void newServerAtLocation(const QString &);
+
+ protected:
+
+ /**
+ * Overridden to provide a context menu plus DnD capabilities.
+ */
+ bool eventFilter(QObject *, QEvent *);
+
+ /**
+ * Called when the appropriate item is selected from the context menu.
+ * Creates an ActiveMonitorWindow.
+ */
+ void monitorServer ();
+
+ /**
+ * Called when the appropriate item is selected from the context menu.
+ * Asks the WebServerManager instance to remove the associated
+ * WebServer.
+ */
+ void removeServer ();
+
+ /**
+ * Called when the appropriate item is selected from the context menu.
+ * Creates a configuration dialog for the associated WebServer.
+ */
+ void configureServer ();
+
+ /**
+ * Called when the appropriate item is selected from the context menu.
+ * Restarts the associated WebServer.
+ */
+ void restartServer ();
+
+ /**
+ * Called when the appropriate item is selected from the context menu.
+ * Pauses the associated WebServer.
+ */
+ void pauseServer ();
+
+ private:
+
+ enum
+ {
+ Title,
+ NewServer,
+ Separator,
+ Monitor,
+ Configure,
+ Remove,
+ Restart,
+ Pause
+ };
+
+
+ WebServer * server_;
+ SingleServerConfigDialog * configDialog_;
+ ActiveMonitorWindow * monitorWindow_;
+ BandwidthGraph * graph_;
+ KPopupMenu * popup_;
+ };
+}
+
+#endif // KPF_APPLET_H
+
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/BandwidthGraph.cpp b/kpf/src/BandwidthGraph.cpp
new file mode 100644
index 00000000..a7f6c311
--- /dev/null
+++ b/kpf/src/BandwidthGraph.cpp
@@ -0,0 +1,335 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include <qdrawutil.h>
+#include <qpainter.h>
+#include <qtooltip.h>
+
+#include <klocale.h>
+#include <kiconloader.h>
+#include <kiconeffect.h>
+
+#include "Defines.h"
+#include "Utils.h"
+#include "BandwidthGraph.h"
+#include "WebServer.h"
+
+namespace KPF
+{
+ BandwidthGraph::BandwidthGraph
+ (
+ WebServer * server,
+ OverlaySelect overlaySelect,
+ QWidget * parent,
+ const char * name
+ )
+ : QWidget (parent, name, WRepaintNoErase),
+ server_ (server),
+ max_ (0L),
+ overlaySelect_ (overlaySelect)
+ {
+ setBackgroundOrigin(AncestorOrigin);
+ history_.resize(width());
+ history_.fill(0L);
+
+ connect
+ (
+ server_,
+ SIGNAL(wholeServerOutput(ulong)),
+ SLOT(slotOutput(ulong))
+ );
+
+ if (UseOverlays == overlaySelect_)
+ {
+ connect
+ (
+ server_,
+ SIGNAL(contentionChange(bool)), this,
+ SLOT(slotServerContentionChange(bool))
+ );
+
+ connect
+ (
+ server_,
+ SIGNAL(pauseChange(bool)), this,
+ SLOT(slotServerPauseChange(bool))
+ );
+ }
+
+ setTooltip();
+ }
+
+ BandwidthGraph::~BandwidthGraph()
+ {
+ // Empty.
+ }
+
+ void
+ BandwidthGraph::setTooltip()
+ {
+ QToolTip::add(this, i18n( "%1 on port %2" )
+ .arg( server_->root() ).arg( server_->listenPort() ) );
+ }
+
+ QRect
+ BandwidthGraph::contentsRect() const
+ {
+ return QRect(1, 1, width() - 2, height() - 2);
+ }
+
+ void
+ BandwidthGraph::updateContents()
+ {
+ QRect r(contentsRect());
+
+ uint w = r.width();
+ uint h = r.height();
+
+ buffer_.fill(this, 0, 0);
+
+ QPainter p(&buffer_);
+
+ p.drawPixmap( ( width()-bgPix_.width() )/2,
+ ( height()-bgPix_.height() )/2, bgPix_ );
+
+ p.setPen(colorGroup().dark());
+
+ for (uint i = 0; i < history_.size(); i++)
+ {
+ ulong l = history_[i];
+
+ if (0 != l)
+ {
+ uint barLength =
+ static_cast<uint>(l / float(max_) * h);
+
+ p.drawLine(i + 1, h, i + 1, h - barLength);
+ }
+ }
+
+ drawOverlays(p);
+
+ update();
+ }
+
+ void
+ BandwidthGraph::paintEvent(QPaintEvent * e)
+ {
+ bitBlt(this, e->rect().topLeft(), &buffer_, e->rect());
+ }
+
+ void
+ BandwidthGraph::resizeEvent(QResizeEvent *)
+ {
+ buffer_.resize(size());
+
+ if ( width() > 48 )
+ bgPix_ = KGlobal::iconLoader()->loadIcon( "kpf", KIcon::Panel, 48 );
+ else if ( width() > 32 )
+ bgPix_ = KGlobal::iconLoader()->loadIcon( "kpf", KIcon::Panel, 32 );
+ else if ( width() > 16 )
+ bgPix_ = KGlobal::iconLoader()->loadIcon( "kpf", KIcon::Panel, 16 );
+ else
+ bgPix_.fill( this, QPoint( 0, 0 ) );
+
+ KIconEffect::semiTransparent( bgPix_ );
+
+ if (width() < 2)
+ {
+ // We have 0 space. Make history 0 size.
+ history_ = QMemArray<ulong>();
+ return;
+ }
+
+ uint w = width() - 2;
+
+ if (w < history_.size())
+ {
+ QMemArray<ulong> newHistory(w);
+
+ uint sizeDiff = history_.size() - w;
+
+ for (uint i = sizeDiff; i < history_.size(); i++)
+ newHistory[i - sizeDiff] = history_[i];
+
+ history_ = newHistory;
+ }
+ else if (w > history_.size())
+ {
+ QMemArray<ulong> newHistory(w);
+
+ uint sizeDiff = w - history_.size();
+
+ for (uint i = 0; i < sizeDiff; i++)
+ {
+ newHistory[i] = 0L;
+ }
+
+ for (uint i = 0; i < history_.size(); i++)
+ newHistory[sizeDiff + i] = history_[i];
+
+ history_ = newHistory;
+ }
+
+ updateContents();
+ }
+
+ void
+ BandwidthGraph::slotOutput(ulong l)
+ {
+ QRect r(contentsRect());
+
+ uint w = r.width();
+ uint h = r.height();
+
+ if (0 == w || 0 == h)
+ return;
+
+ ulong oldMax = max_;
+
+ max_ = 0L;
+
+ if (history_.size() != w)
+ return;
+
+ for (uint i = 1; i < w; i++)
+ {
+ history_[i - 1] = history_[i];
+ max_ = max(history_[i], max_);
+ }
+
+ history_[w - 1] = l;
+ max_ = max(l, max_);
+
+ if (max_ != oldMax)
+ emit(maximumChanged(max_));
+
+ updateContents();
+ }
+
+ void
+ BandwidthGraph::drawOverlays(QPainter & p)
+ {
+ if (NoOverlays == overlaySelect_)
+ return;
+
+ if (!overlayPixmap_.isNull())
+ {
+ p.drawPixmap(3, 3, overlayPixmap_);
+ }
+
+ if (width() < 32 || height() < 32)
+ return;
+
+ if (overlayPixmap_.isNull())
+ {
+ QString maxString;
+
+ QString bs(i18n("%1 b/s"));
+ QString kbs(i18n("%1 kb/s"));
+ QString mbs(i18n("%1 Mb/s"));
+
+ if (max_ > 1024)
+ if (max_ > 1024 * 1024)
+ maxString = mbs.arg(max_ / (1024 * 1024));
+ else
+ maxString = kbs.arg(max_ / 1024);
+ else if ( max_ > 0 )
+ maxString = bs.arg(max_);
+ else
+ maxString = i18n( "Idle" );
+
+ p.setPen(Qt::white);
+
+ p.drawText
+ (
+ 4,
+ 4 + fontMetrics().ascent(),
+ maxString
+ );
+
+ p.setPen(Qt::black);
+
+ p.drawText
+ (
+ 3,
+ 3 + fontMetrics().ascent(),
+ maxString
+ );
+ }
+ }
+
+ QSize
+ BandwidthGraph::sizeHint() const
+ {
+ return QSize(32, 32);
+ }
+
+ QSize
+ BandwidthGraph::minimumSizeHint() const
+ {
+ return QSize(12, 12);
+ }
+
+ WebServer *
+ BandwidthGraph::server()
+ {
+ return server_;
+ }
+
+ void
+ BandwidthGraph::slotServerContentionChange(bool)
+ {
+ updateOverlayPixmap();
+ }
+
+ void
+ BandwidthGraph::slotServerPauseChange(bool)
+ {
+ updateOverlayPixmap();
+ }
+
+ void
+ BandwidthGraph::updateOverlayPixmap()
+ {
+ if (server_->paused())
+ {
+ overlayPixmap_ = SmallIcon("player_pause");
+ }
+ else
+ {
+ if (server_->portContention())
+ {
+ overlayPixmap_ = SmallIcon("connect_creating");
+ }
+ else
+ {
+ overlayPixmap_ = QPixmap();
+ }
+ }
+ }
+
+} // End namespace KPF
+
+#include "BandwidthGraph.moc"
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/BandwidthGraph.h b/kpf/src/BandwidthGraph.h
new file mode 100644
index 00000000..e0cf51dd
--- /dev/null
+++ b/kpf/src/BandwidthGraph.h
@@ -0,0 +1,163 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef KPF_BANDWIDTH_GRAPH_H
+#define KPF_BANDWIDTH_GRAPH_H
+
+#include <qwidget.h>
+#include <qmemarray.h>
+#include <qpixmap.h>
+#include <qrect.h>
+
+class QPainter;
+
+namespace KPF
+{
+ class WebServer;
+
+ /**
+ * Draws a graph of the bandwidth usage of a WebServer over time.
+ * May also displays an overlayed icon to show the status of a WebServer,
+ * i.e. whether it is active (no icon,) paused or in contention for a port.
+ */
+ class BandwidthGraph : public QWidget
+ {
+ Q_OBJECT
+
+ public:
+
+ enum OverlaySelect
+ {
+ UseOverlays,
+ NoOverlays
+ };
+
+ /**
+ * @param server WebServer to monitor.
+ * @param overlaySelect if UseOverlays, draw overlay icons to reflect
+ * server status.
+ */
+ BandwidthGraph
+ (
+ WebServer * server,
+ OverlaySelect overlaySelect,
+ QWidget * parent = 0,
+ const char * name = 0
+ );
+
+ virtual ~BandwidthGraph();
+
+ /**
+ * Set the tooltip showing shared directory name and port
+ */
+ void setTooltip();
+
+ /**
+ * Overridden to provide reasonable default size and shape.
+ */
+ virtual QSize sizeHint() const;
+
+ /**
+ * Overridden to provide reasonable minimum size and shape.
+ */
+ virtual QSize minimumSizeHint() const;
+
+ /**
+ * @return the WebServer object given on construction.
+ */
+ WebServer * server();
+
+ protected slots:
+
+ /**
+ * Connected to associated WebServer to receive notification of output.
+ */
+ void slotOutput(ulong);
+
+ /**
+ * Connected to associated WebServer to receive notification of port
+ * contention.
+ */
+ void slotServerContentionChange(bool);
+
+ /**
+ * Connected to associated WebServer to receive notification of pause
+ * or unpause.
+ */
+ void slotServerPauseChange(bool);
+
+ protected:
+
+ /**
+ * Overridden to provide graph drawing.
+ */
+ virtual void paintEvent(QPaintEvent *);
+
+ /**
+ * Overridden to assist graph drawing.
+ */
+ virtual void resizeEvent(QResizeEvent *);
+
+ /**
+ * Draw overlay icons to reflect status of associated WebServer.
+ */
+ virtual void drawOverlays(QPainter &);
+
+ /**
+ * Provides a rectangle in which to draw the graph itself.
+ */
+ virtual QRect contentsRect() const;
+
+ /**
+ * Called when the status of the associated WebServer changes.
+ */
+ virtual void updateOverlayPixmap();
+
+ signals:
+
+ /**
+ * Emitted when the maximum displayed value has changed.
+ */
+ void maximumChanged(ulong);
+
+ private:
+
+ void updateContents();
+
+ QMemArray<ulong> history_;
+
+ WebServer * server_;
+
+ QPixmap buffer_, bgPix_;
+
+ ulong max_;
+
+ OverlaySelect overlaySelect_;
+
+ QPixmap overlayPixmap_;
+ };
+
+} // End namespace KPF
+
+#endif
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/ByteRange.cpp b/kpf/src/ByteRange.cpp
new file mode 100644
index 00000000..cdd0624b
--- /dev/null
+++ b/kpf/src/ByteRange.cpp
@@ -0,0 +1,176 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include <qstringlist.h>
+
+#include "Defines.h"
+#include "ByteRange.h"
+
+namespace KPF
+{
+ ByteRange::ByteRange()
+ : first_ (0L),
+ last_ (0L),
+ haveLast_ (false)
+ {
+ // Empty.
+ }
+
+ ByteRange::ByteRange(ulong first)
+ : first_ (first),
+ last_ (0L),
+ haveLast_ (false)
+ {
+ // Empty.
+ }
+
+
+ ByteRange::ByteRange(ulong first, ulong last)
+ : first_ (first),
+ last_ (last),
+ haveLast_ (true)
+ {
+ }
+
+ ulong
+ ByteRange::first() const
+ {
+ return first_;
+ }
+
+ ulong
+ ByteRange::last() const
+ {
+ return last_;
+ }
+
+ bool
+ ByteRange::haveLast() const
+ {
+ return haveLast_;
+ }
+
+ void
+ ByteRange::setFirst(ulong l)
+ {
+ first_ = l;
+ }
+
+ void
+ ByteRange::setLast(ulong l)
+ {
+ last_ = l;
+ haveLast_ = true;
+ }
+
+ bool
+ ByteRange::valid() const
+ {
+ return haveLast_ ? (first_ < last_) : true;
+ }
+
+ void
+ ByteRange::clear()
+ {
+ first_ = last_ = 0L;
+ haveLast_ = false;
+ }
+
+ ByteRangeList::ByteRangeList()
+ {
+ // Empty.
+ }
+
+ ByteRangeList::ByteRangeList(const QString & _s, float /* protocol */)
+ {
+ kpfDebug << "ByteRangeList parsing `" << _s << "'" << endl;
+
+ // Hey, parsing time :)
+
+ QString s(_s);
+
+ if ("bytes=" == s.left(6))
+ {
+ s.remove(0, 6);
+ s = s.stripWhiteSpace();
+ }
+
+ QStringList byteRangeSpecList(QStringList::split(',', s));
+
+ QStringList::ConstIterator it;
+
+ for (it = byteRangeSpecList.begin(); it != byteRangeSpecList.end(); ++it)
+ addByteRange(*it);
+ }
+
+ void
+ ByteRangeList::addByteRange(const QString & s)
+ {
+ kpfDebug << "addByteRange(" << s << ")" << endl;
+
+ int dashPos = s.find('-');
+
+ if (-1 == dashPos)
+ {
+ kpfDebug << "No dash" << endl;
+ return;
+ }
+
+ QString firstByte(s.left(dashPos).stripWhiteSpace());
+
+ QString lastByte(s.mid(dashPos + 1).stripWhiteSpace());
+
+ ulong first;
+
+ if (firstByte.isEmpty())
+ first = 0L;
+ else
+ first = firstByte.toULong();
+
+ ulong last;
+
+ bool haveLast = !lastByte.isEmpty();
+
+ if (haveLast)
+ last = lastByte.toULong();
+ else
+ last = 0L;
+
+ if (haveLast)
+ {
+ if (first < last)
+ {
+ kpfDebug << "range: " << first << "d - " << last << "d" << endl;
+ append(ByteRange(first, last));
+ }
+ }
+ else
+ {
+ kpfDebug << "range: " << first << "d - end" << endl;
+ append(ByteRange(first));
+ }
+ }
+
+} // End namespace KPF
+
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/ByteRange.h b/kpf/src/ByteRange.h
new file mode 100644
index 00000000..4dd0b841
--- /dev/null
+++ b/kpf/src/ByteRange.h
@@ -0,0 +1,128 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef KPF_BYTE_RANGE_H
+#define KPF_BYTE_RANGE_H
+
+#include <qstring.h>
+#include <qvaluelist.h>
+
+namespace KPF
+{
+ /**
+ * Parse and store an HTTP 'byte range'.
+ * A range consists of a first and, optionally, a last byte.
+ * If the last byte is unspecified, it should be assumed to be
+ * the last byte of the resource being retrieved.
+ * A valid range has first <= last.
+ */
+ class ByteRange
+ {
+ public:
+
+ /**
+ * Constructs an empty range.
+ */
+ ByteRange();
+
+ /**
+ * Constructs a range with the first byte specified and the last
+ * byte set to 'none'.
+ */
+ ByteRange(ulong first);
+
+ /**
+ * Constructs a range with the first and last bytes specified.
+ */
+ ByteRange(ulong first, ulong last);
+
+ /**
+ * @return first byte in range.
+ */
+ ulong first() const;
+
+ /**
+ * @return last byte in range, if specified. Otherwise undefined.
+ */
+ ulong last() const;
+
+ /**
+ * @return true if last byte was specified.
+ */
+ bool haveLast() const;
+
+ /**
+ * Specify the first byte of the range.
+ */
+ void setFirst (ulong l);
+
+ /**
+ * Specify the last byte of the range. After setting this
+ * value, haveLast() will return true.
+ */
+ void setLast (ulong l);
+
+ /**
+ * @return true if first <= last or last is undefined.
+ */
+ bool valid() const;
+
+ /**
+ * Reset to initial state.
+ */
+ void clear();
+
+ private:
+
+ ulong first_;
+ ulong last_;
+ bool haveLast_;
+ };
+
+ /**
+ * Encapsulates a list of ByteRange.
+ */
+ class ByteRangeList : public QValueList<ByteRange>
+ {
+ public:
+
+ ByteRangeList();
+
+ /**
+ * Contructs a ByteRangeList from a string, which is parsed.
+ * @param protocol specifies the HTTP protocol which should
+ * be assumed whilst parsing.
+ */
+ ByteRangeList(const QString &, float protocol);
+
+ /**
+ * Parses a byte range represented as a string and, if successful,
+ * appends the resultant ByteRange object to this list.
+ */
+ void addByteRange(const QString &);
+ };
+
+} // End namespace KPF
+
+#endif
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/ConfigDialogPage.cpp b/kpf/src/ConfigDialogPage.cpp
new file mode 100644
index 00000000..395bb681
--- /dev/null
+++ b/kpf/src/ConfigDialogPage.cpp
@@ -0,0 +1,318 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include <qwhatsthis.h>
+#include <qlayout.h>
+#include <qspinbox.h>
+#include <qlabel.h>
+#include <qcheckbox.h>
+#include <qlineedit.h>
+
+#include <klocale.h>
+#include <kseparator.h>
+#include <kfiledialog.h>
+
+#include "Defines.h"
+#include "ErrorMessageConfigDialog.h"
+#include "ConfigDialogPage.h"
+#include "WebServerManager.h"
+#include "WebServer.h"
+#include "Help.h"
+
+#include <dnssd/servicebrowser.h>
+
+namespace KPF
+{
+ ConfigDialogPage::ConfigDialogPage(WebServer * server, QWidget * parent)
+ : QWidget (parent, "KPF::ConfigDialogPage"),
+ server_ (server),
+ errorMessageConfigDialog_ (0L)
+ {
+ l_listenPort_ = new QLabel(i18n("&Listen port:"), this);
+ l_bandwidthLimit_ = new QLabel(i18n("&Bandwidth limit:"), this);
+// l_connectionLimit_ = new QLabel(i18n("Connection &limit"), this);
+
+ sb_listenPort_ = new QSpinBox(1, 65535, 1, this);
+ sb_bandwidthLimit_ = new QSpinBox(1, 999999, 1, this);
+// sb_connectionLimit_ = new QSpinBox(1, 9999, 1, this);
+
+ l_serverName_ = new QLabel(i18n("&Server name:"), this);
+ le_serverName_ = new QLineEdit(this);
+
+ bool canPublish = DNSSD::ServiceBrowser::isAvailable() == DNSSD::ServiceBrowser::Working;
+ l_serverName_->setEnabled(canPublish);
+ le_serverName_->setEnabled(canPublish);
+
+ cb_followSymlinks_ = new QCheckBox(i18n("&Follow symbolic links"), this);
+
+// cb_customErrorMessages_ =
+// new QCheckBox(i18n("Use custom error messages"), this);
+
+// pb_errorMessages_ = new QPushButton(i18n("&Configure..."), this);
+
+// pb_errorMessages_->setEnabled(false);
+
+ l_listenPort_ ->setBuddy(sb_listenPort_);
+ l_bandwidthLimit_ ->setBuddy(sb_bandwidthLimit_);
+ l_serverName_ ->setBuddy(le_serverName_);
+// l_connectionLimit_ ->setBuddy(sb_connectionLimit_);
+
+ sb_listenPort_
+ ->setValue(WebServerManager::instance()->nextFreePort());
+
+ sb_bandwidthLimit_ ->setValue(Config::DefaultBandwidthLimit);
+ sb_bandwidthLimit_ ->setSuffix(i18n(" kB/s"));
+// sb_connectionLimit_ ->setValue(Config::DefaultConnectionLimit);
+ cb_followSymlinks_ ->setChecked(Config::DefaultFollowSymlinks);
+
+ QVBoxLayout * l0 = new QVBoxLayout(this, 0, KDialog::spacingHint());
+
+ QGridLayout * l2 = new QGridLayout(l0);
+
+ l2->addWidget(l_listenPort_, 0, 0);
+ l2->addWidget(sb_listenPort_, 0, 1);
+ l2->addWidget(l_bandwidthLimit_, 1, 0);
+ l2->addWidget(sb_bandwidthLimit_, 1, 1);
+ l2->addWidget(l_serverName_, 2, 0);
+ l2->addWidget(le_serverName_, 2, 1);
+// l2->addWidget(l_connectionLimit_, 2, 0);
+// l2->addWidget(sb_connectionLimit_, 2, 1);
+
+ l0->addWidget(cb_followSymlinks_);
+
+#if 0
+ QHBoxLayout * l3 = new QHBoxLayout(l0);
+
+ l3->addWidget(cb_customErrorMessages_);
+ l3->addWidget(pb_errorMessages_);
+#endif
+
+ l0->addStretch(1);
+
+#if 0
+ connect
+ (
+ cb_customErrorMessages_,
+ SIGNAL(toggled(bool)),
+ SLOT(slotCustomErrorMessagesToggled(bool))
+ );
+#endif
+
+#if 0
+ connect
+ (
+ pb_errorMessages_,
+ SIGNAL(clicked()),
+ SLOT(slotConfigureErrorMessages())
+ );
+#endif
+
+ QString listenPortHelp =
+ i18n
+ (
+ "<p>"
+ "Specify the network `port' on which the server should"
+ " listen for connections."
+ "</p>"
+ );
+
+ QString bandwidthLimitHelp =
+ i18n
+ (
+ "<p>"
+ "Specify the maximum amount of data (in kilobytes) that will be"
+ " sent out per second."
+ "</p>"
+ "<p>"
+ "This allows you to keep some bandwidth for yourself instead"
+ " of allowing connections with kpf to hog your connection."
+ "</p>"
+ );
+
+ QString connectionLimitHelp =
+ i18n
+ (
+ "<p>"
+ "Specify the maximum number of connections allowed at"
+ " any one time."
+ "</p>"
+ );
+
+ QString followSymlinksHelp =
+ i18n
+ (
+ "<p>"
+ "Allow serving of files which have a symbolic link in"
+ " the path from / to the file, or are a symbolic link"
+ " themselves."
+ "</p>"
+ "<p>"
+ "<strong>Warning !</strong> This could be a security"
+ " risk. Use only if you understand the issues involved."
+ "</p>"
+ );
+
+ QString errorMessagesHelp =
+ i18n
+ (
+ "<p>"
+ "Specify the text that will be sent upon an error,"
+ " such as a request for a page that does not exist"
+ " on this server."
+ "</p>"
+ );
+
+ QString serverNameHelp = KPF::HelpText::getServerNameHelp();
+ QWhatsThis::add(l_listenPort_, listenPortHelp);
+ QWhatsThis::add(sb_listenPort_, listenPortHelp);
+ QWhatsThis::add(l_bandwidthLimit_, bandwidthLimitHelp);
+ QWhatsThis::add(sb_bandwidthLimit_, bandwidthLimitHelp);
+// QWhatsThis::add(l_connectionLimit_, connectionLimitHelp);
+// QWhatsThis::add(sb_connectionLimit_, connectionLimitHelp);
+ QWhatsThis::add(cb_followSymlinks_, followSymlinksHelp);
+ QWhatsThis::add(l_serverName_, serverNameHelp);
+ QWhatsThis::add(le_serverName_, serverNameHelp);
+// QWhatsThis::add(pb_errorMessages_, errorMessagesHelp);
+
+ connect
+ (
+ sb_listenPort_,
+ SIGNAL(valueChanged(int)),
+ SLOT(slotListenPortChanged(int))
+ );
+
+ connect
+ (
+ sb_bandwidthLimit_,
+ SIGNAL(valueChanged(int)),
+ SLOT(slotBandwidthLimitChanged(int))
+ );
+
+ connect
+ (
+ cb_followSymlinks_,
+ SIGNAL(toggled(bool)),
+ SLOT(slotFollowSymlinksToggled(bool))
+ );
+
+
+ load();
+ }
+
+ ConfigDialogPage::~ConfigDialogPage()
+ {
+ // Empty.
+ }
+
+ void
+ ConfigDialogPage::load()
+ {
+ sb_listenPort_ ->setValue(server_->listenPort());
+ sb_bandwidthLimit_ ->setValue(server_->bandwidthLimit());
+// sb_connectionLimit_ ->setValue(server_->connectionLimit());
+ cb_followSymlinks_ ->setChecked(server_->followSymlinks());
+ le_serverName_ ->setText(server_->serverName());
+// cb_customErrorMessages_ ->setChecked(server_->customErrorMessages());
+ }
+
+ void
+ ConfigDialogPage::save()
+ {
+ server_->setListenPort (sb_listenPort_->value());
+ server_->setBandwidthLimit (sb_bandwidthLimit_->value());
+// server_->setConnectionLimit (sb_connectionLimit_->value());
+ server_->setFollowSymlinks (cb_followSymlinks_->isChecked());
+ server_->setCustomErrorMessages (cb_followSymlinks_->isChecked());
+ server_->setServerName (le_serverName_->text());
+ }
+
+ void
+ ConfigDialogPage::slotCustomErrorMessagesToggled(bool)
+ {
+// pb_errorMessages_->setEnabled(b);
+ }
+
+ void
+ ConfigDialogPage::slotConfigureErrorMessages()
+ {
+ if (0 == errorMessageConfigDialog_)
+ errorMessageConfigDialog_ = new ErrorMessageConfigDialog(server_, this);
+
+ errorMessageConfigDialog_->show();
+ }
+
+ void
+ ConfigDialogPage::slotListenPortChanged(int)
+ {
+ kpfDebug << "slotBandwidthLimitChanged" << endl;
+ checkOkAndEmit();
+ }
+
+ void ConfigDialogPage::checkOk()
+ {
+ kpfDebug << "slotBandwidthLimitChanged" << endl;
+ checkOkAndEmit();
+ }
+
+ void ConfigDialogPage::slotBandwidthLimitChanged(int)
+ {
+ kpfDebug << "slotBandwidthLimitChanged" << endl;
+ checkOkAndEmit();
+ }
+
+ void ConfigDialogPage::slotFollowSymlinksToggled(bool)
+ {
+ kpfDebug << "slotBandwidthLimitChanged" << endl;
+ checkOkAndEmit();
+ }
+
+ void ConfigDialogPage::checkOkAndEmit()
+ {
+ int newPort = sb_listenPort_->value();
+
+ if (newPort <= 1024)
+ {
+ emit(ok(false));
+ return;
+ }
+
+ QPtrList<WebServer>
+ serverList(WebServerManager::instance()->serverListLocal());
+
+ for (QPtrListIterator<WebServer> it(serverList); it.current(); ++it)
+ {
+ if (it.current() == server_)
+ continue;
+
+ if (it.current()->listenPort() == uint(newPort))
+ {
+ emit(ok(false));
+ return;
+ }
+ }
+
+ emit(ok(true));
+ }
+}
+#include "ConfigDialogPage.moc"
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/ConfigDialogPage.h b/kpf/src/ConfigDialogPage.h
new file mode 100644
index 00000000..8d41ea83
--- /dev/null
+++ b/kpf/src/ConfigDialogPage.h
@@ -0,0 +1,107 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef KPF_CONFIG_DIALOG_PAGE_H
+#define KPF_CONFIG_DIALOG_PAGE_H
+
+#include <qptrlist.h>
+#include <qwidget.h>
+
+class QLabel;
+class QSpinBox;
+class QCheckBox;
+class QPushButton;
+class QLineEdit;
+
+namespace KPF
+{
+ class WebServer;
+ class ErrorMessageConfigDialog;
+
+ /**
+ * Allows user configuration of a WebServer object.
+ */
+ class ConfigDialogPage : public QWidget
+ {
+ Q_OBJECT
+
+ public:
+
+ ConfigDialogPage(WebServer *, QWidget * parent);
+
+ virtual ~ConfigDialogPage();
+
+ /**
+ * Read settings from associated WebServer object and update controls.
+ */
+ void load();
+
+ /**
+ * Set attributes of associated WebServer object from controls.
+ */
+ void save();
+
+ void checkOk();
+
+ protected slots:
+
+ void slotConfigureErrorMessages();
+ void slotCustomErrorMessagesToggled(bool);
+ void slotListenPortChanged(int);
+ void slotBandwidthLimitChanged(int);
+ void slotFollowSymlinksToggled(bool);
+
+ protected:
+
+ void checkOkAndEmit();
+
+ signals:
+
+ void ok(bool);
+
+ private:
+
+ WebServer * server_;
+
+ QLabel * l_listenPort_;
+ QLabel * l_bandwidthLimit_;
+ QLabel * l_connectionLimit_;
+ QLabel * l_serverName_;
+
+ QSpinBox * sb_listenPort_;
+ QSpinBox * sb_bandwidthLimit_;
+ QSpinBox * sb_connectionLimit_;
+
+ QCheckBox * cb_followSymlinks_;
+
+ QLineEdit * le_serverName_;
+
+ QCheckBox * cb_customErrorMessages_;
+ QPushButton * pb_errorMessages_;
+
+ ErrorMessageConfigDialog * errorMessageConfigDialog_;
+ };
+}
+
+#endif
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/Defaults.cpp b/kpf/src/Defaults.cpp
new file mode 100644
index 00000000..691ca0f2
--- /dev/null
+++ b/kpf/src/Defaults.cpp
@@ -0,0 +1,97 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include "Defines.h"
+#include "Defaults.h"
+
+namespace KPF
+{
+ namespace Config
+ {
+ const uint DefaultListenPort = 8001;
+ const uint DefaultBandwidthLimit = 4;
+ const uint DefaultConnectionLimit = 64;
+ const bool DefaultFollowSymlinks = false;
+ const bool DefaultCustomErrors = false;
+ const bool DefaultPaused = false;
+ const QString& DefaultServername = QString::null;
+
+ static const char Name[] = "kpfappletrc";
+ static const char KeyServerRootList[] = "ServerRootList";
+ static const char KeyGroupPrefix[] = "Server_";
+ static const char KeyListenPort[] = "ListenPort";
+ static const char KeyBandwidthLimit[] = "BandwidthLimit";
+ static const char KeyConnectionLimit[] = "ConnectionLimit";
+ static const char KeyFollowSymlinks[] = "FollowSymlinks";
+ static const char KeyCustomErrors[] = "CustomErrors";
+ static const char KeyPaused[] = "Paused";
+ static const char KeyServerName[] = "ServerName";
+
+ QString name()
+ {
+ return QString::fromUtf8(Name);
+ }
+
+ QString key(Option o)
+ {
+ switch (o)
+ {
+ case ServerRootList:
+ return QString::fromUtf8(KeyServerRootList);
+
+ case GroupPrefix:
+ return QString::fromUtf8(KeyGroupPrefix);
+
+ case ListenPort:
+ return QString::fromUtf8(KeyListenPort);
+
+ case BandwidthLimit:
+ return QString::fromUtf8(KeyBandwidthLimit);
+
+ case ConnectionLimit:
+ return QString::fromUtf8(KeyConnectionLimit);
+
+ case FollowSymlinks:
+ return QString::fromUtf8(KeyFollowSymlinks);
+
+ case CustomErrors:
+ return QString::fromUtf8(KeyCustomErrors);
+
+ case Paused:
+ return QString::fromUtf8(KeyPaused);
+
+ case ServerName:
+ return QString::fromUtf8(KeyServerName);
+
+ /* default intentionally left out to have the compiler generate
+ * warnings in case we add values to the enumeration but forget
+ * to extend this switch.
+ */
+ }
+ return QString::null;
+ }
+ } // End namespace Config
+
+} // End namespace KPF
+
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/Defaults.h b/kpf/src/Defaults.h
new file mode 100644
index 00000000..d49339fe
--- /dev/null
+++ b/kpf/src/Defaults.h
@@ -0,0 +1,71 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef KPF_DEFAULTS_H
+#define KPF_DEFAULTS_H
+
+#include <qstring.h>
+
+namespace KPF
+{
+ /**
+ * Used to provide a single point of access to config defaults and key names.
+ */
+ namespace Config
+ {
+ extern const uint DefaultListenPort;
+ extern const uint DefaultBandwidthLimit;
+ extern const uint DefaultConnectionLimit;
+ extern const bool DefaultFollowSymlinks;
+ extern const bool DefaultCustomErrors;
+ extern const bool DefaultPaused;
+
+ enum Option
+ {
+ ServerRootList,
+ GroupPrefix,
+ ListenPort,
+ BandwidthLimit,
+ ConnectionLimit,
+ FollowSymlinks,
+ CustomErrors,
+ Paused,
+ ServerName
+ };
+
+ /**
+ * Name to be used for configuration file.
+ */
+ QString name();
+
+ /**
+ * Config key to use when accessing option.
+ */
+ QString key(Option);
+
+ } // End namespace Config
+
+} // End namespace KPF
+
+#endif // KPF_DEFAULTS_H
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/Defines.h b/kpf/src/Defines.h
new file mode 100644
index 00000000..5c724e9a
--- /dev/null
+++ b/kpf/src/Defines.h
@@ -0,0 +1,33 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef KPF_DEFINES_H
+#define KPF_DEFINES_H
+
+#include <kdebug.h>
+
+#define kpfDebug kdDebug(5007) << k_lineinfo << k_funcinfo << endl
+//#define kpfDebug kndDebug()
+
+#endif
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/DirSelectWidget.cpp b/kpf/src/DirSelectWidget.cpp
new file mode 100644
index 00000000..bd36ffeb
--- /dev/null
+++ b/kpf/src/DirSelectWidget.cpp
@@ -0,0 +1,115 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include <qdir.h>
+#include <qfileinfo.h>
+
+#include "DirSelectWidget.h"
+#include "DirSelectWidget.moc"
+
+namespace KPF
+{
+ class DirSelectWidget::Private
+ {
+ public:
+
+ QString pathToMakeVisible;
+ };
+
+ DirSelectWidget::DirSelectWidget
+ (
+ const QString & pathToMakeVisible,
+ QWidget * parent,
+ const char * name
+ )
+ : KListView(parent, name)
+ {
+ d = new Private;
+ d->pathToMakeVisible = pathToMakeVisible;
+
+ setRootIsDecorated(true);
+
+ connect
+ (
+ this,
+ SIGNAL(expanded(QListViewItem *)),
+ SLOT(slotExpanded(QListViewItem *))
+ );
+
+ QListViewItem * root = new QListViewItem(this, "/");
+
+ root->setExpandable(true);
+
+ startTimer(0);
+ }
+
+ DirSelectWidget::~DirSelectWidget()
+ {
+ delete d;
+ }
+
+ void
+ DirSelectWidget::timerEvent(QTimerEvent *)
+ {
+ killTimers();
+
+ if (0 != firstChild())
+ firstChild()->setOpen(true);
+ }
+
+ void
+ DirSelectWidget::slotExpanded(QListViewItem * item)
+ {
+ if (0 != item->firstChild())
+ return;
+
+ QString p(path(item));
+
+ QDir dir(p);
+
+ const QFileInfoList * entryInfoList =
+ dir.entryInfoList(QDir::Dirs | QDir::Readable);
+
+ for (QFileInfoListIterator it(*entryInfoList); it.current(); ++it)
+ {
+ if (it.current()->isDir() && it.current()->isReadable())
+ {
+ QListViewItem * i = new QListViewItem(item, it.current()->fileName());
+ i->setExpandable(true);
+ }
+ }
+ }
+
+ QString
+ DirSelectWidget::path(QListViewItem * item) const
+ {
+ QString ret(item->text(0));
+
+ while (0 != (item = item->parent()))
+ ret.prepend("/" + item->text(0));
+
+ return ret;
+ }
+}
+
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/DirSelectWidget.h b/kpf/src/DirSelectWidget.h
new file mode 100644
index 00000000..1f4c9eaf
--- /dev/null
+++ b/kpf/src/DirSelectWidget.h
@@ -0,0 +1,66 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef KPF_DIR_SELECT_WIDGET_H
+#define KPF_DIR_SELECT_WIDGET_H
+
+#include <klistview.h>
+
+namespace KPF
+{
+ /**
+ * Allows the user to choose a directory, with some restrictions.
+ */
+ class DirSelectWidget : public KListView
+ {
+ Q_OBJECT
+
+ public:
+
+ DirSelectWidget
+ (
+ const QString & pathToMakeVisible = "/",
+ QWidget * = 0,
+ const char * = 0
+ );
+
+ virtual ~DirSelectWidget();
+
+ protected slots:
+
+ void slotExpanded(QListViewItem * item);
+
+ protected:
+
+ void timerEvent(QTimerEvent *);
+ QString path(QListViewItem * item) const;
+
+ private:
+
+ class Private;
+ Private * d;
+ };
+}
+
+#endif // KPF_DIR_SELECT_WIDGET_H
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/DirectoryLister.cpp b/kpf/src/DirectoryLister.cpp
new file mode 100644
index 00000000..bfd0127b
--- /dev/null
+++ b/kpf/src/DirectoryLister.cpp
@@ -0,0 +1,345 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include <cmath>
+
+#include <qapplication.h>
+#include <qdir.h>
+#include <qstring.h>
+#include <qstylesheet.h>
+#include <qpalette.h>
+#include <qtextstream.h>
+#include <kglobalsettings.h>
+#include <klocale.h>
+#include <kmimetype.h>
+#include <kurl.h>
+
+#include "Defines.h"
+#include "DirectoryLister.h"
+#include "Utils.h"
+
+namespace KPF
+{
+ class DirectoryLister::Private
+ {
+ public:
+
+ Private()
+ {
+ }
+ };
+
+ QString colorToCSS(const QColor &c)
+ {
+ return
+ "rgb("
+ + QString::number(c.red())
+ + ", "
+ + QString::number(c.green())
+ + ", "
+ + QString::number(c.blue())
+ + ")";
+ }
+
+ QByteArray buildHTML(const QString & title, const QString & body)
+ {
+ QPalette pal = qApp->palette();
+ QByteArray temp_string;
+ QTextStream html(temp_string, IO_WriteOnly);
+
+ html.setEncoding(QTextStream::UnicodeUTF8);
+
+ html
+ << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+ << endl
+ << "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\""
+ << endl
+ << "\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">"
+ << endl
+ << "<html xmlns=\"http://www.w3.org/1999/xhtml\">"
+ << endl
+ << "\t<head>"
+ << endl
+ << "\t\t<title>"
+ << title
+ << "</title>"
+ << endl
+ << "<style type=\"text/css\">"
+ << endl
+ << "<!--"
+ << endl
+ << "table.filelist { "
+ << "color: "
+ << colorToCSS(pal.color(QPalette::Normal, QColorGroup::Foreground))
+ << "; "
+ << "background-color: "
+ << colorToCSS(pal.color(QPalette::Normal, QColorGroup::Background))
+ << "; "
+ << "border: thin outset; "
+ << "width: 100%; "
+ << "}"
+ << endl
+ << "td { "
+ << "margin: 0px; "
+ << "white-space: nowrap; "
+ << "}"
+ << endl
+ << "td.norm { "
+ << "background-color: "
+ << colorToCSS(pal.color(QPalette::Normal, QColorGroup::Base))
+ << "; "
+ << "color: "
+ << colorToCSS(pal.color(QPalette::Normal, QColorGroup::Foreground))
+ << "; "
+ << "}"
+ << endl
+ << "td.alt { "
+ << "background-color: "
+ << colorToCSS
+ (
+ KGlobalSettings::calculateAlternateBackgroundColor
+ (pal.color(QPalette::Normal, QColorGroup::Base))
+ )
+ << "; "
+ << "color: "
+ << colorToCSS(pal.color(QPalette::Normal, QColorGroup::Foreground))
+ << "; "
+ << "}"
+ << endl
+ << "a { "
+ << "color: "
+ << colorToCSS(pal.color(QPalette::Normal, QColorGroup::Text))
+ << "; "
+ << "text-decoration: none; "
+ << "}"
+ << endl
+ << "th.listheading { "
+ << "color: "
+ << colorToCSS(pal.color(QPalette::Normal, QColorGroup::ButtonText))
+ << "; "
+ << "background-color: "
+ << colorToCSS(pal.color(QPalette::Normal, QColorGroup::Button))
+ << "; "
+ << "text-align: left; "
+ << "white-space: nowrap; "
+ << "border: thin outset; "
+ << "}"
+ << endl
+ << "a.direntry { "
+ << "font-weight: bold; "
+ << "}"
+ << endl
+ << "div.sizeentry { "
+ << "color: "
+ << colorToCSS(pal.color(QPalette::Normal, QColorGroup::Text))
+ << "; "
+ << "text-align: right; "
+ << "}"
+ << endl
+ << "-->"
+ << endl
+ << "</style>"
+ << endl
+ << "\t</head>"
+ << endl
+ << "\t<body>"
+ << endl
+ << body
+ << "\t</body>"
+ << endl
+ << "</html>"
+ << endl
+ ;
+
+ return temp_string;
+ }
+
+ QString prettySize(uint size)
+ {
+ QString suffix;
+ QString temp;
+ float floated_size;
+
+ if (size > 1023)
+ {
+ if (size > 1048575)
+ {
+ floated_size = size / 1048576.0;
+ suffix = i18n(" MB");
+ }
+ else
+ {
+ floated_size = size / 1024.0;
+ suffix = i18n(" KB");
+ }
+ }
+ else
+ {
+ temp.setNum(size);
+ temp += i18n(" bytes");
+ return temp;
+ }
+
+ temp.setNum(floated_size, 'f', 1);
+ temp += suffix;
+ return temp;
+ }
+
+ DirectoryLister * DirectoryLister::instance_ = 0L;
+
+ DirectoryLister *
+ DirectoryLister::instance()
+ {
+ if (0 == instance_)
+ instance_ = new DirectoryLister;
+
+ return instance_;
+ }
+
+ DirectoryLister::DirectoryLister()
+ {
+ d = new Private;
+ }
+
+ DirectoryLister::~DirectoryLister()
+ {
+ delete d;
+ }
+
+ QByteArray
+ DirectoryLister::html(const QString & root, const QString & _path)
+ {
+ kpfDebug << "root: " << root << " path: " << _path << endl;
+
+ QString path;
+
+ if (_path.right(1) != "/")
+ path = _path + "/";
+ else
+ path = _path;
+
+ if (path[0] == '/')
+ path + "";
+
+ QDir d(root + path);
+
+ if (!d.exists())
+ {
+ return buildHTML
+ (
+ i18n("Error"),
+ i18n("Directory does not exist: %1 %2").arg(root).arg(path)
+ );
+ }
+
+ const QFileInfoList * infoList =
+ d.entryInfoList(QDir::DefaultFilter, QDir::Name | QDir::DirsFirst);
+
+ if (0 == infoList)
+ {
+ return buildHTML
+ (
+ i18n("Error"),
+ i18n("Directory unreadable: %1 %2").arg(root).arg(path)
+ );
+ }
+
+ QString html;
+
+ html += "<table";
+ html += " width=\"100%\"";
+ html += " class=\"filelist\">\n";
+
+ html += "<tr>\n";
+ html += "<th align=\"left\" class=\"listheading\">Name</th>\n";
+ html += "<th align=\"left\" class=\"listheading\">Size</th>\n";
+ html += "</tr>\n";
+
+ for (QFileInfoListIterator it(*infoList); it.current(); ++it)
+ {
+ static int counter = 0;
+
+ QFileInfo * fi(it.current());
+
+ if (
+ (fi->fileName()[0] == '.')
+ && ((fi->fileName() != "..") || path == "/")
+ )
+ {
+ // Don't show hidden files
+ continue;
+ }
+
+ ++counter;
+
+ QString td_class = (counter % 2) ? "alt" : "norm";
+
+ html += "<tr>\n";
+
+ html += "<td class=\"" + td_class + "\">";
+
+ QString item_class = QString((fi->isDir()) ? "direntry" : "fileentry");
+
+ KURL fu(path+fi->fileName());
+ html +=
+ "<a href=\""
+ + fu.encodedPathAndQuery()
+ + (fi->isDir() ? "/" : "")
+ + "\" class=\""
+ + item_class
+ + "\">";
+
+ if (fi->fileName() != "..")
+ html += QStyleSheet::escape(fi->fileName());
+ else
+ html += i18n("Parent Directory");
+
+ html += "</a>";
+
+ if (fi->isDir())
+ html += "/";
+
+ html += "</td>\n";
+
+ html += "<td class=\"" + td_class + "\">";
+
+ if (!fi->isDir())
+ html
+ += "<div class=\"sizeentry\">" + prettySize(fi->size()) + "</div>";
+
+ html += "</td>\n";
+ html += "</tr>\n";
+ }
+
+ html += "</table>\n";
+
+ return buildHTML
+ (
+ i18n("Directory listing for %1").arg(QStyleSheet::escape(path)),
+ html
+ );
+ }
+
+} // End namespace KPF
+
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/DirectoryLister.h b/kpf/src/DirectoryLister.h
new file mode 100644
index 00000000..e1809202
--- /dev/null
+++ b/kpf/src/DirectoryLister.h
@@ -0,0 +1,72 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef KPF_DIRECTORY_LISTER_H
+#define KPF_DIRECTORY_LISTER_H
+
+#include <qstring.h>
+#include <qcstring.h>
+
+namespace KPF
+{
+ /**
+ * Creates an HTML document by listing the contents of a directory.
+ */
+ class DirectoryLister
+ {
+ public:
+
+ /**
+ * @return a pointer to the single instance of DirectoryLister.
+ */
+ static DirectoryLister * instance();
+
+ virtual ~DirectoryLister();
+
+ /**
+ * Get a directory listing (HTML) for the specified path. Uses
+ * cache if directory has not been modified since last read.
+ */
+ QByteArray html(const QString &root, const QString & path);
+
+ uint headerLength() const;
+ uint footerLength() const;
+ uint emptyEntryLength() const;
+
+ private:
+
+ /**
+ * Constructs a directory listing object.
+ */
+ DirectoryLister();
+
+ static DirectoryLister * instance_;
+
+ class Private;
+ Private * d;
+ };
+
+} // End namespace KPF
+
+#endif
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/ErrorMessageConfigDialog.cpp b/kpf/src/ErrorMessageConfigDialog.cpp
new file mode 100644
index 00000000..2f7cb43a
--- /dev/null
+++ b/kpf/src/ErrorMessageConfigDialog.cpp
@@ -0,0 +1,154 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include "ErrorMessageConfigDialog.h"
+#include "ErrorMessageConfigDialog.moc"
+
+#include <qlabel.h>
+#include <qframe.h>
+#include <qlayout.h>
+
+#include <kurlrequester.h>
+#include <kconfig.h>
+#include <klocale.h>
+#include <kdialog.h>
+
+#include "Defines.h"
+#include "Defaults.h"
+#include "Utils.h"
+
+namespace KPF
+{
+ ErrorMessageConfigDialog::ErrorMessageConfigDialog
+ (
+ WebServer * webServer,
+ QWidget * parent
+ )
+ : KDialogBase
+ (
+ parent,
+ "ErrorMessageConfigDialog",
+ false,
+ i18n("Configure error messages"),
+ KDialogBase::Ok | KDialogBase::Cancel,
+ KDialogBase::Cancel,
+ true // Use a separator.
+ ),
+ server_(webServer)
+ {
+ QValueList<uint> codeList;
+
+ codeList << 400 << 403 << 404 << 412 << 416 << 500 << 501;
+
+ QFrame * w = makeMainWidget();
+
+ QVBoxLayout * layout =
+ new QVBoxLayout(w, KDialog::marginHint(), KDialog::spacingHint());
+
+ QLabel * info =
+ new QLabel
+ (
+ i18n
+ (
+ "<p>Here you may select files to use instead of the default error"
+ " messages passed to a client.</p>"
+ "<p>The files may contain anything you wish, but by convention"
+ " you should report the error code and the English version of"
+ " the error message (e.g. \"Bad request\"). Your file should"
+ " also be valid HTML.</p>"
+ "<p>The strings ERROR_MESSAGE, ERROR_CODE and RESOURCE, if"
+ " they exist in the file, will be replaced with the English"
+ " error message, the numeric error code and the path of the"
+ " requested resource, respectively.</p>"
+ ),
+ w
+ );
+
+ layout->addWidget(info);
+
+ QGridLayout * grid = new QGridLayout(layout, codeList.count(), 2);
+
+ QString pattern(i18n("%1 %2"));
+
+ KConfig config(Config::name());
+
+ config.setGroup("ErrorMessageOverrideFiles");
+
+ QValueList<uint>::ConstIterator it;
+
+ for (it = codeList.begin(); it != codeList.end(); ++it)
+ {
+ QString originalPath =
+ config.readPathEntry(QString::number(*it));
+
+ QString responseName(translatedResponseName(*it));
+
+ KURLRequester * requester = new KURLRequester(originalPath, w);
+
+ itemList_.append(new Item(*it, requester, responseName, originalPath));
+
+ QLabel * l = new QLabel(pattern.arg(*it).arg(responseName), w);
+
+ l->setBuddy(requester);
+
+ grid->addWidget(l, *it, 0);
+ grid->addWidget(requester, *it, 1);
+ }
+ }
+
+ ErrorMessageConfigDialog::~ErrorMessageConfigDialog()
+ {
+ itemList_.setAutoDelete(true);
+ itemList_.clear();
+ }
+
+ void
+ ErrorMessageConfigDialog::slotURLRequesterTextChanged(const QString &)
+ {
+ }
+
+ void
+ ErrorMessageConfigDialog::accept()
+ {
+ KConfig config(Config::name());
+
+ config.setGroup("ErrorMessageOverrideFiles");
+
+ QPtrListIterator<Item> it(itemList_);
+
+ for (; it.current(); ++it)
+ {
+ config.writePathEntry
+ (
+ QString::number(it.current()->code),
+ it.current()->urlRequester->url()
+ );
+ }
+
+ config.sync();
+
+ KDialogBase::accept();
+ }
+}
+
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/ErrorMessageConfigDialog.h b/kpf/src/ErrorMessageConfigDialog.h
new file mode 100644
index 00000000..51bf4830
--- /dev/null
+++ b/kpf/src/ErrorMessageConfigDialog.h
@@ -0,0 +1,88 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef KPF_ERROR_MESSAGE_CONFIG_DIALOG_H
+#define KPF_ERROR_MESSAGE_CONFIG_DIALOG_H
+
+#include <qmap.h>
+#include <kdialogbase.h>
+
+class KURLRequester;
+
+namespace KPF
+{
+ class WebServer;
+
+ /**
+ * Currently unused pending implementation.
+ */
+ class ErrorMessageConfigDialog : public KDialogBase
+ {
+ Q_OBJECT
+
+ public:
+
+ ErrorMessageConfigDialog(WebServer *, QWidget * parent);
+
+ virtual ~ErrorMessageConfigDialog();
+
+ protected slots:
+
+ void slotURLRequesterTextChanged(const QString &);
+
+ protected:
+
+ void accept();
+
+ private:
+
+ WebServer * server_;
+
+ /**
+ * Provides a graphical interface to allow the user to pick a file
+ * to be used when reporting an error code.
+ */
+ class Item
+ {
+ public:
+
+ Item(uint i, KURLRequester * r, QString s, QString p)
+ : code (i),
+ urlRequester (r),
+ report (s),
+ originalPath (p)
+ {
+ }
+
+ uint code;
+ KURLRequester * urlRequester;
+ QString report;
+ QString originalPath;
+ };
+
+ QPtrList<Item> itemList_;
+ };
+}
+
+#endif
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/Help.cpp b/kpf/src/Help.cpp
new file mode 100644
index 00000000..19c6ae26
--- /dev/null
+++ b/kpf/src/Help.cpp
@@ -0,0 +1,62 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include "Help.h"
+#include <klocale.h>
+
+#include <dnssd/servicebrowser.h>
+
+namespace KPF
+{
+ namespace HelpText
+ {
+
+ QString getServerNameHelp()
+ {
+ switch(DNSSD::ServiceBrowser::isAvailable()) {
+ case DNSSD::ServiceBrowser::Working:
+ return i18n("<p>Specify the name that will be used when announcing"
+ " this server on network.</p>");
+ case DNSSD::ServiceBrowser::Stopped:
+ return i18n("<p>The Zeroconf daemon is not running. See the Handbook"
+ " for more information.<br/>"
+ "Other users will not see this system when browsing"
+ " the network via zeroconf, but sharing will still work.</p>");
+ case DNSSD::ServiceBrowser::Unsupported:
+ return i18n("<p>Zeroconf support is not available in this version of KDE."
+ " See the Handbook for more information.<br/>"
+ "Other users will not see this system when browsing"
+ " the network via zeroconf, but sharing will still work.</p>");
+ default:
+ return i18n("<p>Unknown error with Zeroconf.<br/>"
+ "Other users will not see this system when browsing"
+ " the network via zeroconf, but sharing will still work.</p>");
+ }
+ }
+
+
+ } // End namespace HelpText
+
+} // End namespace KPF
+
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/Help.h b/kpf/src/Help.h
new file mode 100644
index 00000000..12584213
--- /dev/null
+++ b/kpf/src/Help.h
@@ -0,0 +1,44 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef KPF_HELP_H
+#define KPF_HELP_H
+
+#include <qstring.h>
+
+namespace KPF
+{
+ /**
+ * Used to provide a single point of access to config defaults and key names.
+ */
+ namespace HelpText
+ {
+
+ QString getServerNameHelp();
+
+ } // End namespace HelpText
+
+} // End namespace KPF
+
+#endif // KPF_DEFAULTS_H
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/KPFInterface.cpp b/kpf/src/KPFInterface.cpp
new file mode 100644
index 00000000..d12c751d
--- /dev/null
+++ b/kpf/src/KPFInterface.cpp
@@ -0,0 +1,130 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include "Defines.h"
+#include "WebServer.h"
+#include "WebServerManager.h"
+#include "KPFInterface.h"
+
+KPFInterface::KPFInterface()
+ : DCOPObject("KPFInterface")
+{
+ // Empty.
+}
+
+KPFInterface::~KPFInterface()
+{
+ // Empty.
+}
+
+ QStringList
+KPFInterface::serverRootList()
+{
+ QList<KPF::WebServer> l(KPF::WebServerManager::instance()->serverListLocal());
+
+ QStringList ret;
+
+ for (QListIterator<KPF::WebServer> it(l); it.current(); ++it)
+ ret << it.current()->root();
+
+ return ret;
+}
+
+ bool
+KPFInterface::createServer
+(
+ QString root,
+ uint port,
+ uint bandwidthLimit,
+ uint connectionLimit,
+ bool followSymlinks
+)
+{
+ kpfDebug << "KPFInterface::createServer(" << root << ", " <<
+ port << ", " << bandwidthLimit << ", " << connectionLimit << ", "
+ << (followSymlinks ? "true" : "false") << ")" << endl;
+
+ KPF::WebServer * s =
+ KPF::WebServerManager::instance()->createServer
+ (
+ root,
+ port,
+ bandwidthLimit,
+ connectionLimit,
+ followSymlinks
+ );
+
+ if (0 == s)
+ {
+ kpfDebug << "KPFInterface::createServer(): failed" << endl;
+ return false;
+ }
+ else
+ {
+ kpfDebug << "KPFInterface::createServer(): ok" << endl;
+ return true;
+ }
+}
+
+ bool
+KPFInterface::disableServer(QString root)
+{
+ kpfDebug << "KPFInterface::disableServer(" << root << ")" << endl;
+ return KPF::WebServerManager::instance()->disableServer(root);
+}
+
+ bool
+KPFInterface::restartServer(QString root)
+{
+ kpfDebug << "KPFInterface::restartServer(" << root << ")" << endl;
+ return KPF::WebServerManager::instance()->restartServer(root);
+}
+
+ bool
+KPFInterface::reconfigureServer(QString root)
+{
+ kpfDebug << "KPFInterface::reconfigureServer(" << root << ")" << endl;
+ return KPF::WebServerManager::instance()->reconfigureServer(root);
+}
+
+ bool
+KPFInterface::pauseServer(QString root)
+{
+ kpfDebug << "KPFInterface::pauseServer(" << root << ")" << endl;
+ return KPF::WebServerManager::instance()->pauseServer(root, true);
+}
+
+ bool
+KPFInterface::unpauseServer(QString root)
+{
+ kpfDebug << "KPFInterface::unpauseServer(" << root << ")" << endl;
+ return KPF::WebServerManager::instance()->pauseServer(root, false);
+}
+
+ bool
+KPFInterface::serverPaused(QString root)
+{
+ kpfDebug << "KPFInterface::serverPaused(" << root << ")" << endl;
+ return KPF::WebServerManager::instance()->serverPaused(root);
+}
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/KPFInterface.h b/kpf/src/KPFInterface.h
new file mode 100644
index 00000000..facd482c
--- /dev/null
+++ b/kpf/src/KPFInterface.h
@@ -0,0 +1,68 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef KPF_INTERFACE_H
+#define KPF_INTERFACE_H
+
+#include <dcopobject.h>
+
+#include "Defaults.h"
+
+/**
+ * DCOP interface to kpf.
+ */
+class KPFInterface : virtual public DCOPObject
+{
+ K_DCOP
+
+ public:
+
+ KPFInterface ();
+ ~KPFInterface ();
+
+ k_dcop:
+
+ /**
+ * @return list of root directories used by WebServer objects.
+ */
+ virtual QStringList serverRootList();
+
+ virtual bool createServer
+ (
+ QString root,
+ uint port,
+ uint bandwidthLimit = KPF::Config::DefaultBandwidthLimit,
+ uint connectionLimit = KPF::Config::DefaultConnectionLimit,
+ bool followSymlinks = KPF::Config::DefaultFollowSymlinks
+ );
+
+ virtual bool disableServer (QString root);
+ virtual bool restartServer (QString root);
+ virtual bool reconfigureServer (QString root);
+ virtual bool pauseServer (QString root);
+ virtual bool unpauseServer (QString root);
+ virtual bool serverPaused (QString root);
+};
+
+#endif
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/Makefile.am b/kpf/src/Makefile.am
new file mode 100644
index 00000000..5d419f0f
--- /dev/null
+++ b/kpf/src/Makefile.am
@@ -0,0 +1,89 @@
+INCLUDES = $(all_includes)
+
+METASOURCES = AUTO
+
+kde_module_LTLIBRARIES = kpf_panelapplet.la kpfpropertiesdialog.la
+
+kpf_panelapplet_la_SOURCES = \
+ Utils.cpp \
+ DirectoryLister.cpp \
+ ByteRange.cpp \
+ DirSelectWidget.cpp \
+ PortValidator.cpp \
+ Request.cpp \
+ Response.cpp \
+ Resource.cpp \
+ RootValidator.cpp \
+ Server.cpp \
+ ServerPrivate.cpp \
+ ServerSocket.cpp \
+ WebServer.cpp \
+ WebServer.skel \
+ WebServer.stub \
+ WebServerSocket.cpp \
+ WebServerManager.cpp \
+ WebServerManager.skel \
+ SingleServerConfigDialog.cpp \
+ System.cpp \
+ ConfigDialogPage.cpp \
+ ErrorMessageConfigDialog.cpp \
+ ActiveMonitor.cpp \
+ ActiveMonitorItem.cpp \
+ ActiveMonitorWindow.cpp \
+ BandwidthGraph.cpp \
+ ServerWizard.cpp \
+ AppletItem.cpp \
+ Applet.cpp \
+ Defaults.cpp \
+ Help.cpp
+
+kpf_panelapplet_la_LDFLAGS = $(all_libraries) $(KDE_PLUGIN) -module
+
+kpf_panelapplet_la_LIBADD = $(LIB_KIO) -lkdnssd
+
+kpfpropertiesdialog_la_SOURCES = \
+ PropertiesDialogPlugin.cpp \
+ StartingKPFDialog.cpp \
+ WebServer.stub \
+ WebServerManager.stub \
+ Defaults.cpp \
+ Help.cpp
+
+kpfpropertiesdialog_la_LIBADD = $(LIB_KIO) -lkdnssd
+
+kpfpropertiesdialog_la_LDFLAGS = $(all_libraries) $(KDE_PLUGIN) -module
+
+noinst_HEADERS = \
+ Utils.h \
+ Defaults.h \
+ DirectoryLister.h \
+ DirSelectWidget.h \
+ ByteRange.h \
+ PortValidator.h \
+ Request.h \
+ Response.h \
+ Resource.h \
+ RootValidator.h \
+ Server.h \
+ ServerPrivate.h \
+ ServerSocket.h \
+ WebServer.h \
+ WebServerSocket.h \
+ WebServerManager.h \
+ SingleServerConfigDialog.h \
+ ConfigDialogPage.h \
+ PropertiesDialogPlugin.h \
+ StartingKPFDialog.h \
+ ErrorMessageConfigDialog.h \
+ ActiveMonitor.h \
+ ActiveMonitorItem.h \
+ ActiveMonitorWindow.h \
+ BandwidthGraph.h \
+ ServerWizard.h \
+ AppletItem.h \
+ Applet.h \
+ Help.h
+
+messages:
+ $(XGETTEXT) *.cpp -o $(podir)/kpf.pot
+
diff --git a/kpf/src/PortValidator.cpp b/kpf/src/PortValidator.cpp
new file mode 100644
index 00000000..01c2074e
--- /dev/null
+++ b/kpf/src/PortValidator.cpp
@@ -0,0 +1,57 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include "PortValidator.h"
+#include "WebServerManager.h"
+#include "WebServer.h"
+
+namespace KPF
+{
+ PortValidator::PortValidator(QObject * parent, const char * name)
+ : QValidator(parent, name)
+ {
+ // Empty.
+ }
+
+ QValidator::State
+ PortValidator::validate(QString & input, int & /* unused */) const
+ {
+ uint port(input.toUInt());
+
+ QPtrList<WebServer>
+ serverList(WebServerManager::instance()->serverListLocal());
+
+ for (QPtrListIterator<WebServer> it(serverList); it.current(); ++it)
+ {
+ if (it.current()->listenPort() == port)
+ {
+ return Intermediate;
+ }
+ }
+
+ return Valid;
+ }
+
+} // End namespace KPF
+
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/PortValidator.h b/kpf/src/PortValidator.h
new file mode 100644
index 00000000..c86d0d01
--- /dev/null
+++ b/kpf/src/PortValidator.h
@@ -0,0 +1,47 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef KPF_PORT_VALIDATOR_H
+#define KPF_PORT_VALIDATOR_H
+
+#include <qvalidator.h>
+
+namespace KPF
+{
+ /**
+ * Used for checking that a port input by the user is not the same as
+ * one used by an existing server.
+ */
+ class PortValidator : public QValidator
+ {
+ public:
+
+ PortValidator(QObject * parent, const char * name = 0);
+
+ virtual State validate(QString & input, int & pos) const;
+ };
+
+} // End namespace KPF
+
+#endif // KPF_PORT_VALIDATOR_H
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/PropertiesDialogPlugin.cpp b/kpf/src/PropertiesDialogPlugin.cpp
new file mode 100644
index 00000000..e86743e3
--- /dev/null
+++ b/kpf/src/PropertiesDialogPlugin.cpp
@@ -0,0 +1,890 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include <qlayout.h>
+#include <qcheckbox.h>
+#include <qspinbox.h>
+#include <qlabel.h>
+#include <qframe.h>
+#include <qwhatsthis.h>
+#include <qpushbutton.h>
+#include <qwidgetstack.h>
+#include <qtimer.h>
+#include <qdir.h>
+#include <qlineedit.h>
+
+#include <kapplication.h>
+#include <kglobal.h>
+#include <dcopclient.h>
+#include <kdialogbase.h>
+#include <kmessagebox.h>
+#include <kurl.h>
+#include <kconfig.h>
+#include <klocale.h>
+#include <kseparator.h>
+#include <kgenericfactory.h>
+
+#include "Defines.h"
+#include "Defaults.h"
+#include "PropertiesDialogPlugin.h"
+#include "StartingKPFDialog.h"
+#include "WebServerManager_stub.h"
+#include "WebServer_stub.h"
+#include "Help.h"
+
+#include <dnssd/servicebrowser.h>
+
+namespace KPF
+{
+ class ServerState
+ {
+ public:
+
+ ServerState()
+ : shared (false),
+ listenPort (Config::DefaultListenPort),
+ bandwidthLimit (Config::DefaultBandwidthLimit),
+// connectionLimit (Config::DefaultConnectionLimit),
+ followSymlinks (Config::DefaultFollowSymlinks)
+ {
+ }
+
+ bool operator == (const ServerState & other) const
+ {
+ return
+ (
+ other.shared == shared
+ &&
+ other.listenPort == listenPort
+ &&
+ other.bandwidthLimit == bandwidthLimit
+ &&
+// other.connectionLimit == connectionLimit
+// &&
+ other.followSymlinks == followSymlinks
+ );
+ }
+
+ bool operator != (const ServerState & other) const
+ {
+ return
+ (
+ other.shared != shared
+ ||
+ other.listenPort != listenPort
+ ||
+ other.bandwidthLimit != bandwidthLimit
+ ||
+// other.connectionLimit != connectionLimit
+// ||
+ other.followSymlinks != followSymlinks
+ );
+ }
+
+
+ bool shared;
+ uint listenPort;
+ uint bandwidthLimit;
+// uint connectionLimit;
+ QString serverName;
+ bool followSymlinks;
+ };
+
+ class PropertiesDialogPlugin::Private
+ {
+ public:
+
+ Private()
+ : l_listenPort (0L),
+ l_bandwidthLimit (0L),
+// l_connectionLimit (0L),
+ sb_listenPort (0L),
+ sb_bandwidthLimit (0L),
+// sb_connectionLimit (0L),
+ le_serverName (0L),
+ cb_followSymlinks (0L),
+ cb_share (0L),
+ stack (0L),
+ initWidget (0L),
+ configWidget (0L),
+ webServerManagerInterface (0L),
+ kpfRunning (false)
+ {
+ }
+
+ QLabel * l_listenPort;
+ QLabel * l_bandwidthLimit;
+// QLabel * l_connectionLimit;
+ QLabel * l_serverName;
+ QLabel * l_kpfStatus;
+
+ QSpinBox * sb_listenPort;
+ QSpinBox * sb_bandwidthLimit;
+// QSpinBox * sb_connectionLimit;
+ QLineEdit * le_serverName;
+
+ QCheckBox * cb_followSymlinks;
+ QCheckBox * cb_share;
+
+ QPushButton * pb_startKPF;
+
+ QWidgetStack * stack;
+ QWidget * initWidget;
+ QWidget * configWidget;
+
+ WebServerManager_stub * webServerManagerInterface;
+
+ bool kpfRunning;
+ DCOPRef webServerRef;
+ KURL url;
+
+ ServerState currentState;
+ ServerState wantedState;
+ };
+
+ PropertiesDialogPlugin::PropertiesDialogPlugin(KPropertiesDialog * dialog,
+ const char *,
+ const QStringList &)
+ : KPropsDlgPlugin(dialog)
+ {
+ d = new Private;
+
+ d->webServerManagerInterface =
+ new WebServerManager_stub("kpf", "WebServerManager");
+
+ d->url = dialog->kurl();
+
+ if (
+ d->url == QDir::homeDirPath()
+ || d->url == "file:" + QDir::homeDirPath()
+ )
+ {
+ // Don't even show ourselves if it's the home dir
+ return;
+ }
+
+ QWidget * widget = dialog->addPage(i18n("&Sharing"));
+
+ d->stack = new QWidgetStack(widget);
+
+ QVBoxLayout * stackLayout = new QVBoxLayout(widget);
+ stackLayout->addWidget(d->stack);
+
+ d->initWidget = createInitWidget(d->stack);
+ d->configWidget = createConfigWidget(d->stack);
+
+ d->stack->addWidget(d->initWidget, 0);
+ d->stack->addWidget(d->configWidget, 1);
+
+ kapp->dcopClient()->setNotifications(true);
+
+ connect
+ (
+ kapp->dcopClient(),
+ SIGNAL(applicationRegistered(const QCString &)),
+ SLOT(slotApplicationRegistered(const QCString &))
+ );
+
+ connect
+ (
+ kapp->dcopClient(),
+ SIGNAL(applicationRemoved(const QCString &)),
+ SLOT(slotApplicationUnregistered(const QCString &))
+ );
+
+ d->kpfRunning = kapp->dcopClient()->isApplicationRegistered("kpf");
+
+ if (!d->kpfRunning)
+ {
+ d->stack->raiseWidget(d->initWidget);
+ }
+ else
+ {
+ getServerRef();
+ updateGUIFromCurrentState();
+ d->stack->raiseWidget(d->configWidget);
+ }
+ }
+
+ PropertiesDialogPlugin::~PropertiesDialogPlugin()
+ {
+ delete d->webServerManagerInterface;
+ d->webServerManagerInterface = 0;
+
+ delete d;
+ d = 0;
+ }
+
+ void
+ PropertiesDialogPlugin::slotSharingToggled(bool b)
+ {
+ if (b)
+ {
+ if (!userAcceptsWarning())
+ {
+ // Avoid loop.
+ d->cb_share->blockSignals(true);
+ d->cb_share->setChecked(false);
+ d->cb_share->blockSignals(false);
+ b = false;
+ }
+ }
+
+ setControlsEnabled(b);
+ }
+
+ void
+ PropertiesDialogPlugin::setControlsEnabled(bool b)
+ {
+
+ bool canPublish = b && DNSSD::ServiceBrowser::isAvailable() == DNSSD::ServiceBrowser::Working;
+
+ d->l_serverName->setEnabled(canPublish);
+ d->l_listenPort ->setEnabled(b);
+ d->l_bandwidthLimit ->setEnabled(b);
+// d->l_connectionLimit ->setEnabled(b);
+ d->l_serverName ->setEnabled(canPublish);
+
+ d->sb_listenPort ->setEnabled(b);
+ d->sb_bandwidthLimit ->setEnabled(b);
+// d->sb_connectionLimit ->setEnabled(b);
+ d->le_serverName ->setEnabled(canPublish);
+ d->cb_followSymlinks ->setEnabled(b);
+ }
+
+ QWidget *
+ PropertiesDialogPlugin::createInitWidget(QWidget * parent)
+ {
+ QWidget * w = new QWidget(parent);
+
+ QLabel * about =
+ new QLabel
+ (
+ i18n
+ (
+ "<p>To share files via the web, you need to be"
+ " running an 'applet' in your KDE panel. This"
+ " 'applet' is a small program which provides"
+ " file sharing capabilities."
+ "</p>"
+ ),
+ w
+ );
+
+ d->pb_startKPF
+ = new QPushButton(i18n("Start Applet"), w);
+
+ QVBoxLayout * l = new QVBoxLayout(w);
+
+ l->addWidget(about);
+
+ d->l_kpfStatus =
+ new QLabel(i18n("Applet status: <strong>not running</strong>"), w);
+
+ l->addWidget(d->l_kpfStatus);
+
+ QHBoxLayout * l2 = new QHBoxLayout(l);
+
+ l2->addStretch(1);
+ l2->addWidget(d->pb_startKPF);
+
+ l->addStretch(1);
+
+ connect(d->pb_startKPF, SIGNAL(clicked()), SLOT(slotStartKPF()));
+
+ return w;
+ }
+
+ QWidget *
+ PropertiesDialogPlugin::createConfigWidget(QWidget * parent)
+ {
+ QWidget * w = new QWidget(parent);
+
+ d->cb_share =
+ new QCheckBox(i18n("Share this directory on the &Web"), w);
+
+ d->l_listenPort = new QLabel(i18n("&Listen port:"), w);
+ d->l_bandwidthLimit = new QLabel(i18n("&Bandwidth limit:"), w);
+// d->l_connectionLimit = new QLabel(i18n("Connection &limit"), w);
+ d->l_serverName = new QLabel(i18n("&Server name:"), w);
+ bool canPublish = DNSSD::ServiceBrowser::isAvailable() == DNSSD::ServiceBrowser::Working;
+ d->l_serverName->setEnabled(canPublish);
+
+ d->sb_listenPort = new QSpinBox(1000, 999999, 1, w);
+ d->sb_bandwidthLimit = new QSpinBox(1, 999999, 1, w);
+// d->sb_connectionLimit = new QSpinBox(1, 9999, 1, w);
+ d->le_serverName = new QLineEdit( w);
+ d->le_serverName->setEnabled(canPublish);
+
+ d->cb_followSymlinks =
+ new QCheckBox(i18n("&Follow symbolic links"), w);
+
+ d->l_listenPort ->setBuddy(d->sb_listenPort);
+ d->l_serverName ->setBuddy(d->le_serverName);
+ d->l_bandwidthLimit ->setBuddy(d->sb_bandwidthLimit);
+// d->l_connectionLimit ->setBuddy(d->sb_connectionLimit);
+
+ d->sb_listenPort ->setValue(Config::DefaultListenPort);
+ d->sb_bandwidthLimit ->setValue(Config::DefaultBandwidthLimit);
+ d->sb_bandwidthLimit ->setSuffix(i18n("kB/s"));
+// d->sb_connectionLimit ->setValue(Config::DefaultConnectionLimit);
+ d->cb_followSymlinks ->setChecked(Config::DefaultFollowSymlinks);
+
+ QVBoxLayout * l0 =
+ new QVBoxLayout(w, KDialog::marginHint(), KDialog::spacingHint());
+
+ l0->addWidget(d->cb_share);
+
+ l0->addWidget(new KSeparator(QFrame::HLine, w));
+
+ QGridLayout * l2 = new QGridLayout(l0);
+
+ l2->addWidget(d->l_listenPort, 0, 0);
+ l2->addWidget(d->sb_listenPort, 0, 1);
+ l2->addWidget(d->l_bandwidthLimit, 1, 0);
+ l2->addWidget(d->sb_bandwidthLimit, 1, 1);
+// l2->addWidget(d->l_connectionLimit, 2, 0);
+// l2->addWidget(d->sb_connectionLimit, 2, 1);
+ l2->addWidget(d->l_serverName, 2, 0);
+ l2->addWidget(d->le_serverName, 2, 1);
+
+ l0->addWidget(d->cb_followSymlinks);
+
+ l0->addStretch(1);
+
+ QString shareHelp =
+ i18n
+ (
+ "<p>"
+ "Setting this option makes all files in this directory and"
+ " any subdirectories available for reading to anyone"
+ " who wishes to view them."
+ "</p>"
+ "<p>"
+ "To view your files, a web browser or similar program"
+ " may be used."
+ "</p>"
+ "<p>"
+ "<strong>Warning!</strong> Before sharing a directory,"
+ " you should be sure that it does not contain sensitive"
+ " information, such as passwords, company secrets, your"
+ " addressbook, etc."
+ "</p>"
+ "<p>"
+ "Note that you cannot share your home directory"
+ " (%1)"
+ "</p>"
+ )
+ .arg(QDir::homeDirPath());
+
+ QString listenPortHelp =
+ i18n
+ (
+ "<p>"
+ "Specify the network `port' on which the server should"
+ " listen for connections."
+ "</p>"
+ );
+
+ QString bandwidthLimitHelp =
+ i18n
+ (
+ "<p>"
+ "Specify the maximum amount of data (in kilobytes) that will be"
+ " sent out per second."
+ "</p>"
+ "<p>"
+ "This allows you to keep some bandwidth for yourself instead"
+ " of allowing connections with kpf to hog your connection."
+ "</p>"
+ );
+
+ QString connectionLimitHelp =
+ i18n
+ (
+ "<p>"
+ "Specify the maximum number of connections allowed at"
+ " any one time."
+ "</p>"
+ );
+
+ QString followSymlinksHelp =
+ i18n
+ (
+ "<p>"
+ "Allow serving of files which have a symbolic link in"
+ " the path from / to the file, or are a symbolic link"
+ " themselves."
+ "</p>"
+ "<p>"
+ "<strong>Warning!</strong> This could be a security"
+ " risk. Use only if you understand the issues involved."
+ "</p>"
+ );
+ QString serverNameHelp = KPF::HelpText::getServerNameHelp();
+
+ QWhatsThis::add(d->cb_share, shareHelp);
+ QWhatsThis::add(d->l_listenPort, listenPortHelp);
+ QWhatsThis::add(d->sb_listenPort, listenPortHelp);
+ QWhatsThis::add(d->l_bandwidthLimit, bandwidthLimitHelp);
+ QWhatsThis::add(d->sb_bandwidthLimit, bandwidthLimitHelp);
+// QWhatsThis::add(d->l_connectionLimit, connectionLimitHelp);
+// QWhatsThis::add(d->sb_connectionLimit, connectionLimitHelp);
+ QWhatsThis::add(d->l_serverName, serverNameHelp);
+ QWhatsThis::add(d->le_serverName, serverNameHelp);
+ QWhatsThis::add(d->cb_followSymlinks, followSymlinksHelp);
+
+ connect(d->cb_share, SIGNAL(toggled(bool)), SLOT(slotSharingToggled(bool)));
+
+ slotSharingToggled(false);
+
+ connect
+ (
+ d->cb_share,
+ SIGNAL(toggled(bool)),
+ SLOT(slotChanged())
+ );
+
+ connect
+ (
+ d->sb_listenPort,
+ SIGNAL(valueChanged(int)),
+ SLOT(slotChanged())
+ );
+
+ connect
+ (
+ d->sb_bandwidthLimit,
+ SIGNAL(valueChanged(int)),
+ SLOT(slotChanged())
+ );
+
+#if 0
+ connect
+ (
+ d->sb_connectionLimit,
+ SIGNAL(valueChanged(int)),
+ SLOT(slotChanged())
+ );
+#endif
+ connect
+ (
+ d->le_serverName,
+ SIGNAL(textChanged(const QString&)),
+ SLOT(slotChanged())
+ );
+
+ connect
+ (
+ d->cb_followSymlinks,
+ SIGNAL(toggled(bool)),
+ SLOT(slotChanged())
+ );
+
+ return w;
+ }
+
+ void
+ PropertiesDialogPlugin::slotStartKPF()
+ {
+ d->l_kpfStatus
+ ->setText(i18n("Applet status: <strong>starting...</strong>"));
+
+ kapp->dcopClient()
+ ->send("kicker", "default", "addApplet(QString)", "kpfapplet.desktop");
+
+ QTimer::singleShot(4 * 1000, this, SLOT(slotStartKPFFailed()));
+ }
+
+ void
+ PropertiesDialogPlugin::slotStartKPFFailed()
+ {
+ d->l_kpfStatus
+ ->setText(i18n("Applet status: <strong>failed to start</strong>"));
+
+ d->pb_startKPF->setEnabled(true);
+ }
+
+ void
+ PropertiesDialogPlugin::slotApplicationRegistered(const QCString & s)
+ {
+ if ("kpf" == s)
+ {
+ d->kpfRunning = true;
+
+ d->l_kpfStatus
+ ->setText(i18n("Applet status: <strong>running</strong>"));
+
+ d->pb_startKPF->setEnabled(false);
+
+ getServerRef();
+ updateGUIFromCurrentState();
+ d->stack->raiseWidget(d->configWidget);
+ }
+ }
+
+ void
+ PropertiesDialogPlugin::slotApplicationUnregistered(const QCString & s)
+ {
+ if ("kpf" == s)
+ {
+ d->kpfRunning = false;
+
+ d->webServerRef.clear();
+
+ d->pb_startKPF->setEnabled(true);
+
+ d->l_kpfStatus
+ ->setText(i18n("Applet status: <strong>not running</strong>"));
+
+ d->stack->raiseWidget(d->initWidget);
+ }
+ }
+
+ void
+ PropertiesDialogPlugin::readSettings()
+ {
+ d->currentState = ServerState();
+
+ if (!d->kpfRunning || d->webServerRef.isNull())
+ return;
+
+ d->currentState.shared = true;
+
+ WebServer_stub webServer(d->webServerRef.app(), d->webServerRef.object());
+
+ d->currentState.listenPort = webServer.listenPort();
+
+ if (DCOPStub::CallFailed == webServer.status())
+ {
+ // TODO: warn user ?
+ kpfDebug << "WebServer_stub call failed" << endl;
+ d->currentState.listenPort = Config::DefaultListenPort;
+ return;
+ }
+
+ d->currentState.bandwidthLimit = webServer.bandwidthLimit();
+
+ if (DCOPStub::CallFailed == webServer.status())
+ {
+ // TODO: warn user ?
+ kpfDebug << "WebServer_stub call failed" << endl;
+ d->currentState.bandwidthLimit = Config::DefaultBandwidthLimit;
+ return;
+ }
+
+#if 0
+ d->currentState.connectionLimit = webServer.connectionLimit();
+
+ if (DCOPStub::CallFailed == webServer.status())
+ {
+ // TODO: warn user ?
+ kpfDebug << "WebServer_stub call failed" << endl;
+ d->currentState.connectionLimit = Config::DefaultConnectionLimit;
+ return;
+ }
+#endif
+
+ d->currentState.serverName = webServer.serverName();
+ if (DCOPStub::CallFailed == webServer.status())
+ {
+ // TODO: warn user ?
+ kpfDebug << "WebServer_stub call failed" << endl;
+ d->currentState.serverName = "";
+ return;
+ }
+
+
+ d->currentState.followSymlinks = webServer.followSymlinks();
+
+ if (DCOPStub::CallFailed == webServer.status())
+ {
+ // TODO: warn user ?
+ kpfDebug << "WebServer_stub call failed" << endl;
+ d->currentState.followSymlinks = Config::DefaultFollowSymlinks;
+ return;
+ }
+ }
+
+ void
+ PropertiesDialogPlugin::getServerRef()
+ {
+ QValueList<DCOPRef> serverRefList =
+ d->webServerManagerInterface->serverList();
+
+ if (DCOPStub::CallFailed == d->webServerManagerInterface->status())
+ {
+ // TODO: warn
+ kpfDebug << "webServerManagerInterface.serverList call failed" << endl;
+ return;
+ }
+
+ d->webServerRef.clear();
+
+ QValueList<DCOPRef>::ConstIterator it(serverRefList.begin());
+
+ for (; it != serverRefList.end(); ++it)
+ {
+ DCOPRef serverRef(*it);
+
+ WebServer_stub webServer(serverRef.app(), serverRef.object());
+
+ if (webServer.root() == d->url.path())
+ {
+ d->webServerRef = serverRef;
+ break;
+ }
+ }
+ }
+
+ bool
+ PropertiesDialogPlugin::userAcceptsWarning() const
+ {
+ QString noWarningKey("DoNotWarnAboutSharingDirectoriesViaHTTP");
+
+ KConfig * config(KGlobal::config());
+
+ if (config->readBoolEntry(noWarningKey, false))
+ return true;
+
+ return
+ (
+ KMessageBox::Continue
+ ==
+ KMessageBox::warningContinueCancel
+ (
+ d->configWidget,
+ i18n(
+ "<p>"
+ "Before you share a directory, be <strong>absolutely"
+ " certain</strong> that it does not contain sensitive"
+ " information."
+ "</p>"
+ "<p>"
+ "Sharing a directory makes all information"
+ " in that directory <strong>and all subdirectories</strong>"
+ " available to <strong>anyone</strong> who wishes to read it."
+ "</p>"
+ "<p>"
+ "If you have a system administrator, please ask for permission"
+ " before sharing a directory in this way."
+ "</p>"
+ ),
+ i18n("Warning - Sharing Sensitive Information?"),
+ i18n("&Share Directory"),
+ noWarningKey,
+ true
+ )
+ );
+ }
+
+ void
+ PropertiesDialogPlugin::slotChanged()
+ {
+ kpfDebug << "PropertiesDialogPlugin::slotChanged" << endl;
+ readSettings();
+ updateWantedStateFromGUI();
+
+ setDirty(d->currentState != d->wantedState);
+ kpfDebug << "Dirty: " << isDirty() << endl;
+ emit(changed());
+ }
+
+ void
+ PropertiesDialogPlugin::applyChanges()
+ {
+ readSettings();
+ updateWantedStateFromGUI();
+
+ enum Action
+ {
+ None,
+ Enable,
+ Disable,
+ Reconfigure
+ };
+
+ bool needRestart = false;
+
+ Action action = None;
+
+ if (!d->currentState.shared && d->wantedState.shared)
+ {
+// kpfDebug << "Not shared, but want to be. Action is Enable" << endl;
+ action = Enable;
+ }
+ else if (d->currentState.shared && !d->wantedState.shared)
+ {
+// kpfDebug << "Shared, but don't want to be. Action is Disable" << endl;
+ action = Disable;
+ }
+ else if
+ (
+ d->currentState.listenPort != d->wantedState.listenPort
+ ||
+ d->currentState.bandwidthLimit != d->wantedState.bandwidthLimit
+ ||
+// d->currentState.connectionLimit != d->wantedState.connectionLimit
+// ||
+ d->currentState.serverName != d->wantedState.serverName
+ ||
+ d->currentState.followSymlinks != d->wantedState.followSymlinks
+ )
+ {
+// kpfDebug << "Config changed. Action is Reconfigure" << endl;
+ action = Reconfigure;
+
+ if (d->currentState.listenPort != d->wantedState.listenPort)
+ needRestart = true;
+ }
+
+ if (None == action)
+ {
+// kpfDebug << "Nothing changed. Action = None" << endl;
+ return;
+ }
+
+ switch (action)
+ {
+ case Enable:
+ {
+ DCOPRef ref =
+ d->webServerManagerInterface->createServer
+ (
+ d->url.path(),
+ d->wantedState.listenPort,
+ d->wantedState.bandwidthLimit,
+ Config::DefaultConnectionLimit,//d->wantedState.connectionLimit,
+ d->wantedState.followSymlinks,
+ d->wantedState.serverName
+ );
+
+ if (ref.isNull())
+ {
+ // TODO: Warn user.
+ kpfDebug
+ << "kpf refused to create server - warn user here !" << endl;
+ break;
+ }
+ else
+ {
+ d->webServerRef = ref;
+ }
+ }
+ break;
+
+ case Disable:
+ if (d->webServerRef.isNull())
+ {
+ // TODO: Warn user.
+ kpfDebug << "Disable, but d->webServerRef is null" << endl;
+ }
+ else
+ {
+ d->webServerManagerInterface->disableServer(d->webServerRef);
+ }
+ break;
+
+ case Reconfigure:
+
+ if (d->webServerRef.isNull())
+ {
+ kpfDebug << "Need restart, but d->webServerRef is null" << endl;
+ }
+ else
+ {
+ WebServer_stub webServer
+ (d->webServerRef.app(), d->webServerRef.object());
+
+ webServer.set
+ (
+ d->wantedState.listenPort,
+ d->wantedState.bandwidthLimit,
+ Config::DefaultConnectionLimit,//d->wantedState.connectionLimit,
+ d->wantedState.followSymlinks,
+ d->wantedState.serverName
+ );
+
+ if (DCOPStub::CallFailed == webServer.status())
+ {
+ // TODO: Warn user.
+ kpfDebug << "Reconfigure failed" << endl;
+ }
+
+ if (needRestart)
+ {
+ webServer.restart();
+
+ if (DCOPStub::CallFailed == webServer.status())
+ {
+ // TODO: Warn user.
+ kpfDebug << "Restart failed" << endl;
+ }
+ }
+ }
+ break;
+
+ default:
+ kpfDebug << "Code error in KPF::PropertiesDialogPlugin." << endl;
+ break;
+ }
+ }
+
+ void
+ PropertiesDialogPlugin::updateGUIFromCurrentState()
+ {
+ readSettings();
+
+ // We don't want slotSharingToggled to be called.
+ d->cb_share->blockSignals(true);
+ d->cb_share->setChecked(d->currentState.shared);
+ d->cb_share->blockSignals(false);
+
+ d->sb_listenPort ->setValue (d->currentState.listenPort);
+ d->sb_bandwidthLimit ->setValue (d->currentState.bandwidthLimit);
+// d->sb_connectionLimit ->setValue (d->currentState.connectionLimit);
+ d->le_serverName ->setText (d->currentState.serverName);
+ d->cb_followSymlinks ->setChecked (d->currentState.followSymlinks);
+
+ setControlsEnabled(d->currentState.shared);
+ }
+
+ void
+ PropertiesDialogPlugin::updateWantedStateFromGUI()
+ {
+ d->wantedState.shared = d->cb_share->isChecked();
+ d->wantedState.listenPort = d->sb_listenPort->value();
+ d->wantedState.bandwidthLimit = d->sb_bandwidthLimit->value();
+// d->wantedState.connectionLimit = d->sb_connectionLimit->value();
+ d->wantedState.serverName = d->le_serverName->text();
+ d->wantedState.followSymlinks = d->cb_followSymlinks->isChecked();
+ }
+
+ typedef KGenericFactory<PropertiesDialogPlugin, KPropertiesDialog> PropertiesDialogPluginFactory;
+}
+
+K_EXPORT_COMPONENT_FACTORY( kpfpropertiesdialog,
+ KPF::PropertiesDialogPluginFactory( "kpf" ) )
+
+#include "PropertiesDialogPlugin.moc"
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/PropertiesDialogPlugin.h b/kpf/src/PropertiesDialogPlugin.h
new file mode 100644
index 00000000..5881080a
--- /dev/null
+++ b/kpf/src/PropertiesDialogPlugin.h
@@ -0,0 +1,82 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef KPF_PROPERTIES_DIALOG_PLUGIN_H
+#define KPF_PROPERTIES_DIALOG_PLUGIN_H
+
+#include <kpropertiesdialog.h>
+
+namespace KPF
+{
+ /**
+ * Provides an implementation of KPropsDlgPlugin which is plugged into
+ * Konqueror's directory properties dialog. Allows creating and configuring
+ * WebServer objects via DCOP conversations with the KPFInterface. Also
+ * allows starting the kpf applet if it is not already loaded by kicker.
+ */
+ class PropertiesDialogPlugin : public KPropsDlgPlugin
+ {
+ Q_OBJECT
+
+ public:
+
+ PropertiesDialogPlugin(KPropertiesDialog *, const char *, const QStringList &);
+
+ virtual ~PropertiesDialogPlugin();
+
+ virtual void applyChanges();
+
+ protected slots:
+
+ void slotSharingToggled(bool);
+ void slotStartKPF();
+ void slotStartKPFFailed();
+
+ void slotApplicationRegistered(const QCString &);
+ void slotApplicationUnregistered(const QCString &);
+
+ void slotChanged();
+
+ protected:
+
+ void getServerRef();
+
+ void updateGUIFromCurrentState();
+ void updateWantedStateFromGUI();
+
+ QWidget * createInitWidget(QWidget * parent);
+ QWidget * createConfigWidget(QWidget * parent);
+
+ void readSettings();
+ void setControlsEnabled(bool);
+ bool userAcceptsWarning() const;
+
+ private:
+
+ class Private;
+ Private * d;
+ };
+}
+
+#endif
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/Request.cpp b/kpf/src/Request.cpp
new file mode 100644
index 00000000..01735363
--- /dev/null
+++ b/kpf/src/Request.cpp
@@ -0,0 +1,377 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include <climits> // For ULONG_MAX
+
+#include <qregexp.h>
+#include <kurl.h>
+
+#include "Defines.h"
+#include "Utils.h"
+#include "Request.h"
+
+namespace KPF
+{
+ Request::Request()
+ : protocolMajor_ (0),
+ protocolMinor_ (9),
+ method_ (Unsupported),
+ haveHost_ (false),
+ haveIfModifiedSince_ (false),
+ haveIfUnmodifiedSince_ (false),
+ expectContinue_ (false),
+ haveRange_ (false),
+ persist_ (false)
+ {
+ }
+
+ Request::~Request()
+ {
+ }
+
+ void
+ Request::parseHeaders(const QStringList & buf)
+ {
+ for (QStringList::ConstIterator it(buf.begin()); it != buf.end(); ++it)
+ {
+ QString line(*it);
+
+ int colonPos = line.find(':');
+
+ if (-1 != colonPos)
+ {
+ QString name(line.left(colonPos).stripWhiteSpace().lower());
+ QString value(line.mid(colonPos + 1).stripWhiteSpace());
+ handleHeader(name, value);
+ }
+ }
+ }
+
+ void
+ Request::handleHeader(const QString & name, const QString & value)
+ {
+ if ("host" == name)
+ {
+ setHost(value);
+ }
+ if ("range" == name)
+ {
+ setRange(value);
+ }
+ else if ("if-modified-since" == name)
+ {
+ QDateTime dt;
+
+ if (parseDate(value, dt))
+ setIfModifiedSince(dt);
+ }
+ else if ("if-unmodified-since" == name)
+ {
+ QDateTime dt;
+
+ if (parseDate(value, dt))
+ setIfUnmodifiedSince(dt);
+ }
+ else if ("connection" == name)
+ {
+ QString v(value.lower());
+
+ if ("keep-alive" == v)
+ {
+ setPersist(true);
+ }
+ else if ("close" == v)
+ {
+ setPersist(false);
+ }
+ }
+ }
+
+ void
+ Request::setProtocol(const QString & _s)
+ {
+ QString s(_s);
+
+ s.remove(0, 5);
+
+ int dotPos = s.find('.');
+
+ if (-1 != dotPos)
+ {
+ protocolMajor_ = s.left(dotPos).toUInt();
+ protocolMinor_ = s.mid(dotPos + 1).toUInt();
+ }
+ }
+
+ void
+ Request::setProtocol(uint major, uint minor)
+ {
+ protocolMajor_ = major;
+ protocolMinor_ = minor;
+ }
+
+ void
+ Request::setMethod(const QString & s)
+ {
+ if ("GET" == s)
+ method_ = Get;
+ else if ("HEAD" == s)
+ method_ = Head;
+ else
+ method_ = Unsupported;
+ }
+
+ void
+ Request::setMethod(Method m)
+ {
+ method_ = m;
+ }
+
+ void
+ Request::setPath(const QString & s)
+ {
+ KURL p(s);
+ path_ = clean(p.path());
+
+#if 0
+ if ('/' == path_.at(path_.length() - 1))
+ {
+ path_.append("index.html");
+ }
+#endif
+ }
+
+ void
+ Request::setHost(const QString & s)
+ {
+ host_ = s;
+ haveHost_ = true;
+ }
+
+ void
+ Request::setIfModifiedSince(const QDateTime & dt)
+ {
+ ifModifiedSince_ = dt;
+ haveIfModifiedSince_ = true;
+ }
+
+ void
+ Request::setIfUnmodifiedSince(const QDateTime & dt)
+ {
+ ifUnmodifiedSince_ = dt;
+ haveIfUnmodifiedSince_ = true;
+ }
+
+ uint
+ Request::protocolMajor() const
+ {
+ return protocolMajor_;
+ }
+
+ uint
+ Request::protocolMinor() const
+ {
+ return protocolMinor_;
+ }
+
+ float
+ Request::protocol() const
+ {
+ return (float(protocolMajor_) + float(protocolMinor_) / 10.0);
+ }
+
+ Request::Method
+ Request::method() const
+ {
+ return method_;
+ }
+
+ bool
+ Request::haveHost() const
+ {
+ return haveHost_;
+ }
+
+ bool
+ Request::haveIfModifiedSince() const
+ {
+ return haveIfModifiedSince_;
+ }
+
+ bool
+ Request::haveIfUnmodifiedSince() const
+ {
+ return haveIfUnmodifiedSince_;
+ }
+
+ QString
+ Request::path() const
+ {
+ return path_;
+ }
+
+ QString
+ Request::host() const
+ {
+ return host_;
+ }
+
+ QDateTime
+ Request::ifModifiedSince() const
+ {
+ return ifModifiedSince_;
+ }
+
+ QDateTime
+ Request::ifUnmodifiedSince() const
+ {
+ return ifUnmodifiedSince_;
+ }
+
+ QCString
+ Request::protocolString() const
+ {
+ QCString s("HTTP/");
+ s += QCString().setNum(protocolMajor_);
+ s += '.';
+ s += QCString().setNum(protocolMinor_);
+ return s;
+ }
+
+ void
+ Request::setExpectContinue(bool b)
+ {
+ expectContinue_ = b;
+ }
+
+ bool
+ Request::expectContinue() const
+ {
+ return expectContinue_;
+ }
+
+ void
+ Request::setRange(const QString & s)
+ {
+ kpfDebug << "Request::setRange(`" << s << "')" << endl;
+
+ haveRange_ = true;
+
+ ByteRangeList l(s, protocol());
+
+ ulong first (ULONG_MAX);
+ ulong last (0L);
+ bool haveLast (false);
+
+ for (ByteRangeList::ConstIterator it(l.begin()); it != l.end(); ++it)
+ {
+ ByteRange r(*it);
+ first = min(r.first(), first);
+
+ if (r.haveLast())
+ {
+ haveLast = true;
+ last = max(r.last(), last);
+ }
+ }
+
+ kpfDebug << "Request::setRange(): first == " << first << "d" << endl;
+
+ range_.setFirst(first);
+
+ if (haveLast)
+ {
+ kpfDebug << "Request::setRange(): last == " << last << "d" << endl;
+ range_.setLast(last);
+ }
+ kpfDebug << "Request::setRange(): no last" << endl;
+ }
+
+ ByteRange
+ Request::range() const
+ {
+ return range_;
+ }
+
+ bool
+ Request::haveRange() const
+ {
+ return haveRange_;
+ }
+
+ void
+ Request::setPersist(bool b)
+ {
+ if (protocol() > 1.0) // Bad, but makes wget work.
+ {
+ persist_ = b;
+ }
+ }
+
+ bool
+ Request::persist() const
+ {
+ return persist_;
+ }
+
+ void
+ Request::clear()
+ {
+ protocolMajor_ = 0;
+ protocolMinor_ = 9;
+ method_ = Unsupported;
+ haveHost_ = false;
+ haveIfModifiedSince_ = false;
+ haveIfUnmodifiedSince_ = false;
+ expectContinue_ = false;
+ haveRange_ = false;
+ persist_ = false;
+ path_ = QString::null;
+ host_ = QString::null;
+ ifModifiedSince_ = QDateTime();
+ ifUnmodifiedSince_ = QDateTime();
+ range_.clear();
+ }
+
+ QString
+ Request::clean(const QString & _path) const
+ {
+ QString s(_path);
+
+ while (s.endsWith("/./"))
+ s.truncate(s.length() - 2);
+
+ while (s.endsWith("/."))
+ s.truncate(s.length() - 1);
+
+ // Double slash -> slash.
+
+ QRegExp r("\\/\\/+");
+ s.replace(r, "/");
+
+ return s;
+ }
+
+} // End namespace KPF
+
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/Request.h b/kpf/src/Request.h
new file mode 100644
index 00000000..92034c9c
--- /dev/null
+++ b/kpf/src/Request.h
@@ -0,0 +1,252 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef KPF_REQUEST_H
+#define KPF_REQUEST_H
+
+#include <qcstring.h>
+#include <qstringlist.h>
+#include <qdatetime.h>
+
+#include "ByteRange.h"
+
+namespace KPF
+{
+ /**
+ * Represents an HTTP request.
+ */
+ class Request
+ {
+ public:
+
+ /**
+ * HTTP/1.1 specifies many request types, known as 'methods'.
+ * An HTTP/1.1 compliant server must implement HEAD and GET.
+ * We support only these two. All others are rejected either
+ * because they are of limited use or because they are a security
+ * risk.
+ */
+ enum Method { Head, Get, Unsupported };
+
+ /**
+ * Construct a Request object and set parameters to defaults.
+ */
+ Request();
+ virtual ~Request();
+
+ /**
+ * Take a list of headers and parse them, setting our values
+ * appropriately.
+ */
+ void parseHeaders(const QStringList &);
+
+ /**
+ * Parse one header line and set whatever value is appropriate.
+ */
+ void handleHeader(const QString & name, const QString & value);
+
+ /**
+ * HTTP has had a few revisions (protocols) since birth. Here you may
+ * specify the protocol which you believe the client is using. This
+ * version of setProtocol parses a string of the form
+ * "HTTP/major.minor", where major and minor are integers.
+ */
+ void setProtocol (const QString &);
+
+ /**
+ * HTTP has had a few revisions (protocols) since birth. Here you may
+ * specify the protocol which you believe the client is using.
+ */
+ void setProtocol (uint major, uint minor);
+
+ /**
+ * Specify the 'method' requested by the client. This version parses
+ * a string.
+ */
+ void setMethod (const QString &);
+
+ /**
+ * Specify the 'method' requested by the client.
+ */
+ void setMethod (Method);
+
+ /**
+ * Set the path to the requested resource. The path is
+ * immediately decoded and canonicalised.
+ */
+ void setPath (const QString &);
+
+ /**
+ * HTTP/1.1 requests must have a "Host:" header.
+ */
+ void setHost (const QString &);
+
+ /**
+ * HTTP/1.1 allows an "If-Modified-Since" header, specifying that
+ * the requested resource should only be retrieved if the resource
+ * has been modified since a certain date and time.
+ */
+ void setIfModifiedSince (const QDateTime &);
+
+ /**
+ * HTTP/1.1 allows an "If-Unmodified-Since" header, specifying that
+ * the requested resource should only be retrieved if the resource
+ * has NOT been modified since a certain date and time.
+ */
+ void setIfUnmodifiedSince (const QDateTime &);
+
+ /**
+ * HTTP/1.1 allows an "Expect: 100-continue" header, specifying
+ * that the server should immediately respond with "100 Continue".
+ */
+ void setExpectContinue (bool);
+
+ /**
+ * Specify a range of bytes which should be retrieved from the
+ * resource. See RFC 2616.
+ */
+ void setRange (const QString &);
+
+ /**
+ * HTTP/1.1 allows "persistent" connections. These may be specified
+ * by "Keep-Alive:" or "Connection: Keep-Alive" headers.
+ */
+ void setPersist (bool);
+
+ /**
+ * @return major version of protocol which was set.
+ */
+ uint protocolMajor() const;
+
+ /**
+ * @return minor version of protocol which was set.
+ */
+ uint protocolMinor() const;
+
+ /**
+ * @return version of protocol which was set, as major.minor, e.g.
+ * if the protocol was set to HTTP/0.9, this would return 0.9.
+ */
+ float protocol() const;
+
+ /**
+ * @return the (enumerated) request type ('method').
+ */
+ Method method() const;
+
+ /**
+ * @return true if @ref setHost was called previously.
+ */
+ bool haveHost() const;
+
+ /**
+ * @return true if @ref setIfModifiedSince was called previously.
+ */
+ bool haveIfModifiedSince() const;
+
+ /**
+ * @return true if @ref setIfUnmodifiedSince was called previously.
+ */
+ bool haveIfUnmodifiedSince() const;
+
+ /**
+ * @return the path that was set with (and modified by) setPath()
+ */
+ QString path() const;
+
+ /**
+ * @return the host that was set previously.
+ */
+ QString host() const;
+
+ /**
+ * @return the date/time set by @ref setIfModifiedSince previously.
+ */
+ QDateTime ifModifiedSince() const;
+
+ /**
+ * @return the date/time set by @ref setIfUnmodifiedSince previously.
+ */
+ QDateTime ifUnmodifiedSince() const;
+
+ /**
+ * @return the protocol as a string, e.g. "HTTP/1.1".
+ */
+ QCString protocolString() const;
+
+ /**
+ * @return true if @ref setExpectContinue was used previously.
+ */
+ bool expectContinue() const;
+
+ /**
+ * @return byte range set by @ref setRange previously.
+ */
+ ByteRange range() const;
+
+ /**
+ * @return true if @ref setRange was called previously.
+ */
+ bool haveRange() const;
+
+ /**
+ * @return true if @ref setPersist was called previously.
+ */
+ bool persist() const;
+
+ /**
+ * Reset to initial state.
+ */
+ void clear();
+
+ /**
+ * Clean up the path given in a request, so it's more like what we
+ * expect.
+ */
+ QString clean(const QString & path) const;
+
+ private:
+
+ // Order dependency
+ uint protocolMajor_;
+ uint protocolMinor_;
+ Method method_;
+ bool haveHost_;
+ bool haveIfModifiedSince_;
+ bool haveIfUnmodifiedSince_;
+ bool expectContinue_;
+ bool haveRange_;
+ bool persist_;
+ // End order dependency
+
+ QString path_;
+ QString host_;
+ QDateTime ifModifiedSince_;
+ QDateTime ifUnmodifiedSince_;
+ ByteRange range_;
+ };
+
+} // End namespace KPF
+
+#endif
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/Resource.cpp b/kpf/src/Resource.cpp
new file mode 100644
index 00000000..d5e77072
--- /dev/null
+++ b/kpf/src/Resource.cpp
@@ -0,0 +1,346 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include <qstringlist.h>
+#include <qdir.h>
+#include <qfile.h>
+#include <qfileinfo.h>
+#include <qregexp.h>
+#include <kglobal.h>
+
+#include <kmimetype.h>
+
+#include "Utils.h"
+#include "Defines.h"
+#include "Resource.h"
+#include "DirectoryLister.h"
+
+namespace KPF
+{
+ enum FileType { Dir, File };
+
+ class Resource::Private
+ {
+ public:
+
+ Private()
+ : size(0),
+ sizeCalculated(false),
+ offset(0)
+ {
+ }
+
+ QString root;
+ FileType fileType;
+ QString path;
+ QFile file;
+ QFileInfo fileInfo;
+ QDir dir;
+ uint size;
+ bool sizeCalculated;
+ uint offset;
+
+ QByteArray html;
+ };
+
+ Resource::Resource()
+ {
+ d = new Private;
+ }
+
+ Resource::~Resource()
+ {
+ delete d;
+ d = 0;
+ }
+
+ void
+ Resource::setPath(const QString & root, const QString & relativePath)
+ {
+ kpfDebug << "setPath(`" << root << "',`" << relativePath << "'" << endl;
+
+ d->root = root;
+ d->path = relativePath;
+ d->size = 0;
+ d->offset = 0;
+ d->sizeCalculated = false;
+
+ d->file.close();
+
+ // Fix root if it doesn't have a trailing slash.
+
+ if ('/' != d->root.at(d->root.length() - 1))
+ d->root += '/';
+
+ if (d->path.right(1) == "/")
+ {
+ // A directory was requested
+ kpfDebug << "Directory requested" << endl;
+
+ // Does the path actually point to a directory ?
+
+ if (QFileInfo(d->root + d->path).isDir())
+ {
+ kpfDebug << "Path points to directory" << endl;
+
+ // Does an index.html exist in that directory ?
+
+ if (QFileInfo(d->root + d->path + "index.html").exists())
+ {
+ kpfDebug << "Found index.html" << endl;
+
+ // Ok, add `index.html'.
+
+ d->path += "index.html";
+ }
+ else
+ {
+ kpfDebug << "NOT Found index.html" << endl;
+ }
+
+ }
+ else
+ {
+ kpfDebug << "NOT Path points to directory" << endl;
+ }
+ }
+ else
+ {
+ kpfDebug << "NOT Directory requested" << endl;
+ }
+
+ kpfDebug << "QFileInfo::setFile(`" << d->root << "' + `" << d->path << "'" << endl;
+ d->fileInfo.setFile(d->root + d->path);
+ }
+
+ bool
+ Resource::open()
+ {
+ if (!d->fileInfo.exists())
+ {
+ kpfDebug << "File doesn't exist" << endl;
+ return false;
+ }
+
+ if (d->fileInfo.isDir())
+ {
+ d->fileType = Dir;
+ d->dir.setPath(d->root + d->path);
+
+ if (!d->dir.isReadable())
+ {
+ kpfDebug << "Dir isn't readable" << endl;
+ return false;
+ }
+ else
+ {
+ generateHTML();
+ }
+ }
+ else
+ {
+ d->fileType = File;
+ d->file.setName(d->root + d->path);
+
+ if (!d->file.open(IO_ReadOnly))
+ {
+ kpfDebug << "File isn't readable" << endl;
+ return false;
+ }
+ }
+
+ calculateSize();
+ return true;
+ }
+
+ void
+ Resource::close()
+ {
+ if (File == d->fileType)
+ d->file.close();
+ }
+
+ bool
+ Resource::seek(int pos)
+ {
+ if (File == d->fileType)
+ {
+ return d->file.at(pos);
+ }
+ else
+ {
+ // TODO STUB
+ return false;
+ }
+ }
+
+ int
+ Resource::readBlock(char * data, uint maxlen)
+ {
+ int bytesRead(0);
+
+ if (File == d->fileType)
+ {
+ bytesRead = d->file.readBlock(data, maxlen);
+ }
+ else
+ {
+ if (d->offset < d->size)
+ {
+ uint bytesAvailable = QMIN(maxlen, d->size - d->offset);
+
+ memcpy(data, d->html.data() + d->offset, bytesAvailable);
+
+ d->offset += bytesAvailable;
+
+ return bytesAvailable;
+ }
+ else
+ {
+ // Else bytesRead is still 0, because the read was out of bounds.
+
+ kpfDebug << "Out of bounds in html" << endl;
+ }
+ }
+
+ return bytesRead;
+ }
+
+ uint
+ Resource::size() const
+ {
+ return d->size;
+ }
+
+ int
+ Resource::at() const
+ {
+ return d->offset;
+ }
+
+ bool
+ Resource::atEnd() const
+ {
+ if (File == d->fileType)
+ {
+ return d->file.atEnd();
+ }
+ else
+ {
+ return d->offset >= d->size;
+ }
+ }
+
+ void
+ Resource::calculateSize()
+ {
+ if (File == d->fileType)
+ {
+ d->size = d->fileInfo.size();
+ }
+ else
+ {
+ d->size = d->html.size() - 1;
+ }
+ }
+
+
+ bool
+ Resource::readable() const
+ {
+ return d->fileInfo.isReadable();
+ }
+
+ QDateTime
+ Resource::lastModified() const
+ {
+ return d->fileInfo.lastModified();
+ }
+
+ bool
+ Resource::exists() const
+ {
+ bool b = d->fileInfo.exists();
+
+ if (!b)
+ {
+ kpfDebug << "File doesn't exist" << endl;
+ }
+
+ return b;
+ }
+
+ bool
+ Resource::symlink() const
+ {
+ if (d->fileInfo.isSymLink())
+ return true;
+
+ QString path(d->fileInfo.dirPath());
+
+ QStringList l(QStringList::split('/', path));
+
+ QString testPath;
+
+ for (QStringList::ConstIterator it(l.begin()); it != l.end(); ++it)
+ {
+ testPath += '/';
+ testPath += *it;
+
+ if (QFileInfo(testPath).isSymLink())
+ return true;
+ }
+
+ return false;
+ }
+
+ bool
+ Resource::seekable() const
+ {
+ return !(d->fileInfo.isDir());
+ }
+
+ QString
+ Resource::mimeType() const
+ {
+ if (d->fileInfo.isDir())
+ return "text/html; charset=utf-8";
+ return KMimeType::findByPath( d->root + d->path )->name();
+ }
+
+ void
+ Resource::generateHTML()
+ {
+ d->html = DirectoryLister::instance()->html(d->root, d->path);
+ }
+
+ void
+ Resource::clear()
+ {
+ delete d;
+ d = new Private;
+ }
+
+} // End namespace KPF
+
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/Resource.h b/kpf/src/Resource.h
new file mode 100644
index 00000000..870daed7
--- /dev/null
+++ b/kpf/src/Resource.h
@@ -0,0 +1,149 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef KPF_RESOURCE_H
+#define KPF_RESOURCE_H
+
+#include <qstring.h>
+#include <qdatetime.h>
+
+namespace KPF
+{
+ /**
+ * Provide any resource the client requests. This may be a file, a
+ * directory listing, or nothing at all, depending on the flabbiness
+ * of the client's arse.
+ */
+ class Resource
+ {
+ public:
+
+ /**
+ * Default ctor, object unusable until you setPath().
+ */
+ Resource();
+
+ /**
+ * Closes all open files.
+ */
+ virtual ~Resource();
+
+ /**
+ * Reset this object and tell it what the new paths are.
+ */
+ void setPath(const QString & root, const QString & relativePath);
+
+ /**
+ * @return true if the file was opened ok or the dir was readable.
+ */
+ bool open();
+
+ /**
+ * Just close.
+ */
+ void close();
+
+ /**
+ * Seek to the specified position.
+ * @return false if this is a dir.
+ */
+ bool seek(int);
+
+ /**
+ * Read a block of the file or the generated HTML.
+ */
+ int readBlock(char * data, uint maxlen);
+
+ /**
+ * @return false if the file or directory doesn't exist.
+ */
+ bool exists() const;
+
+ /**
+ * Performs a search through the entire path, looking for symbolic links.
+ *
+ * Expensive !
+ *
+ * @return true if the path contains a symbolic link.
+ */
+ bool symlink() const;
+
+ /**
+ * @return true if the resource is readable.
+ */
+ bool readable() const;
+
+ /**
+ * @return mtime of resource.
+ */
+ QDateTime lastModified() const;
+
+ /**
+ * @return size of file, or size of HTML that will be generated.
+ */
+ uint size() const;
+
+ /**
+ * @return current file position.
+ */
+ int at() const;
+
+ /**
+ * @return true if nothing left to read.
+ */
+ bool atEnd() const;
+
+ /**
+ * @return true if file, false if dir. Perhaps I'll make the HTML
+ * seekable later.
+ */
+ bool seekable() const;
+
+ /**
+ * @return mime type of file if available. If dir, returns text/html.
+ * If nothing available, returns text/plain.
+ */
+ QString mimeType() const;
+
+ /**
+ * Reset to initial state.
+ */
+ void clear();
+
+ private:
+
+ /**
+ * Update d->size;
+ */
+ void calculateSize();
+
+ void generateHTML();
+
+ class Private;
+ Private * d;
+ };
+
+} // End namespace KPF
+
+#endif
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/Response.cpp b/kpf/src/Response.cpp
new file mode 100644
index 00000000..f9ce46d8
--- /dev/null
+++ b/kpf/src/Response.cpp
@@ -0,0 +1,194 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include <qfile.h>
+#include <qtextstream.h>
+#include <qregexp.h>
+
+#include <kconfig.h>
+
+#include "Defines.h"
+#include "Utils.h"
+#include "Response.h"
+#include "Defaults.h"
+
+namespace KPF
+{
+ Response::Response()
+ : code_(0),
+ size_(0)
+ {
+ // Empty.
+ }
+
+ void
+ Response::setCode(uint code)
+ {
+ code_ = code;
+ }
+
+ void
+ Response::setSize(ulong size)
+ {
+ size_ = size;
+ }
+
+ Response::~Response()
+ {
+ // Empty.
+ }
+
+ bool
+ Response::valid() const
+ {
+ return 0 != code_;
+ }
+
+ ulong
+ Response::size() const
+ {
+ return size_;
+ }
+
+ uint
+ Response::code() const
+ {
+ return code_;
+ }
+
+ QCString
+ Response::text(const Request & request) const
+ {
+ QString s;
+
+ // XXX: Ensure that all codes we know about are enumerated here.
+ switch (code_)
+ {
+ case 200:
+ case 206:
+ case 304:
+ if (request.protocol() >= 1.0)
+ {
+ s = QString(request.protocolString())
+ + QString(" %1 %2\r\n").arg(code_).arg(responseName(code_));
+ }
+ break;
+
+ case 400:
+ case 403:
+ case 404:
+ case 412:
+ case 416:
+ case 500:
+ case 501:
+ case 505:
+ s = QString(request.protocolString())
+ + QString(" %1 %2\r\n").arg(code_).arg(responseName(code_))
+ + data(code_, request);
+ break;
+
+ default:
+ kpfDebug << "Huh ?" << endl;
+ break;
+ }
+
+ return s.utf8();
+ }
+
+ QString
+ Response::data(uint code, const Request & request) const
+ {
+ QString contentType = "Content-Type: text/html; charset=utf-8\r\n";
+
+ KConfig config(Config::name());
+
+ config.setGroup("General");
+
+ QString html;
+
+ if
+ (
+ config.readBoolEntry
+ (Config::key(Config::CustomErrors), Config::DefaultCustomErrors)
+ )
+ {
+ config.setGroup("ErrorMessageOverrideFiles");
+
+ QString filename = config.readPathEntry(QString::number(code));
+
+ if (!filename.isEmpty())
+ {
+ QFile f(filename);
+
+ if (f.open(IO_ReadOnly))
+ {
+ QRegExp regexpMessage ("ERROR_MESSAGE");
+ QRegExp regexpCode ("ERROR_CODE");
+ QRegExp regexpResource ("RESOURCE");
+
+ QTextStream str(&f);
+
+ while (!str.atEnd())
+ {
+ QString line(str.readLine());
+
+ line.replace(regexpMessage, responseName(code));
+ line.replace(regexpCode, QString::number(code));
+ line.replace(regexpResource, request.path());
+
+ html = line + "\r\n";
+ }
+ }
+ }
+ }
+ else
+ {
+ html = "<html>\r\n";
+ html += "<head>\r\n";
+ html += "<title>\r\n" + responseName(code) + "</title>\r\n";
+ html += "<style type=\"text/css\">\r\n";
+ html += "BODY { color: black; background-color: rgb(228, 228, 228); }\r\n";
+ html += "H1 { font-size: 1.7em; color: rgb(60, 85, 110); }\r\n";
+ html += "P { margin: 40px, 40px, 10px, 10px; }\r\n";
+ html += "</style>\r\n";
+ html += "</head>\r\n";
+ html += "<body>\r\n<h1>\r\nError: " + responseName(code) + "\r\n</h1>\r\n";
+ html += "<p>Requested resource: " + request.path() + "</p>\r\n";
+ html += "</body>\r\n</html>\r\n";
+ }
+
+ QString contentLength =
+ QString("Content-Length: %1\r\n").arg(html.length());
+
+ return (contentType + contentLength + "\r\n" + html);
+ }
+
+ void
+ Response::clear()
+ {
+ code_ = size_ = 0;
+ }
+
+} // End namespace KPF
+
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/Response.h b/kpf/src/Response.h
new file mode 100644
index 00000000..c24ba69c
--- /dev/null
+++ b/kpf/src/Response.h
@@ -0,0 +1,102 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef KPF_RESPONSE_H
+#define KPF_RESPONSE_H
+
+#include "Request.h"
+
+namespace KPF
+{
+ /**
+ * Represents some of the data which is used as a reponse to an
+ * HTTP request.
+ */
+ class Response
+ {
+ public:
+
+ /**
+ *
+ */
+ Response();
+
+ /**
+ *
+ */
+ virtual ~Response();
+
+ /**
+ * Each response has a code. See the HTTP specification.
+ */
+ void setCode(uint);
+
+ /**
+ * Set the size, in bytes, of the resource that will be transferred
+ * to the client.
+ */
+ void setSize(ulong);
+
+ /**
+ * @return true if code isn't 0.
+ */
+ bool valid() const;
+
+ /**
+ * @return size of requested resource.
+ */
+ ulong size() const;
+
+ /**
+ * @return HTTP response code.
+ */
+ uint code() const;
+
+ /**
+ * @return header/body data to send to the client. This string is
+ * constructed differently depending on HTTP response code.
+ */
+ QCString text(const Request &) const;
+
+ /**
+ * Reset to initial state.
+ */
+ void clear();
+
+ protected:
+
+ /**
+ * @internal
+ * Create HTML.
+ */
+ QString data(uint, const Request &) const;
+
+ private:
+
+ uint code_;
+ uint size_;
+ };
+
+} // End namespace KPF
+#endif
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/RootValidator.cpp b/kpf/src/RootValidator.cpp
new file mode 100644
index 00000000..17d6a43a
--- /dev/null
+++ b/kpf/src/RootValidator.cpp
@@ -0,0 +1,66 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include <qfileinfo.h>
+#include <qdir.h>
+
+#include "RootValidator.h"
+#include "WebServerManager.h"
+
+namespace KPF
+{
+ RootValidator::RootValidator(QObject * parent, const char * name)
+ : QValidator(parent, name)
+ {
+ }
+
+ QValidator::State
+ RootValidator::validate(QString & input, int & /* unused */) const
+ {
+ QString root(input);
+
+ if ('/' == root.at(root.length() - 1))
+ {
+ root.truncate(root.length() - 1);
+ }
+
+ // Duplicate ?
+
+ if (0 != WebServerManager::instance()->server(root))
+ return Intermediate;
+
+ QFileInfo fi(root);
+
+ if (!fi.isDir())
+ return Intermediate;
+
+// Disabling disallowing of ~, on request.
+// if (fi.dirPath() == QDir::homeDirPath())
+// return Intermediate;
+
+ return Acceptable;
+ }
+
+} // End namespace KPF
+
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/RootValidator.h b/kpf/src/RootValidator.h
new file mode 100644
index 00000000..5ac96829
--- /dev/null
+++ b/kpf/src/RootValidator.h
@@ -0,0 +1,47 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef KPF_ROOT_VALIDATOR_H
+#define KPF_ROOT_VALIDATOR_H
+
+#include <qvalidator.h>
+
+namespace KPF
+{
+ /**
+ * Used for checking that a path input by the user is acceptable
+ * as a server root directory.
+ */
+ class RootValidator : public QValidator
+ {
+ public:
+
+ RootValidator(QObject * parent, const char * name = 0);
+
+ virtual State validate(QString & input, int & pos) const;
+ };
+
+} // End namespace KPF
+
+#endif // KPF_ROOT_VALIDATOR_H
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/Server.cpp b/kpf/src/Server.cpp
new file mode 100644
index 00000000..3e59281a
--- /dev/null
+++ b/kpf/src/Server.cpp
@@ -0,0 +1,1137 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include "Defines.h"
+#include "DirectoryLister.h"
+#include "WebServer.h"
+#include "Server.h"
+#include "ServerPrivate.h"
+#include "Utils.h"
+
+#undef KPF_TRAFFIC_DEBUG
+
+namespace KPF
+{
+ static const uint IncomingDataLimit = 8 * 1024; // kB.
+ static const uint Timeout = 60 * 1000; // seconds.
+ static const uint MaxKeepAlive = 20; // transactions.
+
+ Server::Server
+ (
+ const QString & dir,
+ bool followSymlinks,
+ int socket,
+ WebServer * parent
+ )
+ : QObject(parent, "Server")
+ {
+ d = new Private;
+
+ kpfDebug << "New server: " << d->id << endl;
+
+ d->dir = dir;
+
+ d->followSymlinks = followSymlinks;
+
+ d->birth = QDateTime::currentDateTime();
+
+ d->socket.setSocket(socket);
+
+ connect(&(d->socket), SIGNAL(readyRead()), this, SLOT(slotReadyRead()));
+
+ connect
+ (
+ &(d->socket),
+ SIGNAL(bytesWritten(int)),
+ SLOT(slotBytesWritten(int))
+ );
+
+ connect
+ (
+ &(d->socket),
+ SIGNAL(connectionClosed()),
+ SLOT(slotConnectionClosed())
+ );
+
+ connect
+ (
+ &(d->idleTimer),
+ SIGNAL(timeout()),
+ SLOT(slotTimeout())
+ );
+
+ connect
+ (
+ &(d->readTimer),
+ SIGNAL(timeout()),
+ SLOT(slotRead())
+ );
+
+ // If nothing happens for a bit, cancel ourselves.
+
+ d->idleTimer.start(Timeout, true);
+ }
+
+ Server::~Server()
+ {
+ delete d;
+ d = 0;
+ }
+
+ void
+ Server::slotReadyRead()
+ {
+ kpfDebug << d->id << ":slotReadyRead" << endl;
+
+ // DoS protection.
+
+ d->dataRead += static_cast<uint>(d->socket.bytesAvailable());
+
+ if (d->dataRead > IncomingDataLimit)
+ {
+ kpfDebug
+ << d->id
+ << ": Read would breach limit. Assuming DoS -> finished"
+ << endl;
+
+ setFinished(NoFlush /* Don't bother flushing socket */);
+ return;
+ }
+
+ // Reset idle timer.
+
+ d->idleTimer.start(Timeout, true);
+
+ // Read all available data to incomingLineBuffer.
+
+ while (d->socket.canReadLine())
+ {
+ kpfDebug << d->id << ": socket.canReadLine" << endl;
+
+ QString line(d->socket.readLine().stripWhiteSpace());
+
+#ifdef KPF_TRAFFIC_DEBUG
+ kpfDebug
+ << d->id
+ << ": Adding line to incomingLineBuffer: "
+ << line
+ << endl;
+#endif
+
+ d->incomingLineBuffer.append(line);
+ }
+
+ if (!d->incomingLineBuffer.isEmpty())
+ {
+ kpfDebug
+ << d->id
+ << ": incomingLineBuffer isn't empty - calling slotRead directly"
+ << endl;
+
+ slotRead();
+ }
+ else
+ {
+ kpfDebug
+ << d->id
+ << ": incomingLineBuffer is empty. Nothing to do."
+ << endl;
+ }
+ }
+
+ void
+ Server::slotRead()
+ {
+ kpfDebug << d->id << ": slotRead" << endl;
+
+ if (d->incomingLineBuffer.isEmpty())
+ {
+ kpfDebug << d->id << ": incomingLineBuffer is empty !" << endl;
+ return;
+ }
+
+ // There is data available in incomingLineBuffer.
+
+ switch (d->state)
+ {
+ case WaitingForRequest:
+ kpfDebug << d->id << ": I was waiting for a request" << endl;
+ (void) readRequest(d->incomingLineBuffer.first());
+ d->incomingLineBuffer.remove(d->incomingLineBuffer.begin());
+ break;
+
+ case WaitingForHeaders:
+ kpfDebug << d->id << ": I was waiting for headers" << endl;
+ readHeaders();
+ break;
+
+ case Responding:
+ case Finished:
+ default:
+ kpfDebug << d->id << ": I was responding or finished" << endl;
+ break;
+ }
+ }
+
+ bool
+ Server::readRequest(const QString & line)
+ {
+ ++d->requestCount;
+
+#ifdef KPF_TRAFFIC_DEBUG
+ kpfDebug
+ << d->id
+ << ": (request #" << d->requestCount << ") readRequest: `"
+ << line << "'" << endl;
+#endif
+
+ QStringList l(QStringList::split(' ', line));
+
+ // A request usually looks like METHOD PATH PROTOCOL but we don't
+ // require PROTOCOL - we just assume HTTP/0.9 and act accordingly.
+
+ if (l.count() == 2)
+ {
+ kpfDebug << d->id << ": readRequest: HTTP/0.9 ???" << endl;
+ emit(request(this));
+ d->state = Responding;
+ respond(400);
+ emit(readyToWrite(this));
+ return false;
+ }
+
+ // The Request object handles setting parsing the strings we pass it here.
+ // It converts GET/HEAD/whatever to an enum, fixes up the path and
+ // converts the protocol string to a number.
+
+ d->request.setMethod (l[0]);
+ d->request.setPath (l[1]);
+ d->request.setProtocol (l.count() == 3 ? l[2] : QString::null);
+
+ // Before we check the request, say we received it.
+
+ emit(request(this));
+
+ return checkRequest();
+ }
+
+ bool
+ Server::checkRequest()
+ {
+ // We only handle METHOD of GET or HEAD.
+
+ if (Request::Unsupported == d->request.method())
+ {
+ kpfDebug << d->id << ": readRequest: method unsupported" << endl;
+ d->state = Responding;
+ respond(501);
+ emit(readyToWrite(this));
+ return false;
+ }
+
+ // If there's .. or ~ in the path, we disallow. Either there's a mistake
+ // or someone's trying to h@x0r us. I wouldn't have worried about ~
+ // normally, because I don't do anything with it, so the resource would
+ // simply not be found, but I'm worried that the QDir/QFile/QFileInfo
+ // stuff might try to expand it, so I'm not taking any chances.
+
+ if (d->request.path().contains("..") || d->request.path().contains('~'))
+ {
+ kpfDebug << d->id << ": readRequest: bogus path" << endl;
+ d->state = Responding;
+ respond(403);
+ emit(readyToWrite(this));
+ return false;
+ }
+
+ if (d->request.protocol() > 1.1)
+ {
+ if (d->request.protocol() >= 2.0)
+ {
+ kpfDebug
+ << d->id
+ << ": readRequest: unsupported protocol major number"
+ << endl;
+
+ d->request.setProtocol(1, 1);
+
+ d->state = Responding;
+ respond(505);
+ emit(readyToWrite(this));
+ return false;
+ }
+ else
+ {
+ kpfDebug
+ << d->id
+ << ": readRequest: unsupported protocol minor number"
+ << endl;
+
+ d->request.setProtocol(1, 1);
+ }
+ }
+
+ if (d->request.protocol() >= 1.0)
+ {
+ kpfDebug
+ << d->id
+ << ": readRequest: need to wait for headers now"
+ << endl;
+
+ if (d->request.protocol() > 1.0)
+ {
+ d->request.setPersist(true);
+ }
+
+ d->state = WaitingForHeaders;
+ d->readTimer.start(0, true);
+ }
+ else
+ {
+ kpfDebug
+ << d->id
+ << ": readRequest: immediate response"
+ << endl;
+
+ d->state = Responding;
+ prepareResponse();
+ emit(readyToWrite(this));
+ return true;
+ }
+
+ return true;
+ }
+
+ void
+ Server::readHeaders()
+ {
+ kpfDebug << d->id << ": readHeaders" << endl;
+
+ // Pop lines from front of incomingLineBuffer and add to
+ // incomingHeaderLineBuffer until we reach the end of the headers, when we
+ // generate a response to the request.
+
+ while (!d->incomingLineBuffer.isEmpty())
+ {
+ // This would be cleaner if there was a QValueQueue.
+ // Basically we 'dequeue' the first line from incomingHeaderBuffer.
+
+ QString line(d->incomingLineBuffer.first());
+ d->incomingLineBuffer.remove(d->incomingLineBuffer.begin());
+
+ // Unless the line is empty, this is (in theory) a header.
+
+ if (!line.isEmpty())
+ {
+ kpfDebug << d->id << ": Header line: " << line << endl;
+ d->incomingHeaderLineBuffer << line;
+ }
+ else
+ {
+ kpfDebug << d->id << ": Blank line - end of headers" << endl;
+
+ // We have a buffer filled with all the header data received.
+ // First parse those headers.
+
+ d->request.parseHeaders(d->incomingHeaderLineBuffer);
+
+ // Clear out the buffer because we won't need to use it again
+ // and leaving all that data in memory is pointless.
+
+ d->incomingHeaderLineBuffer.clear();
+
+ // We've parsed the headers so the next thing we do is respond.
+
+ d->state = Responding;
+ prepareResponse();
+
+ // When the response has been prepared, we're ready to write
+ // some data back into that socket.
+
+ kpfDebug << d->id << ": Ready to write" << endl;
+
+ emit(readyToWrite(this));
+
+ return;
+ }
+ }
+
+ // Because we haven't found an empty line and therefore parsed
+ // headers + returned, we must wait for more headers.
+
+ kpfDebug
+ << d->id
+ << ": readHeaders: No lines left in header buffer."
+ << " Setting state to WaitingForHeaders"
+ << endl;
+
+ d->state = WaitingForHeaders;
+ }
+
+ void
+ Server::prepareResponse()
+ {
+ // The path to the requested resource is relative to our root.
+
+ QString filename = d->dir + '/' + d->request.path();
+
+ kpfDebug << d->id << ": filename: " << filename << endl;
+
+ d->resource.setPath(d->dir, d->request.path());
+
+ if (!d->resource.exists())
+ {
+ kpfDebug << d->id << ": Resource doesn't exist: %s" << filename << endl;
+
+ // No file ? Perhaps we should give a directory listing.
+
+ if (!(/*d->generateDirectoryListings && */ d->request.path() == "/"))
+ {
+ // Either index.html wasn't the file requested, or we're not supposed
+ // to generated listings.
+
+ respond(404);
+ return;
+ }
+ }
+
+ if (!d->followSymlinks && d->resource.symlink())
+ {
+ // If we're not supposed to follow symlinks and there's a symlink
+ // somewhere on the path, deny.
+
+ respond(403);
+ return;
+ }
+
+ if (!d->resource.readable())
+ {
+ // Deny even HEAD for unreadable files.
+
+ respond(403);
+ return;
+ }
+
+// if ((Request::Get == d->request.method()) && !d->resource.open())
+ // Open resource even if we're asked for HEAD. We need to ensure
+ // Content-Length is sent correctly.
+
+ if (!d->resource.open())
+ {
+ // Couldn't open the file. XXX why not ?
+
+ respond(403);
+ return;
+ }
+
+ if (d->request.haveRange())
+ {
+ // There was a byte range specified in the request so handleRange()
+ // to check that the range makes sense for the requested resource.
+
+ kpfDebug << d->id << ": Byte range in request" << endl;
+
+ if (!handleRange(d->request.range()))
+ {
+ // handleRange() takes care of sending the necessary response.
+ return;
+ }
+ }
+ else
+ {
+ kpfDebug
+ << d->id
+ << "No byte range in request."
+ << endl;
+
+ if (d->request.haveIfModifiedSince())
+ {
+ // If we saw an If-Modified-Since header and the resource hasn't
+ // been modified since that date, we respond with '304 Not modified'
+
+ if (toGMT(d->resource.lastModified()) <= d->request.ifModifiedSince())
+ {
+ kpfDebug
+ << d->id
+ << "Got IfModifiedSince and will respond with 304 (unmodified)"
+ << endl;
+
+ respond(304);
+ // We will not serve the file, so don't the size.
+ }
+ else
+ {
+ kpfDebug
+ << d->id
+ << "Got IfModifiedSince and will serve whole file (modified)"
+ << endl;
+
+ // We will serve the file, so set the size.
+ d->fileBytesLeft = d->resource.size();
+ }
+ }
+ else if (d->request.haveIfUnmodifiedSince())
+ {
+ // As above, except the logic is reversed.
+
+ if (toGMT(d->resource.lastModified()) > d->request.ifUnmodifiedSince())
+ {
+ kpfDebug
+ << d->id
+ << "Got IfUnmodifiedSince and will respond with 412 (modified)"
+ << endl;
+
+ respond(412);
+ // We not serve the file, so don't set the size.
+ }
+ else
+ {
+ kpfDebug
+ << d->id
+ << "Got IfUnmodifiedSince and will serve whole file (unmodified)"
+ << endl;
+
+ // We will serve the file, so set the size.
+ d->fileBytesLeft = d->resource.size();
+ }
+ }
+ else
+ {
+ // We will serve the file, so set the size.
+ d->fileBytesLeft = d->resource.size();
+ }
+
+ // If we haven't set the response up yet, that means we are not using a
+ // special response due to a modification time condition. Therefore we
+ // are doing the 'usual' 200 response.
+
+ if (0 == d->response.code())
+ respond(200, d->fileBytesLeft);
+ }
+
+ kpfDebug
+ << d->id
+ << "Done setting up response. Code will be "
+ << responseName(d->response.code())
+ << endl;
+
+
+ // Send some headers back to the client, but only if the protocol
+ // they asked us to use is new enough to require this.
+
+ if (d->request.protocol() >= 1.0)
+ {
+ writeLine("Server: kpf");
+ writeLine("Date: " + dateString());
+ writeLine("Last-Modified: " + dateString(d->resource.lastModified()));
+ writeLine("Content-Type: " + d->resource.mimeType());
+
+ // Generate a Content-Range header if we are sending partial content.
+
+ if (206 == d->response.code())
+ {
+ QString line = "Content-Range: bytes ";
+
+ line += QString::number(d->request.range().first());
+
+ line += '-';
+
+ if (d->request.range().haveLast())
+ line += QString::number(d->request.range().last());
+ else
+ line += QString::number(d->resource.size() - 1);
+
+ line += '/';
+
+ line += QString::number(d->resource.size());
+
+ writeLine(line);
+ }
+
+ writeLine("Content-Length: " + QString::number(d->fileBytesLeft));
+ }
+
+ if (d->requestCount >= MaxKeepAlive && d->request.protocol() >= 1.1)
+ {
+ // We have made many transactions on this connection. Time to
+ // give up and let the client re-connect. If we don't do this,
+ // they could keep this connection open indefinitely.
+
+ writeLine("Connection: close");
+ }
+ else if (d->request.protocol() == 1.0)
+ {
+ // wget seems broken. If it sends keep-alive, it hangs waiting for
+ // nothing at all. Ignore its keep-alive request.
+ writeLine("Connection: close");
+ }
+ else if (d->request.protocol() == 1.1) {
+ writeLine("Connection: keep-alive");
+ }
+
+ // End of headers so send a newline.
+
+ if (d->request.protocol() >= 1.0)
+ {
+ writeLine("");
+ }
+ }
+
+ bool
+ Server::handleRange(const ByteRange & r)
+ {
+ // Here we check if the given ByteRange makes sense for the
+ // requested resource.
+
+ // Is the range just plain broken ?
+
+ if (!r.valid())
+ {
+ kpfDebug << d->id << ": Invalid byte range" << endl;
+ respond(416);
+ return false;
+ }
+
+ // Does the range start before the end of our resource ?
+
+ else if (r.first() > d->resource.size())
+ {
+ kpfDebug << d->id << ": Range starts after EOF" << endl;
+ respond(416);
+ return false;
+ }
+
+ // Does the range end after the end of our resource ?
+
+ else if (r.haveLast() && r.last() > d->resource.size())
+ {
+ kpfDebug << d->id << ": Range end after EOF" << endl;
+ respond(416);
+ return false;
+ }
+
+ // Ok, in theory the range should be satisfiable ...
+
+ else
+ {
+ // ... but maybe we can't seek to the start of the range.
+
+ if (!d->resource.seek(r.first()))
+ {
+ kpfDebug << d->id << ": Invalid byte range (couldn't seek)" << endl;
+ // Should this be 501 ?
+ respond(416);
+ return false;
+ }
+
+ kpfDebug << d->id << ": Ok, setting fileBytesLeft" << endl;
+
+ // Work out how many bytes are left to send to the client. Careful
+ // with the off-by-one errors here, eh ?
+
+ if (r.haveLast())
+ {
+ d->fileBytesLeft = r.last() + 1 - r.first();
+ }
+ else
+ {
+ d->fileBytesLeft = d->resource.size() - r.first();
+ }
+
+ kpfDebug << d->id << ": fileBytesLeft = "
+ << d->fileBytesLeft << "d" << endl;
+
+ respond(206, d->fileBytesLeft);
+ }
+
+ return true;
+ }
+
+ void
+ Server::slotBytesWritten(int i)
+ {
+ // Don't you just love it when people forget 'unsigned' ?
+
+ if (i > 0)
+ d->bytesWritten += i;
+
+ emit(output(this, i));
+
+ // Reset idle timer.
+ d->idleTimer.start(Timeout, true);
+ }
+
+ void
+ Server::slotConnectionClosed()
+ {
+ kpfDebug << d->id << ": slotConnectionClosed -> finished" << endl;
+ setFinished(Flush);
+ }
+
+ void
+ Server::writeLine(const QString & line)
+ {
+ // Fill a buffer. We're not allowed to send anything out until our
+ // controller calls write().
+
+ QCString s(line.utf8() + "\r\n");
+
+ d->headerBytesLeft += s.length();
+ d->outgoingHeaderBuffer += s;
+ }
+
+ void
+ Server::cancel()
+ {
+ kpfDebug << d->id << ": cancel -> finished" << endl;
+ setFinished(NoFlush);
+ }
+
+ void
+ Server::respond(uint code, ulong fileSize)
+ {
+ // Set the code of our Response object.
+
+ d->response.setCode(code);
+
+ // Request from the Response object the text that should be sent
+ // back to the client.
+
+ QCString s(d->response.text(d->request));
+
+ // Tell our Response object how long it will be in total (it doesn't
+ // know its total size until we tell it about the resource size.)
+
+ d->response.setSize(s.length() + fileSize);
+
+ // Tell the world we've finished setting up our response.
+
+ emit(response(this));
+
+ // Add the response text to the outgoing header buffer.
+
+ d->headerBytesLeft += s.length();
+ d->outgoingHeaderBuffer += s;
+ }
+
+ void
+ Server::setFinished(FlushSelect flushSelect)
+ {
+ if (Finished == d->state) // Already finished.
+ return;
+
+ d->state = Finished;
+
+ kpfDebug
+ << d->id
+ << ": finished("
+ << (Flush == flushSelect ? "flush" : "no flush")
+ << ")"
+ << endl;
+
+ if (Flush == flushSelect)
+ d->socket.flush();
+
+ d->socket.close();
+
+ d->death = QDateTime::currentDateTime();
+
+ emit(finished(this));
+ }
+
+ QHostAddress
+ Server::peerAddress() const
+ {
+ return d->socket.peerAddress();
+ }
+
+ ulong
+ Server::bytesLeft() const
+ {
+ // Return the combined size of the two output buffers.
+
+ return d->headerBytesLeft + d->fileBytesLeft;
+ }
+
+ ulong
+ Server::write(ulong maxBytes)
+ {
+ // We must be in 'Responding' state here. If not, there's a problem
+ // in the code.
+
+ if (Responding != d->state)
+ {
+ kpfDebug << d->id << ": write() but state != Responding -> finished";
+ setFinished(Flush);
+ return 0;
+ }
+
+ // If the socket has been closed (e.g. the remote end hung up)
+ // then we just give up.
+
+ if (QSocket::Connection != d->socket.state())
+ {
+ kpfDebug << d->id << ": Socket closed by client -> finished" << endl;
+ setFinished(Flush);
+ return 0;
+ }
+
+ kpfDebug << d->id << ": Response code is " << d->response.code() << " ("
+ << responseName(d->response.code()) << ")" << endl;
+
+ ulong bytesWritten = 0;
+
+ // Write header data.
+
+ ulong headerBytesWritten = 0;
+
+ if (!writeHeaderData(maxBytes, headerBytesWritten))
+ {
+ return 0;
+ }
+
+ maxBytes -= headerBytesWritten;
+ bytesWritten += headerBytesWritten;
+
+ // If we are only sending headers (response code != 2xx or request type
+ // was HEAD) or we reached the end of the file we were sending, give up.
+
+ if (d->response.code() < 200 || d->response.code() > 299)
+ {
+ kpfDebug << d->id << ": We are only sending headers -> finished" << endl;
+
+ // If we're sending 'Not modified' then we don't need to drop
+ // the connection just yet.
+
+ if (d->response.code() == 304 && d->request.persist())
+ {
+ kpfDebug
+ << d->id
+ << ": 304 and persist. Not dropping connection yet."
+ << endl;
+
+ reset();
+ }
+ else
+ {
+ setFinished(Flush);
+ }
+
+ return bytesWritten;
+ }
+
+ // Just HEAD ? Ok, then if we're set to persistent mode we go back
+ // and wait for another request. Otherwise we're done and can go home.
+
+ if (Request::Head == d->request.method())
+ {
+ if (d->request.persist())
+ {
+ reset();
+ }
+ else
+ {
+ setFinished(Flush);
+ }
+
+ return bytesWritten;
+ }
+
+ // If we've written our limit then wait until next time.
+
+ if (0 == maxBytes)
+ {
+ return bytesWritten;
+ }
+
+ // Write resource data.
+
+ ulong fileBytesWritten = 0;
+
+ // writeFileData() returns true if the op was successful and also
+ // returns the number of bytes written via the second parameter.
+
+ if (!writeFileData(maxBytes, fileBytesWritten))
+ {
+ return 0;
+ }
+
+ kpfDebug << "Wrote " << fileBytesWritten << " from file" << endl;
+
+ maxBytes -= fileBytesWritten;
+ bytesWritten += fileBytesWritten;
+
+ // Did we finish sending the resource data ?
+
+ if (0 == d->fileBytesLeft)
+ {
+ kpfDebug << d->id << ": No bytes left to write. Closing file." << endl;
+
+ d->resource.close();
+
+ // If we're in persistent mode, don't quit just yet.
+
+ if (d->requestCount < MaxKeepAlive && d->request.persist())
+ {
+ kpfDebug
+ << d->id
+ << ": Request included Keep-Alive, so we set state"
+ << " to WaitingForRequest and don't send finished()"
+ << endl;
+
+ reset();
+ }
+ else
+ {
+ kpfDebug
+ << d->id
+ << ": No keep-alive or hit MaxKeepAlive, so finished."
+ << endl;
+
+ setFinished(Flush);
+ }
+ }
+ else
+ {
+ // Ok, we have some data to send over the socket. Tell the world.
+
+ kpfDebug
+ << d->id
+ << "Still have data left to send."
+ << endl;
+
+ emit(readyToWrite(this));
+ }
+
+ return bytesWritten;
+ }
+
+
+ bool
+ Server::writeHeaderData(ulong max, ulong & bytesWritten)
+ {
+ // Is there some header data left to write ?
+
+ if (0 == d->headerBytesLeft)
+ return true;
+
+ // Calculate where to start reading from the buffer.
+
+ uint headerPos =
+ d->outgoingHeaderBuffer.length() - d->headerBytesLeft;
+
+ // Calculate how many bytes we should write this session.
+
+ uint bytesToWrite = min(d->headerBytesLeft, max);
+
+ // Calculate how many bytes we _may_ write.
+
+ bytesToWrite = min(bytesToWrite, d->socket.outputBufferLeft());
+
+ // Get a pointer to the data, offset by the position we start reading.
+
+ char * data = d->outgoingHeaderBuffer.data() + headerPos;
+
+ // Write the data, or at least as much as the socket buffers will
+ // take, and remember how much we wrote.
+
+ int headerBytesWritten = d->socket.writeBlock(data, bytesToWrite);
+
+ // <rant>
+ // Using -1 to signal an error is fucking evil.
+ // Return false instead or add a 'bool & ok' parameter.
+ // If you're not going to use exceptions, at least don't use
+ // crap C conventions for error handling.
+ // </rant>
+
+ if (-1 == headerBytesWritten)
+ {
+ // Socket error.
+
+ kpfDebug << d->id << ": Socket error -> finished" << endl;
+ setFinished(Flush);
+ return false;
+ }
+
+#ifdef KPF_TRAFFIC_DEBUG
+ kpfDebug
+ << d->id
+ << ": Wrote header data: `"
+ << d->outgoingHeaderBuffer.left(headerPos)
+ << "'"
+ << endl;
+#endif
+
+ // Subtract the number of bytes we wrote from the number of bytes
+ // left to write.
+
+ bytesWritten += headerBytesWritten;
+ d->headerBytesLeft -= headerBytesWritten;
+
+ // We may be doing a long file send next, so clear the header buffer
+ // because we don't need that data hanging around in memory anymore.
+
+ if (0 == d->headerBytesLeft)
+ d->outgoingHeaderBuffer.resize(0);
+
+ return true;
+ }
+
+ bool
+ Server::writeFileData(ulong maxBytes, ulong & bytesWritten)
+ {
+ // Nothing left in the file ?
+
+ if (d->resource.atEnd())
+ {
+ d->resource.close();
+ kpfDebug << d->id << ": file at end -> finished" << endl;
+ setFinished(Flush);
+ return false;
+ }
+
+ // Calculate how much data we may write this session.
+ // If none, give up.
+
+ uint bytesToWrite = min(d->fileBytesLeft, maxBytes);
+
+ if (0 == bytesToWrite)
+ return true;
+
+ bytesToWrite = min(bytesToWrite, d->socket.outputBufferLeft());
+
+ QByteArray a(bytesToWrite);
+
+ if (0 == bytesToWrite)
+ return true;
+
+ // Read some data (maximum = bytesToWrite) from the file.
+
+ int fileBytesRead = d->resource.readBlock(a.data(), bytesToWrite);
+
+ // Write that data to the socket and remember how much was actually
+ // written (may be less than requested if socket buffers are full.)
+
+ int fileBytesWritten = d->socket.writeBlock(a.data(), fileBytesRead);
+
+ // Was there an error writing to the socket ?
+
+ if (-1 == fileBytesWritten)
+ {
+ // Socket error.
+ kpfDebug << d->id << ": Socket error -> finished" << endl;
+ d->resource.close();
+ setFinished(Flush);
+ return false;
+ }
+
+#ifdef KPF_TRAFFIC_DEBUG
+ kpfDebug
+ << d->id
+ << ": Wrote file data: `"
+ << QCString(a.data(), fileBytesWritten)
+ << "'"
+ << endl;
+#endif
+
+ // We should have been able to write the full amount to the socket,
+ // because we tested d->socket.outputBufferLeft(). If we didn't
+ // manage to write that much, either we have a bug or QSocket does.
+
+ if (fileBytesWritten < fileBytesRead)
+ {
+ kpfDebug << d->id << ": Short write !" << endl;
+ d->resource.close();
+ setFinished(Flush);
+ return false;
+ }
+
+ // Subtract the amount of bytes written from the number left to write.
+
+ bytesToWrite -= fileBytesWritten;
+ bytesWritten += fileBytesWritten;
+ d->fileBytesLeft -= fileBytesWritten;
+
+ return true;
+ }
+
+ void
+ Server::slotTimeout()
+ {
+ kpfDebug << d->id << ": Timeout -> finished" << endl;
+ setFinished(NoFlush);
+ }
+
+ Request
+ Server::request() const
+ {
+ return d->request;
+ }
+
+ Response
+ Server::response() const
+ {
+ return d->response;
+ }
+
+ ulong
+ Server::output() const
+ {
+ return d->bytesWritten;
+ }
+
+ Server::State
+ Server::state() const
+ {
+ return d->state;
+ }
+
+ QDateTime
+ Server::birth() const
+ {
+ return d->birth;
+ }
+
+ QDateTime
+ Server::death() const
+ {
+ return d->death;
+ }
+
+ void
+ Server::reset()
+ {
+ kpfDebug << d->id << ": Resetting for another request" << endl;
+
+ d->request .clear();
+ d->response .clear();
+ d->resource .clear();
+
+ d->state = WaitingForRequest;
+ d->readTimer.start(0, true);
+ }
+
+} // End namespace KPF
+
+#include "Server.moc"
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/Server.h b/kpf/src/Server.h
new file mode 100644
index 00000000..8d26a167
--- /dev/null
+++ b/kpf/src/Server.h
@@ -0,0 +1,186 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef KPF_SERVER_H
+#define KPF_SERVER_H
+
+#include <qdatetime.h>
+#include <qstring.h>
+#include <qhostaddress.h>
+
+#include "Request.h"
+#include "Response.h"
+
+namespace KPF
+{
+ class WebServer;
+
+ /**
+ * Converses with a remote client. Handles requests and generates responses.
+ * Bandwidth is controlled by parent (WebServer).
+ */
+ class Server : public QObject
+ {
+ Q_OBJECT
+
+ public:
+
+ /**
+ * A Server can be in one of four states, enumerated here.
+ */
+ enum State { WaitingForRequest, WaitingForHeaders, Responding, Finished };
+
+ /**
+ * @param dir Root directory. Files not contained in this directory
+ * and subdirectories thereof will not be server.
+ *
+ * @param followSymlinks If false, an expensive algorithm will ensure
+ * that no symbolic links are present anywhere in the path from /
+ * to the requested resource.
+ *
+ * @param socket The system's socket device, used for communication.
+ *
+ * @param parent A WebServer, which will manage this Server.
+ */
+ Server
+ (
+ const QString & dir,
+ bool followSymlinks,
+ int socket,
+ WebServer * parent
+ );
+
+ /**
+ * Free internal data and close connection if still open.
+ */
+ virtual ~Server();
+
+ /**
+ * @return address of client that has connected to us.
+ */
+ QHostAddress peerAddress() const;
+
+ /**
+ * @return Request object associated with this connection.
+ * When persistent connections are used, the object may
+ * differ depending on the current state.
+ */
+ Request request() const;
+
+ /**
+ * @return Response object associated with this connection.
+ * When persistent connections are used, the object may
+ * differ depending on the current state.
+ */
+ Response response() const;
+
+ /**
+ * @return number of bytes sent out over the socket since this object
+ * was created.
+ */
+ ulong output() const;
+
+ /**
+ * @return current state.
+ * @see State.
+ */
+ State state() const;
+
+ /**
+ * @return date and time this object was created.
+ */
+ QDateTime birth() const;
+
+ /**
+ * @return date and time all activity was completed.
+ */
+ QDateTime death() const;
+
+ /**
+ * @return number of bytes remaining to send to client.
+ */
+ ulong bytesLeft() const;
+
+ /**
+ * Send no more than maxBytes to the client.
+ *
+ * @return number of bytes sent.
+ */
+ ulong write(ulong maxBytes);
+
+ /**
+ * Stop negotiating with client and sending data. Emit @ref finished.
+ * Do not flush output.
+ */
+ void cancel();
+
+ protected slots:
+
+ void slotReadyRead ();
+ void slotRead ();
+ void slotBytesWritten (int);
+ void slotConnectionClosed ();
+ void slotTimeout ();
+
+ signals:
+
+ void readyToWrite (Server *);
+ void output (Server *, ulong);
+ void finished (Server *);
+ void response (Server *);
+ void request (Server *);
+
+ private:
+
+ // Disable copying.
+
+ Server(const Server &);
+ Server & operator = (const Server &);
+
+ enum FlushSelect
+ {
+ Flush,
+ NoFlush
+ };
+
+ void setFinished (FlushSelect);
+ void writeLine (const QString &);
+ void prepareResponse ();
+ void respond (uint code, ulong size = 0);
+ bool readRequest (const QString &);
+ bool checkRequest ();
+ void handleRequest ();
+ void readHeaders ();
+ bool handleRange (const ByteRange &);
+ bool writeHeaderData (ulong, ulong &);
+ bool writeFileData (ulong, ulong &);
+ void reset ();
+
+ class Private;
+ Private * d;
+ };
+
+} // End namespace KPF
+
+#endif // KPF_SERVER_H
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/ServerPrivate.cpp b/kpf/src/ServerPrivate.cpp
new file mode 100644
index 00000000..079fb29b
--- /dev/null
+++ b/kpf/src/ServerPrivate.cpp
@@ -0,0 +1,54 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include "Defines.h"
+#include "ServerPrivate.h"
+#include "Defaults.h"
+
+namespace KPF
+{
+ ulong Server::Private::ID = 0L;
+
+ Server::Private::Private()
+ : socket (0, "KPF::Server::Private.socket"),
+ state (WaitingForRequest),
+ bytesWritten (0L),
+ headerBytesLeft (0L),
+ fileBytesLeft (0L),
+ dataRead (0L),
+ followSymlinks (Config::DefaultFollowSymlinks),
+ generateDirectoryListings (false),
+ requestCount (0),
+ id (ID++)
+ {
+ // Empty.
+ }
+
+ Server::Private::~Private()
+ {
+ // Empty.
+ }
+
+} // End namespace KPF
+
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/ServerPrivate.h b/kpf/src/ServerPrivate.h
new file mode 100644
index 00000000..fcf7fd39
--- /dev/null
+++ b/kpf/src/ServerPrivate.h
@@ -0,0 +1,78 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef KPF_SERVER_PRIVATE_H
+#define KPF_SERVER_PRIVATE_H
+
+#include <qcstring.h>
+#include <qtimer.h>
+
+#include "ServerSocket.h"
+#include "Server.h"
+#include "Request.h"
+#include "Response.h"
+#include "Resource.h"
+
+namespace KPF
+{
+ /**
+ * Data for Server class. Kept here to speed up recompilation.
+ */
+ class Server::Private
+ {
+ public:
+
+ Private();
+ ~Private();
+
+ // Order dependency
+ ServerSocket socket;
+ Server::State state;
+ ulong bytesWritten;
+ ulong headerBytesLeft;
+ ulong fileBytesLeft;
+ ulong dataRead;
+ bool followSymlinks;
+ bool generateDirectoryListings;
+ uint requestCount;
+ // End order dependency
+
+ QString dir;
+ Request request;
+ Response response;
+ Resource resource;
+ QStringList incomingHeaderLineBuffer;
+ QStringList incomingLineBuffer;
+ QDateTime birth;
+ QDateTime death;
+ QCString outgoingHeaderBuffer;
+ QTimer idleTimer;
+ QTimer readTimer;
+ ulong id;
+ static ulong ID;
+ };
+
+} // End namespace KPF
+
+#endif // KPF_SERVER_PRIVATE_H
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/ServerSocket.cpp b/kpf/src/ServerSocket.cpp
new file mode 100644
index 00000000..9f39ac57
--- /dev/null
+++ b/kpf/src/ServerSocket.cpp
@@ -0,0 +1,46 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include <qsocketdevice.h>
+
+#include "Defines.h"
+#include "ServerSocket.h"
+
+namespace KPF
+{
+ ServerSocket::ServerSocket(QObject * parent, const char * name)
+ : QSocket(parent, name)
+ {
+ // Empty.
+ }
+
+ uint
+ ServerSocket::outputBufferLeft()
+ {
+ return socketDevice()->sendBufferSize() - bytesToWrite();
+ }
+
+} // End namespace KPF
+
+// vim:ts=2:sw=2:tw=78:et
+
diff --git a/kpf/src/ServerSocket.h b/kpf/src/ServerSocket.h
new file mode 100644
index 00000000..14a2e2d8
--- /dev/null
+++ b/kpf/src/ServerSocket.h
@@ -0,0 +1,47 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef KPF_SERVER_SOCKET_H
+#define KPF_SERVER_SOCKET_H
+
+#include <qsocket.h>
+
+namespace KPF
+{
+ /**
+ * Used to help calculate how much of a QSocket's output buffer we can
+ * use before data will be sent out.
+ */
+ class ServerSocket : public QSocket
+ {
+ public:
+
+ ServerSocket(QObject * parent, const char * name = 0);
+
+ uint outputBufferLeft();
+ };
+
+} // End namespace KPF
+
+#endif // KPF_SERVER_SOCKET_H
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/ServerWizard.cpp b/kpf/src/ServerWizard.cpp
new file mode 100644
index 00000000..8ca47e22
--- /dev/null
+++ b/kpf/src/ServerWizard.cpp
@@ -0,0 +1,410 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qspinbox.h>
+#include <qdir.h>
+#include <qptrlist.h>
+#include <qlineedit.h>
+
+#include <kapplication.h>
+#include <klineedit.h>
+#include <kdialog.h>
+#include <klocale.h>
+#include <kurlrequester.h>
+#include <kfiledialog.h>
+
+#include "Defines.h"
+#include "ServerWizard.h"
+#include "WebServerManager.h"
+#include "WebServer.h"
+#include "Help.h"
+#include <dnssd/servicebrowser.h>
+
+#include <unistd.h>
+
+namespace KPF
+{
+ ServerWizard::ServerWizard(QWidget * parent)
+ : KWizard(parent, "KPF::ServerWizard", true)
+ {
+ setCaption(i18n("New Server - %1").arg("kpf"));
+
+ page1_ = new QWidget(this);
+ page2_ = new QWidget(this);
+ page3_ = new QWidget(this);
+// page4_ = new QWidget(this);
+ page5_ = new QWidget(this);
+
+ QLabel * l_rootDirectoryHelp =
+ new QLabel
+ (
+ i18n
+ (
+ "<p>"
+ "Specify the directory which contains the files"
+ " you wish to share."
+ "</p>"
+ "<p>"
+ "<em>Warning</em>: Do not share any directories that contain"
+ " sensitive information!"
+ "</p>"
+ ),
+ page1_
+ );
+
+ QLabel * l_listenPortHelp =
+ new QLabel
+ (
+ i18n
+ (
+ "<p>"
+ "Specify the network `port' on which the server should"
+ " listen for connections."
+ "</p>"
+ ),
+ page2_
+ );
+
+ QLabel * l_bandwidthLimitHelp =
+ new QLabel
+ (
+ i18n
+ (
+ "<p>"
+ "Specify the maximum amount of data (in kilobytes) that will be"
+ " sent out per second."
+ "</p>"
+ "<p>"
+ "This allows you to keep some bandwidth for yourself instead"
+ " of allowing connections with kpf to hog your connection."
+ "</p>"
+ ),
+ page3_
+ );
+/*
+ QLabel * l_connectionLimitHelp =
+ new QLabel
+ (
+ i18n
+ (
+ "<p>"
+ "Specify the maximum number of connections allowed at"
+ " any one time."
+ "</p>"
+ ),
+ page4_
+ );
+*/
+ bool canPublish = DNSSD::ServiceBrowser::isAvailable() == DNSSD::ServiceBrowser::Working;
+ QLabel * l_serverNameHelp =
+ new QLabel
+ (
+ KPF::HelpText::getServerNameHelp(),
+ page5_
+ );
+
+ QLabel * l_root_ =
+ new QLabel(i18n("&Root directory:"), page1_);
+
+ QLabel * l_listenPort_ =
+ new QLabel(i18n("&Listen port:"), page2_);
+
+ QLabel * l_bandwidthLimit_ =
+ new QLabel(i18n("&Bandwidth limit:"), page3_);
+
+// QLabel * l_connectionLimit_ =
+// new QLabel(i18n("Connection &limit"), page4_);
+
+ QLabel * l_serverName_ =
+ new QLabel(i18n("&Server name:"), page5_);
+
+ if(!canPublish)
+ l_serverName_->setEnabled(false);
+
+ kur_root_ = new KURLRequester(page1_);
+
+ sb_listenPort_ = new QSpinBox(1, 65535, 1, page2_);
+
+ sb_bandwidthLimit_ = new QSpinBox(1, 999999, 1, page3_);
+// sb_connectionLimit_ = new QSpinBox(1, 9999, 1, page4_);
+
+ char hostname[255];
+ gethostname(hostname, 255-2);
+ hostname[255-1]=0;
+ le_serverName_ = new QLineEdit(hostname, page5_);
+
+ if(!canPublish)
+ le_serverName_->setEnabled(false);
+
+ l_root_ ->setBuddy(kur_root_);
+ l_listenPort_ ->setBuddy(sb_listenPort_);
+ l_bandwidthLimit_ ->setBuddy(sb_bandwidthLimit_);
+ l_serverName_ ->setBuddy(le_serverName_);
+// l_connectionLimit_ ->setBuddy(sb_connectionLimit_);
+
+ sb_listenPort_
+ ->setValue(WebServerManager::instance()->nextFreePort());
+
+ sb_bandwidthLimit_ ->setValue(Config::DefaultBandwidthLimit);
+ sb_bandwidthLimit_ ->setSuffix(i18n(" kB/s"));
+// sb_connectionLimit_ ->setValue(Config::DefaultConnectionLimit);
+
+ QVBoxLayout * layout1 =
+ new QVBoxLayout(page1_, KDialog::marginHint(), KDialog::spacingHint());
+
+ QVBoxLayout * layout2 =
+ new QVBoxLayout(page2_, KDialog::marginHint(), KDialog::spacingHint());
+
+ QVBoxLayout * layout3 =
+ new QVBoxLayout(page3_, KDialog::marginHint(), KDialog::spacingHint());
+
+// QVBoxLayout * layout4 =
+// new QVBoxLayout(page4_, KDialog::marginHint(), KDialog::spacingHint());
+
+ QVBoxLayout * layout5 =
+ new QVBoxLayout(page5_, KDialog::marginHint(), KDialog::spacingHint());
+
+ layout1->addWidget(l_rootDirectoryHelp);
+ layout2->addWidget(l_listenPortHelp);
+ layout3->addWidget(l_bandwidthLimitHelp);
+// layout4->addWidget(l_connectionLimitHelp);
+ layout5->addWidget(l_serverNameHelp);
+
+ QHBoxLayout * layout10 = new QHBoxLayout(layout1);
+
+ layout10->addWidget(l_root_);
+ layout10->addWidget(kur_root_);
+
+ layout1->addStretch(1);
+
+ QHBoxLayout * layout20 = new QHBoxLayout(layout2);
+
+ layout20->addWidget(l_listenPort_);
+ layout20->addWidget(sb_listenPort_);
+
+ layout2->addStretch(1);
+
+ QHBoxLayout * layout30 = new QHBoxLayout(layout3);
+
+ layout30->addWidget(l_bandwidthLimit_);
+ layout30->addWidget(sb_bandwidthLimit_);
+
+ layout3->addStretch(1);
+
+// QHBoxLayout * layout40 = new QHBoxLayout(layout4);
+
+// layout40->addWidget(l_connectionLimit_);
+// layout40->addWidget(sb_connectionLimit_);
+
+// layout4->addStretch(1);
+
+ QHBoxLayout * layout50 = new QHBoxLayout(layout5);
+
+ layout50->addWidget(l_serverName_);
+ layout50->addWidget(le_serverName_);
+
+ addPage(page1_, i18n("Root Directory"));
+ addPage(page2_, i18n("Listen Port"));
+ addPage(page3_, i18n("Bandwidth Limit"));
+// addPage(page4_, i18n("Connection Limit"));
+ addPage(page5_, i18n("Server Name"));
+
+ kur_root_->setURL(QDir::homeDirPath() + "/public_html");
+ kur_root_->setMode
+ (KFile::Directory | KFile::ExistingOnly | KFile::LocalOnly);
+
+// setFinishEnabled(page4_, true);
+ setFinishEnabled(page5_, true);
+
+ // This slot sets the 'Next' button on page 1 to enabled/disabled
+ // depending on whether the path is OK.
+
+ connect
+ (
+ kur_root_,
+ SIGNAL(textChanged(const QString &)),
+ SLOT(slotServerRootChanged(const QString &))
+ );
+
+ // Used for setting the caption on the file dialog.
+
+ connect
+ (
+ kur_root_,
+ SIGNAL(openFileDialog(KURLRequester *)),
+ SLOT(slotOpenFileDialog(KURLRequester *))
+ );
+
+ // This slot sets the 'Next' button on page 2 to enabled/disabled
+ // depending on whether the port is OK.
+
+ connect
+ (
+ sb_listenPort_,
+ SIGNAL(valueChanged(int)),
+ SLOT(slotListenPortChanged(int))
+ );
+
+ // Update 'Next' button on page 1.
+
+ slotServerRootChanged(kur_root_->url());
+
+ // Update 'Next' button on page 2.
+
+ slotListenPortChanged(sb_listenPort_->value());
+ }
+
+ ServerWizard::~ServerWizard()
+ {
+ }
+
+ void
+ ServerWizard::setLocation(const QString & location)
+ {
+ kur_root_->setURL(location);
+ }
+
+ QString
+ ServerWizard::root() const
+ {
+ return kur_root_->url();
+ }
+
+ uint
+ ServerWizard::listenPort() const
+ {
+ return sb_listenPort_->value();
+ }
+
+ uint
+ ServerWizard::bandwidthLimit() const
+ {
+ return sb_bandwidthLimit_->value();
+ }
+ QString
+
+ ServerWizard::serverName() const
+ {
+ return le_serverName_->text();
+ }
+
+ uint
+ ServerWizard::connectionLimit() const
+ {
+ return Config::DefaultConnectionLimit; // sb_connectionLimit_->value();
+ }
+
+ void
+ ServerWizard::accept()
+ {
+ QWizard::accept();
+ emit(dying(this));
+ }
+
+ void
+ ServerWizard::reject()
+ {
+ QWizard::reject();
+ emit(dying(this));
+ }
+
+ void
+ ServerWizard::slotListenPortChanged(int newPort)
+ {
+ if (newPort <= 1024)
+ {
+ setNextEnabled(page2_, false);
+ return;
+ }
+
+ QPtrList<WebServer>
+ serverList(WebServerManager::instance()->serverListLocal());
+
+ for (QPtrListIterator<WebServer> it(serverList); it.current(); ++it)
+ {
+ if (it.current()->listenPort() == uint(newPort))
+ {
+ setNextEnabled(page2_, false);
+ return;
+ }
+ }
+
+ setNextEnabled(page2_, true);
+ }
+
+ void
+ ServerWizard::slotServerRootChanged(const QString & _root)
+ {
+ QString root(_root);
+
+ kpfDebug << root << endl;
+
+ // Duplicate ?
+
+ if (WebServerManager::instance()->hasServer(root))
+ {
+ kpfDebug << "Already have a server at " << root << endl;
+ setNextEnabled(page1_, false);
+ return;
+ }
+
+ if ("/" != root.right(1))
+ root += "/";
+
+ QFileInfo fi(root);
+
+ if (!fi.isDir()) // || (fi.dirPath() == QDir::homeDirPath()))
+ {
+ kpfDebug << root << " isn't a dir" << endl; //, or it's $HOME" << endl;
+ setNextEnabled(page1_, false);
+ return;
+ }
+
+ setNextEnabled(page1_, true);
+ }
+
+ void
+ ServerWizard::slotOpenFileDialog(KURLRequester * urlRequester)
+ {
+ KFileDialog * fileDialog = urlRequester->fileDialog();
+
+ if (0 == fileDialog)
+ {
+ kpfDebug << "URL requester's file dialog is 0" << endl;
+ return;
+ }
+
+ fileDialog->setCaption(i18n("Choose Directory to Share - %1").arg("kpf"));
+ }
+
+ void
+ ServerWizard::help()
+ {
+ kapp->invokeHelp("share-config", "kpf");
+ }
+}
+
+#include "ServerWizard.moc"
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/ServerWizard.h b/kpf/src/ServerWizard.h
new file mode 100644
index 00000000..a484b8eb
--- /dev/null
+++ b/kpf/src/ServerWizard.h
@@ -0,0 +1,90 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef KPF_SERVER_WIZARD_H
+#define KPF_SERVER_WIZARD_H
+
+#include <kwizard.h>
+
+class QSpinBox;
+class KURLRequester;
+class QLineEdit;
+
+namespace KPF
+{
+ /**
+ * Used to allow easy creation and configuration of a WebServer object.
+ */
+ class ServerWizard : public KWizard
+ {
+ Q_OBJECT
+
+ public:
+
+ ServerWizard(QWidget * parent = 0);
+
+ virtual ~ServerWizard();
+
+ void setLocation(const QString &);
+
+ QString root() const;
+ uint listenPort() const;
+ uint bandwidthLimit() const;
+ uint connectionLimit() const;
+ QString serverName() const;
+
+ signals:
+
+ void dying(ServerWizard *);
+
+ protected slots:
+
+ virtual void accept();
+ virtual void reject();
+ void slotServerRootChanged(const QString &);
+ void slotListenPortChanged(int);
+ void slotOpenFileDialog(KURLRequester *);
+
+ protected:
+
+ virtual void help();
+
+ private:
+
+
+ KURLRequester * kur_root_;
+ QSpinBox * sb_listenPort_;
+ QSpinBox * sb_bandwidthLimit_;
+ QSpinBox * sb_connectionLimit_;
+ QLineEdit * le_serverName_;
+
+ QWidget * page1_;
+ QWidget * page2_;
+ QWidget * page3_;
+ QWidget * page4_;
+ QWidget * page5_;
+ };
+}
+
+#endif // KPF_SERVER_WIZARD_H
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/SingleServerConfigDialog.cpp b/kpf/src/SingleServerConfigDialog.cpp
new file mode 100644
index 00000000..6fa1792b
--- /dev/null
+++ b/kpf/src/SingleServerConfigDialog.cpp
@@ -0,0 +1,98 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include <klocale.h>
+
+#include "Defines.h"
+#include "WebServer.h"
+#include "ConfigDialogPage.h"
+#include "SingleServerConfigDialog.h"
+
+namespace KPF
+{
+ SingleServerConfigDialog::SingleServerConfigDialog
+ (
+ WebServer * server,
+ QWidget * parent
+ )
+ : KDialogBase
+ (
+ parent,
+ "KPF::SingleServerConfigDialog",
+ false,
+ i18n("Configuring Server %1 - kpf").arg(server->root()),
+ KDialogBase::Ok | KDialogBase::Cancel,
+ KDialogBase::Ok,
+ true
+ ),
+ server_(server)
+ {
+ widget_ = new ConfigDialogPage(server_, this);
+
+ connect
+ (
+ widget_,
+ SIGNAL(ok(bool)),
+ SLOT(slotOk(bool))
+ );
+
+ setMainWidget(widget_);
+
+ connect(this, SIGNAL(finished()), SLOT(slotFinished()));
+
+ widget_->checkOk();
+ }
+
+ SingleServerConfigDialog::~SingleServerConfigDialog()
+ {
+ // Empty.
+ }
+
+ WebServer *
+ SingleServerConfigDialog::server()
+ {
+ return server_;
+ }
+
+ void
+ SingleServerConfigDialog::accept()
+ {
+ widget_->save();
+ KDialogBase::accept();
+ }
+
+ void
+ SingleServerConfigDialog::slotFinished()
+ {
+ emit(dying(this));
+ }
+
+ void
+ SingleServerConfigDialog::slotOk(bool ok)
+ {
+ enableButtonOK(ok);
+ }
+}
+
+#include "SingleServerConfigDialog.moc"
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/SingleServerConfigDialog.h b/kpf/src/SingleServerConfigDialog.h
new file mode 100644
index 00000000..a6f1c17f
--- /dev/null
+++ b/kpf/src/SingleServerConfigDialog.h
@@ -0,0 +1,68 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef KPF_SINGLE_SERVER_CONFIG_DIALOG_H
+#define KPF_SINGLE_SERVER_CONFIG_DIALOG_H
+
+#include <kdialogbase.h>
+
+namespace KPF
+{
+ class WebServer;
+ class ConfigDialogPage;
+
+ /**
+ * Used to allow configuration of a WebServer object.
+ */
+ class SingleServerConfigDialog : public KDialogBase
+ {
+ Q_OBJECT
+
+ public:
+
+ SingleServerConfigDialog(WebServer *, QWidget * parent);
+
+ virtual ~SingleServerConfigDialog();
+
+ WebServer * server();
+
+ protected slots:
+
+ void slotFinished();
+ void accept();
+ void slotOk(bool);
+
+ signals:
+
+ void dying(SingleServerConfigDialog *);
+
+ private:
+
+ WebServer * server_;
+
+ ConfigDialogPage * widget_;
+ };
+}
+
+#endif
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/StartingKPFDialog.cpp b/kpf/src/StartingKPFDialog.cpp
new file mode 100644
index 00000000..19b073bd
--- /dev/null
+++ b/kpf/src/StartingKPFDialog.cpp
@@ -0,0 +1,139 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include <qlayout.h>
+#include <qlabel.h>
+#include <qframe.h>
+#include <qtimer.h>
+
+#include <dcopclient.h>
+#include <kapplication.h>
+#include <klocale.h>
+
+#include "Defines.h"
+#include "StartingKPFDialog.h"
+
+namespace KPF
+{
+ class StartingKPFDialog::Private
+ {
+ public:
+
+ Private()
+ {
+ // Empty.
+ }
+
+ QTimer timer;
+ };
+
+ StartingKPFDialog::StartingKPFDialog(QWidget * parent)
+ :
+ KDialogBase
+ (
+ parent,
+ "StartingKPFDialog",
+ true, /* modal */
+ i18n("Starting KDE public fileserver applet"),
+ KDialogBase::Ok | KDialogBase::Cancel,
+ KDialogBase::Cancel,
+ true
+ )
+ {
+ d = new Private;
+
+ QFrame * mainWidget = makeMainWidget();
+
+ QLabel * about =
+ new QLabel
+ (
+ i18n("Starting kpf..."),
+ mainWidget
+ );
+
+ QVBoxLayout * layout = new QVBoxLayout(mainWidget);
+
+ layout->addWidget(about);
+
+ kapp->dcopClient()->setNotifications(true);
+
+ connect
+ (
+ kapp->dcopClient(),
+ SIGNAL(applicationRegistered(const QCString &)),
+ SLOT(slotApplicationRegistered(const QCString &))
+ );
+
+ kapp->dcopClient()
+ ->send("kicker", "default", "addApplet(QString)", "kpfapplet.desktop");
+
+ connect(&d->timer, SIGNAL(timeout()), SLOT(slotTimeout()));
+
+ enableButtonOK(false);
+ enableButtonCancel(true);
+
+ d->timer.start(8 * 1000 /* 8 seconds */, true /* single shot */);
+ }
+
+ StartingKPFDialog::~StartingKPFDialog()
+ {
+ delete d;
+ d = 0;
+ }
+
+ void
+ StartingKPFDialog::slotTimeout()
+ {
+ enableButtonOK(true);
+ enableButtonCancel(false);
+
+ if (kpfRunning())
+ {
+ kpfDebug << "slotTimeout: kpf is running now" << endl;
+ }
+ else
+ {
+ kpfDebug << "slotTimeout: kpf still not running" << endl;
+ }
+ }
+
+ bool
+ StartingKPFDialog::kpfRunning()
+ {
+ return kapp->dcopClient()->isApplicationRegistered("kpf");
+ }
+
+ void
+ StartingKPFDialog::slotApplicationRegistered(const QCString & id)
+ {
+ if ("kpf" == id)
+ {
+ kpfDebug << "kpf just started up" << endl;
+ enableButtonOK(true);
+ enableButtonCancel(false);
+ }
+ }
+}
+
+#include "StartingKPFDialog.moc"
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/StartingKPFDialog.h b/kpf/src/StartingKPFDialog.h
new file mode 100644
index 00000000..cbeca549
--- /dev/null
+++ b/kpf/src/StartingKPFDialog.h
@@ -0,0 +1,62 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef KPF_STARTING_KPF_DIALOG_H
+#define KPF_STARTING_KPF_DIALOG_H
+
+#include <kdialogbase.h>
+
+namespace KPF
+{
+ /**
+ * Used to provide a simply display which informs the user that an attempt
+ * to start the kpf applet is being made.
+ */
+ class StartingKPFDialog : public KDialogBase
+ {
+ Q_OBJECT
+
+ public:
+
+ StartingKPFDialog(QWidget * parent);
+
+ virtual ~StartingKPFDialog();
+
+ protected slots:
+
+ void slotTimeout();
+ void slotApplicationRegistered(const QCString &);
+
+ protected:
+
+ bool kpfRunning();
+
+ private:
+
+ class Private;
+ Private * d;
+ };
+}
+
+#endif
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/System.cpp b/kpf/src/System.cpp
new file mode 100644
index 00000000..f8f2c78b
--- /dev/null
+++ b/kpf/src/System.cpp
@@ -0,0 +1,71 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#ifdef __unix__
+
+// System includes
+#include <signal.h> // For signal(2).
+#include <unistd.h> // For get?uid.
+#include <sys/types.h> // For get?uid.
+
+namespace kpf
+{
+ void blockSigPipe()
+ {
+ ::signal(SIGPIPE, SIG_IGN);
+ }
+
+ int userId()
+ {
+ return ::getuid();
+ }
+
+ int effectiveUserId()
+ {
+ return ::geteuid();
+ }
+}
+
+#else // non-UNIX
+
+namespace kpf
+{
+ void blockSigPipe()
+ {
+ return;
+ }
+
+ int userId()
+ {
+ return -1;
+ }
+
+ int effectiveUserId()
+ {
+ return -1;
+ }
+
+}
+
+#endif
+
diff --git a/kpf/src/System.h b/kpf/src/System.h
new file mode 100644
index 00000000..b740e0c7
--- /dev/null
+++ b/kpf/src/System.h
@@ -0,0 +1,35 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef KPF_SYSTEM_H
+#define KPF_SYSTEM_H
+
+namespace kpf
+{
+ void blockSigPipe();
+
+ int userId();
+ int effectiveUserId();
+}
+
+#endif
diff --git a/kpf/src/Utils.cpp b/kpf/src/Utils.cpp
new file mode 100644
index 00000000..a867a998
--- /dev/null
+++ b/kpf/src/Utils.cpp
@@ -0,0 +1,414 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include <ctime>
+#include <clocale>
+
+#include <qfile.h>
+#include <qregexp.h>
+
+#include <klocale.h>
+
+#include "Defines.h"
+#include "Utils.h"
+
+namespace KPF
+{
+ static bool dateInitDone = false;
+
+ static QStringList monthList;
+
+ static const char rfc1123Format[] = "%a, %d %b %Y %H:%M:%S GMT";
+
+ static time_t qDateTimeToTimeT(const QDateTime & t)
+ {
+ struct tm tempTm;
+
+ tempTm.tm_year = t.date().year();
+ tempTm.tm_mon = t.date().month();
+ tempTm.tm_mday = t.date().day();
+ tempTm.tm_hour = t.time().hour();
+ tempTm.tm_min = t.time().minute();
+ tempTm.tm_sec = t.time().second();
+ tempTm.tm_isdst = -1;
+
+ // Fix up differences between QDateTime and tm.
+
+ tempTm.tm_year -= 1900;
+
+ --tempTm.tm_mon;
+
+ return ::mktime(&tempTm);
+ }
+
+ QDateTime
+ toGMT(const QDateTime & dt)
+ {
+ time_t dtAsTimeT = qDateTimeToTimeT(dt);
+
+ struct tm * dtAsGmTm = ::gmtime(&dtAsTimeT);
+
+ if (0 == dtAsGmTm)
+ return QDateTime();
+
+ time_t dtAsGmTimeT = ::mktime(dtAsGmTm);
+
+ QDateTime ret;
+
+ ret.setTime_t(dtAsGmTimeT);
+
+ return ret;
+ }
+
+ void dateInit()
+ {
+ if (dateInitDone)
+ return;
+
+ dateInitDone = true;
+
+ monthList
+ << "Jan"
+ << "Feb"
+ << "Mar"
+ << "Apr"
+ << "May"
+ << "Jun"
+ << "Jul"
+ << "Aug"
+ << "Sep"
+ << "Oct"
+ << "Nov"
+ << "Dec"
+ ;
+ }
+
+ QString dateString()
+ {
+ return dateString(QDateTime::currentDateTime());
+ }
+
+
+ QString dateString(const QDateTime & t)
+ {
+ time_t asTimeT = qDateTimeToTimeT(t);
+
+ struct tm * asTm = ::gmtime(&asTimeT);
+
+ if (0 == asTm)
+ {
+ kpfDebug << "::gmtime() failed" << endl;
+ return QString::null;
+ }
+
+ asTm->tm_isdst = -1;
+
+ const int len = 128;
+
+ char buf[len];
+
+ // Ensure we use locale "C" for strftime.
+
+ QCString oldLC_ALL = ::strdup(::setlocale(LC_TIME, "C"));
+ QCString oldLC_TIME = ::strdup(::setlocale(LC_ALL, "C"));
+ {
+ ::strftime(static_cast<char *>(buf), len, rfc1123Format, asTm);
+ }
+ ::setlocale(LC_TIME, oldLC_TIME.data());
+ ::setlocale(LC_ALL, oldLC_ALL.data());
+
+ return QString::fromUtf8(buf);
+ }
+
+ bool
+ parseDate(const QString & s, QDateTime & dt)
+ {
+ dateInit();
+
+// kpfDebug << "Parsing date `" << s << "'" << endl;
+
+ QStringList l(QStringList::split(' ', s));
+
+ switch (l.count())
+ {
+ case 4:
+// kpfDebug << "Date type is RFC 850" << endl;
+ return parseDateRFC850(l, dt);
+ break;
+ case 5:
+// kpfDebug << "Date type is asctime" << endl;
+ return parseDateAscTime(l, dt);
+ break;
+ case 6:
+// kpfDebug << "Date type is RFC1123" << endl;
+ return parseDateRFC1123(l, dt);
+ break;
+ default:
+// kpfDebug << "Date type is unknown" << endl;
+ return false;
+ break;
+ }
+
+ return false;
+ }
+
+ bool
+ parseDateRFC1123(const QStringList & l, QDateTime & dt)
+ {
+ if ("GMT" != l[5])
+ return false;
+
+ uint day(l[1].toUInt());
+
+ bool haveMonth = false;
+ uint month = 0;
+
+ QStringList::ConstIterator it;
+
+ for (it = monthList.begin(); it != monthList.end(); ++it)
+ {
+ if (*it == l[2])
+ {
+ haveMonth = true;
+ break;
+ }
+
+ ++month;
+ }
+
+ if (!haveMonth)
+ return false;
+
+ uint year(l[3].toUInt());
+
+ QStringList timeTokenList(QStringList::split(':', l[4]));
+
+ if (3 != timeTokenList.count())
+ return false;
+
+ uint hours (timeTokenList[0].toUInt());
+ uint minutes (timeTokenList[1].toUInt());
+ uint seconds (timeTokenList[2].toUInt());
+
+ dt.setDate(QDate(year, month + 1, day));
+ dt.setTime(QTime(hours, minutes, seconds));
+
+ return dt.isValid();
+ }
+
+ bool
+ parseDateRFC850(const QStringList & l, QDateTime & dt)
+ {
+ if ("GMT" != l[3])
+ return false;
+
+ QStringList dateTokenList(QStringList::split('-', l[1]));
+
+ if (3 != dateTokenList.count())
+ return false;
+
+ uint day(dateTokenList[0].toUInt());
+
+ bool haveMonth = false;
+ uint month = 0;
+
+ QStringList::ConstIterator it;
+
+ for (it = monthList.begin(); it != monthList.end(); ++it)
+ {
+ if (*it == dateTokenList[1])
+ {
+ haveMonth = true;
+ break;
+ }
+
+ ++month;
+ }
+
+ if (!haveMonth)
+ return false;
+
+ uint year(dateTokenList[2].toUInt());
+
+ if (year < 50)
+ year += 2000;
+ else if (year < 100)
+ year += 1900;
+
+ QStringList timeTokenList(QStringList::split(':', l[2]));
+
+ if (3 != timeTokenList.count())
+ return false;
+
+ uint hours (timeTokenList[0].toUInt());
+ uint minutes (timeTokenList[1].toUInt());
+ uint seconds (timeTokenList[2].toUInt());
+
+ dt.setDate(QDate(year, month + 1, day));
+ dt.setTime(QTime(hours, minutes, seconds));
+
+ return dt.isValid();
+ }
+
+ bool
+ parseDateAscTime(const QStringList & l, QDateTime & dt)
+ {
+ bool haveMonth = false;
+ uint month = 0;
+
+ QStringList::ConstIterator it;
+
+ for (it = monthList.begin(); it != monthList.end(); ++it)
+ {
+ if (*it == l[1])
+ {
+ haveMonth = true;
+ break;
+ }
+
+ ++month;
+ }
+
+ if (!haveMonth)
+ return false;
+
+ uint day(l[2].toUInt());
+
+ QStringList timeTokenList(QStringList::split(':', l[3]));
+
+ if (3 != timeTokenList.count())
+ return false;
+
+ uint hours (timeTokenList[0].toUInt());
+ uint minutes (timeTokenList[1].toUInt());
+ uint seconds (timeTokenList[2].toUInt());
+
+ uint year(l[4].toUInt());
+
+ dt.setDate(QDate(year, month + 1, day));
+ dt.setTime(QTime(hours, minutes, seconds));
+
+ return dt.isValid();
+ }
+
+ QString
+ translatedResponseName(uint code)
+ {
+ QString s;
+
+ switch (code)
+ {
+ case 200:
+ s = i18n("OK");
+ break;
+ case 206:
+ s = i18n("Partial content");
+ break;
+ case 304:
+ s = i18n("Not modified");
+ break;
+ case 400:
+ s = i18n("Bad request");
+ break;
+ case 403:
+ s = i18n("Forbidden");
+ break;
+ case 404:
+ s = i18n("Not found");
+ break;
+ case 412:
+ s = i18n("Precondition failed");
+ break;
+ case 416:
+ s = i18n("Bad range");
+ break;
+ case 500:
+ s = i18n("Internal error");
+ break;
+ case 501:
+ s = i18n("Not implemented");
+ break;
+ case 505:
+ s = i18n("HTTP version not supported");
+ break;
+ default:
+ s = i18n("Unknown");
+ break;
+ }
+
+ return s;
+ }
+
+ QString
+ responseName(uint code)
+ {
+ QString s;
+
+ switch (code)
+ {
+ case 200:
+ s = "OK";
+ break;
+ case 206:
+ s = "Partial content";
+ break;
+ case 304:
+ s = "Not modified";
+ break;
+ case 400:
+ s = "Bad request";
+ break;
+ case 403:
+ s = "Forbidden";
+ break;
+ case 404:
+ s = "Not found";
+ break;
+ case 412:
+ s = "Precondition failed";
+ break;
+ case 416:
+ s = "Bad range";
+ break;
+ case 500:
+ s = "Internal error";
+ break;
+ case 501:
+ s = "Not implemented";
+ break;
+ case 505:
+ s = "HTTP version not supported";
+ break;
+ default:
+ s = "Unknown";
+ break;
+ }
+
+ return s;
+ }
+
+
+} // End namespace KPF
+
+
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/Utils.h b/kpf/src/Utils.h
new file mode 100644
index 00000000..6a81dfa8
--- /dev/null
+++ b/kpf/src/Utils.h
@@ -0,0 +1,113 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef KPF_UTILS_H
+#define KPF_UTILS_H
+
+#include <qstringlist.h>
+#include <qstring.h>
+#include <qdatetime.h>
+#include <qfileinfo.h>
+
+/**
+ * Used to keep all parts of the kpf application out of the global namespace.
+ */
+namespace KPF
+{
+ /**
+ * Safe version of QMIN
+ * @return minimum of a and b.
+ */
+ template<class T> T min(T a, T b) { return a < b ? a : b; }
+
+ /**
+ * Safe version of QMAX
+ * @return minimum of a and b.
+ */
+ template<class T> T max(T a, T b) { return b < a ? a : b; }
+
+ /**
+ * @return the current date and time as an HTTP/1.1 compliant date string.
+ */
+ QString dateString();
+
+ /**
+ * @return the passed QDateTime as an HTTP/1.1 compliant date string.
+ */
+ QString dateString(const QDateTime & dt);
+
+ /**
+ * Parse an HTTP/1.1 date to a QDateTime.
+ * @param ret the QDateTime representation (result of parsing)
+ * @return true if the date is an an acceptable format.
+ */
+ bool parseDate(const QString &, QDateTime & ret);
+
+ /**
+ * Parse an RFC 1123 format date to a QDateTime. Usually called by
+ * @ref parseDate.
+ */
+ bool parseDateRFC1123(const QStringList &, QDateTime &);
+
+ /**
+ * Parse an RFC 850 format date to a QDateTime. Usually called by
+ * @ref parseDate.
+ */
+ bool parseDateRFC850(const QStringList &, QDateTime &);
+
+ /**
+ * Parse an asctime(3) format date to a QDateTime. Usually called by
+ * @ref parseDate.
+ */
+ bool parseDateAscTime(const QStringList &, QDateTime &);
+
+ /**
+ * @return i18n(HTTP response message for code)
+ */
+ QString translatedResponseName(uint code);
+
+ /**
+ * @return HTTP response message for code
+ */
+ QString responseName(uint code);
+
+ /**
+ * @return the passed QDateTime converted GMT, honouring daylight
+ * saving time if necessary.
+ */
+ QDateTime toGMT(const QDateTime &);
+
+ /**
+ * Unquote hex quoted chars in string.
+ */
+ QString unquote(const QString &);
+
+ /**
+ * Quote naughty chars in %xx format.
+ */
+ QString quote(const QString &);
+
+} // End namespace KPF
+
+#endif
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/WebServer.cpp b/kpf/src/WebServer.cpp
new file mode 100644
index 00000000..37301f09
--- /dev/null
+++ b/kpf/src/WebServer.cpp
@@ -0,0 +1,644 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+// System includes
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+// Qt includes
+#include <qsocket.h>
+#include <qdatetime.h>
+#include <qtimer.h>
+
+// KDE includes
+#include "config.h"
+#include <kconfig.h>
+#include <kglobal.h>
+#include <kmessagebox.h>
+#include <klocale.h>
+#include <dnssd/publicservice.h>
+
+// Local includes
+#include "Defines.h"
+#include "Defaults.h"
+#include "WebServerSocket.h"
+#include "WebServer.h"
+#include "Server.h"
+#include "Utils.h"
+
+namespace KPF
+{
+ static const uint SamplesPerSecond = 10;
+ static const uint MaxBacklog = 1024;
+
+ class WebServer::Private
+ {
+ public:
+
+ Private()
+ : socket (0L),
+ listenPort (Config::DefaultListenPort),
+ connectionLimit (Config::DefaultConnectionLimit),
+ bandwidthLimit (Config::DefaultBandwidthLimit),
+ lastTotalOutput (0L),
+ totalOutput (0L),
+ portContention (true),
+ paused (false),
+ followSymlinks (Config::DefaultFollowSymlinks),
+ customErrorMessages (false)
+ {
+ }
+
+ ~Private()
+ {
+ delete socket;
+ delete service;
+ service = 0;
+ socket = 0;
+ }
+
+ WebServerSocket * socket;
+ uint listenPort;
+ uint connectionLimit;
+ QPtrList<Server> serverList;
+ QString root;
+ QString serverName;
+ QTimer writeTimer;
+ QTimer resetOutputTimer;
+ QTimer bindTimer;
+ QTimer backlogTimer;
+ ulong bandwidthLimit;
+ ulong lastTotalOutput;
+ ulong totalOutput;
+ bool portContention;
+ bool paused;
+ bool followSymlinks;
+ bool customErrorMessages;
+ QValueList<int> backlog;
+ DNSSD::PublicService* service;
+
+ };
+
+ WebServer::WebServer(const QString & root)
+ : DCOPObject(QCString("WebServer_") + root.utf8()),
+ QObject()
+ {
+ d = new Private;
+
+ d->root = root;
+ loadConfig();
+ publish();
+
+ connect(&d->bindTimer, SIGNAL(timeout()), SLOT(slotBind()));
+ connect(&d->writeTimer, SIGNAL(timeout()), SLOT(slotWrite()));
+ connect(&d->resetOutputTimer, SIGNAL(timeout()), SLOT(slotCheckOutput()));
+ connect(&d->backlogTimer, SIGNAL(timeout()), SLOT(slotClearBacklog()));
+
+ d->bindTimer .start( 0, true);
+ d->resetOutputTimer .start(1000 / SamplesPerSecond, false);
+ }
+
+
+ WebServer::WebServer
+ (
+ const QString & root,
+ uint listenPort,
+ uint bandwidthLimit,
+ uint connectionLimit,
+ bool followSymlinks,
+ const QString & serverName
+ )
+ : DCOPObject(QCString("WebServer_") + root.utf8()),
+ QObject()
+ {
+ d = new Private;
+
+ d->root = root;
+
+ d->listenPort = listenPort;
+ d->bandwidthLimit = bandwidthLimit;
+ d->connectionLimit = connectionLimit;
+ d->followSymlinks = followSymlinks;
+ d->serverName = serverName;
+
+ saveConfig();
+ publish();
+ connect(&d->bindTimer, SIGNAL(timeout()), SLOT(slotBind()));
+ connect(&d->writeTimer, SIGNAL(timeout()), SLOT(slotWrite()));
+ connect(&d->resetOutputTimer, SIGNAL(timeout()), SLOT(slotCheckOutput()));
+ connect(&d->backlogTimer, SIGNAL(timeout()), SLOT(slotClearBacklog()));
+
+ d->bindTimer .start( 0, true);
+ d->resetOutputTimer .start(1000 / SamplesPerSecond, false);
+ }
+
+ WebServer::~WebServer()
+ {
+ killAllConnections();
+
+ delete d;
+ d = 0;
+ }
+
+ void WebServer::publish()
+ {
+ d->service = new DNSSD::PublicService(d->serverName,"_http._tcp",d->listenPort);
+ connect(d->service,SIGNAL(published(bool)),this,SLOT(wasPublished(bool)));
+ d->service->publishAsync();
+ }
+
+ void WebServer::wasPublished(bool ok)
+ {
+ if(ok) {
+ KMessageBox::information( NULL, i18n("Successfully published this new service to the network (ZeroConf)."), i18n( "Successfully Published the Service" ), "successfullypublished" );
+ kpfDebug << "Published to dnssd successfully" << endl;
+ }
+ else {
+ KMessageBox::information( NULL, i18n("Failed to publish this new service to the network (ZeroConf). The server will work fine without this, however."), i18n( "Failed to Publish the Service" ), "failedtopublish" );
+ }
+ }
+
+ void
+ WebServer::slotBind()
+ {
+ if (0 != d->socket)
+ {
+ qWarning("Uhhh, socket isn't 0, but I'm told to bind ?");
+ return;
+ }
+
+ d->socket = new WebServerSocket(d->listenPort, d->connectionLimit);
+
+ d->portContention = !d->socket->ok();
+
+ emit(contentionChange(d->portContention));
+
+ if (!d->portContention)
+ connect(d->socket, SIGNAL(connection(int)), SLOT(slotConnection(int)));
+
+ else
+ {
+ delete d->socket;
+ d->socket = 0;
+ d->bindTimer.start(1000, true);
+ }
+ }
+
+ void
+ WebServer::slotConnection(int fd)
+ {
+ if (!d->backlog.isEmpty())
+ {
+ if (d->backlog.count() < MaxBacklog)
+ {
+ kpfDebug << "Adding this connection to the backlog." << endl;
+ d->backlog.append(fd);
+ }
+ else
+ {
+ kpfDebug << "Backlog full. Ignoring this connection." << endl;
+ }
+ return;
+ }
+
+ if (!handleConnection(fd))
+ {
+ if (d->backlog.count() < MaxBacklog)
+ {
+ kpfDebug << "Adding this connection to the backlog." << endl;
+ d->backlog.append(fd);
+ d->backlogTimer.start(10, true);
+ }
+ else
+ {
+ kpfDebug << "Backlog full. Ignoring this connection." << endl;
+ }
+ }
+ }
+
+ bool
+ WebServer::handleConnection(int fd)
+ {
+ if (d->paused)
+ {
+ kpfDebug << "Paused." << endl;
+ return false;
+ }
+
+ if (d->serverList.count() >= d->connectionLimit)
+ {
+// kpfDebug << "Hit connection limit." << endl;
+ return false;
+ }
+
+ int on = 1;
+
+ ::setsockopt
+ (
+ fd,
+ SOL_SOCKET,
+ SO_REUSEADDR,
+ ( char* )&on,
+ sizeof( on ) );
+
+ on = 0;
+
+ ::setsockopt
+ (
+ fd,
+ SOL_SOCKET,
+ SO_LINGER,
+ ( char* ) &on,
+ sizeof( on ) );
+
+ Server * s = new Server(d->root, d->followSymlinks, fd, this);
+
+ connect
+ (
+ s,
+ SIGNAL(output(Server *, ulong)),
+ SLOT(slotOutput(Server *, ulong))
+ );
+
+ connect(s, SIGNAL(finished(Server *)), SLOT(slotFinished(Server *)));
+ connect(s, SIGNAL(request(Server *)), SIGNAL(request(Server *)));
+ connect(s, SIGNAL(response(Server *)), SIGNAL(response(Server *)));
+
+ d->serverList.append(s);
+
+ connect
+ (s, SIGNAL(readyToWrite(Server *)), SLOT(slotReadyToWrite(Server *)));
+
+ emit(connection(s));
+
+ return true;
+ }
+
+ void
+ WebServer::restart()
+ {
+ d->bindTimer.stop();
+
+ killAllConnections();
+ delete d->socket;
+ d->socket = 0;
+ d->service->setServiceName(d->serverName);
+ d->service->setPort(d->listenPort);
+ d->bindTimer.start(0, true);
+ }
+
+ void
+ WebServer::killAllConnections()
+ {
+ QPtrListIterator<Server> it(d->serverList);
+
+ for (; it.current(); ++it)
+ it.current()->cancel();
+
+ }
+
+ void
+ WebServer::slotOutput(Server * s, ulong l)
+ {
+ emit(output(s, l));
+ }
+
+ void
+ WebServer::slotFinished(Server * s)
+ {
+ emit(finished(s));
+ d->serverList.removeRef(s);
+ delete s;
+ s = 0;
+ }
+
+ void
+ WebServer::setBandwidthLimit(ulong l)
+ {
+ d->bandwidthLimit = l;
+ saveConfig();
+ }
+
+ ulong
+ WebServer::bandwidthLimit()
+ {
+ return d->bandwidthLimit;
+ }
+
+ void
+ WebServer::setFollowSymlinks(bool b)
+ {
+ d->followSymlinks = b;
+ saveConfig();
+ }
+
+ bool
+ WebServer::followSymlinks()
+ {
+ return d->followSymlinks;
+ }
+
+ QString
+ WebServer::root()
+ {
+ return d->root;
+ }
+
+ uint
+ WebServer::connectionLimit()
+ {
+ return d->connectionLimit;
+ }
+
+ void
+ WebServer::setConnectionLimit(uint i)
+ {
+ d->connectionLimit = i;
+ saveConfig();
+ }
+
+ uint
+ WebServer::listenPort()
+ {
+ return d->listenPort;
+ }
+
+ void
+ WebServer::setListenPort(uint i)
+ {
+ d->listenPort = i;
+ saveConfig();
+ }
+
+ bool
+ WebServer::customErrorMessages()
+ {
+ return d->customErrorMessages;
+ }
+
+ void
+ WebServer::setCustomErrorMessages(bool b)
+ {
+ d->customErrorMessages = b;
+ saveConfig();
+ }
+
+ ulong
+ WebServer::bytesLeft() const
+ {
+#if 0
+ // Multiply the bandwidth limit by 10 so we can do 10 checks per second.
+
+ ulong l =
+ (d->bandwidthLimit * 10240) - (d->totalOutput - d->lastTotalOutput);
+#endif
+
+ ulong l =
+ ulong(d->bandwidthLimit * (1024 / double(SamplesPerSecond)))
+ - (d->totalOutput - d->lastTotalOutput);
+
+ return l;
+ }
+
+ ulong
+ WebServer::bandwidthPerClient() const
+ {
+ ulong l = 0L;
+
+ if (!d->serverList.isEmpty())
+ {
+ l = bytesLeft() / d->serverList.count();
+ }
+
+ kpfDebug << l << endl;
+
+ return l;
+ }
+
+ void
+ WebServer::slotReadyToWrite(Server *)
+ {
+ d->writeTimer.stop();
+ d->writeTimer.start(0, true);
+ }
+
+ void
+ WebServer::slotWrite()
+ {
+ if (d->serverList.isEmpty())
+ return;
+
+ QPtrListIterator<Server> it(d->serverList);
+
+ for (; it.current(); ++it)
+ {
+ if (0 == bytesLeft())
+ break;
+
+ Server * s = it.current();
+
+ if (0 == s->bytesLeft())
+ continue;
+
+ ulong bytesAvailable = 0;
+
+ if (0 == bandwidthPerClient())
+ bytesAvailable = bytesLeft();
+ else
+ bytesAvailable = min(s->bytesLeft(), bandwidthPerClient());
+
+ if (0 != bytesAvailable)
+ d->totalOutput += s->write(bytesAvailable);
+ }
+
+ d->writeTimer.start(1000 / SamplesPerSecond, true);
+ }
+
+ void
+ WebServer::slotCheckOutput()
+ {
+ emit(connectionCount(d->serverList.count()));
+ ulong lastOutput = d->totalOutput - d->lastTotalOutput;
+ emit(wholeServerOutput(ulong(lastOutput * SamplesPerSecond)));
+ d->lastTotalOutput = d->totalOutput;
+ }
+
+ void
+ WebServer::slotClearBacklog()
+ {
+// kpfDebug << "WebServer::slotClearBacklog" << endl;
+
+ if (!d->backlog.isEmpty())
+ {
+ uint currentBacklogCount = d->backlog.count();
+
+ for (uint i = 0; i < currentBacklogCount; i++)
+ {
+ int fd = d->backlog.first();
+
+ if (handleConnection(fd))
+ {
+ kpfDebug
+ << "Ah, we can now handle this connection. Removing from backlog."
+ << endl;
+
+ d->backlog.remove(d->backlog.begin());
+ }
+ else
+ {
+// kpfDebug
+// << "Still can't handle this connection. Leaving in backlog"
+// << endl;
+
+ break;
+ }
+ }
+ }
+
+ if (!d->backlog.isEmpty())
+ {
+ d->backlogTimer.start(10, true);
+ }
+ }
+
+ uint
+ WebServer::connectionCount()
+ {
+ return d->serverList.count();
+ }
+
+ void
+ WebServer::pause(bool b)
+ {
+ if(b == d->paused) return;
+
+ d->paused = b;
+ if (b) d->service->stop();
+ else d->service->publishAsync(); //published() should be already connected
+ emit pauseChange(d->paused);
+ saveConfig();
+ }
+
+ bool
+ WebServer::paused()
+ {
+ return d->paused;
+ }
+ QString
+ WebServer::serverName()
+ {
+ return d->serverName;
+ }
+ void
+ WebServer::setServerName(const QString& serverName)
+ {
+ d->serverName=serverName;
+ }
+
+ bool
+ WebServer::portContention()
+ {
+ return d->portContention;
+ }
+
+ void
+ WebServer::set
+ (
+ uint listenPort,
+ ulong bandwidthLimit,
+ uint connectionLimit,
+ bool followSymlinks,
+ const QString& serverName
+ )
+ {
+ d->listenPort = listenPort;
+ d->bandwidthLimit = bandwidthLimit;
+ d->connectionLimit = connectionLimit;
+ d->followSymlinks = followSymlinks;
+ d->serverName = serverName;
+
+ saveConfig();
+ }
+
+ void
+ WebServer::loadConfig()
+ {
+ kpfDebug << "WebServer(" << d->root << "): Loading configuration" << endl;
+ KConfig c(Config::name());
+
+ c.setGroup(Config::key(Config::GroupPrefix) + d->root);
+
+ d->listenPort =
+ c.readUnsignedNumEntry
+ (Config::key(Config::ListenPort), d->listenPort);
+
+ d->bandwidthLimit =
+ c.readUnsignedNumEntry
+ (Config::key(Config::BandwidthLimit), d->bandwidthLimit);
+
+ d->connectionLimit =
+ c.readUnsignedNumEntry
+ (Config::key(Config::ConnectionLimit), d->connectionLimit);
+
+ d->followSymlinks =
+ c.readBoolEntry
+ (Config::key(Config::FollowSymlinks), d->followSymlinks);
+
+ d->customErrorMessages =
+ c.readBoolEntry
+ (Config::key(Config::CustomErrors), d->customErrorMessages);
+
+ d->paused =
+ c.readBoolEntry
+ (Config::key(Config::Paused), d->paused);
+
+ d->serverName =
+ c.readEntry
+ (Config::key(Config::ServerName), d->serverName);
+
+ }
+
+ void
+ WebServer::saveConfig()
+ {
+ kpfDebug << "WebServer(" << d->root << "): Saving configuration" << endl;
+ KConfig c(Config::name());
+
+ c.setGroup(Config::key(Config::GroupPrefix) + d->root);
+
+ c.writeEntry(Config::key(Config::ListenPort), d->listenPort);
+ c.writeEntry(Config::key(Config::BandwidthLimit), d->bandwidthLimit);
+ c.writeEntry(Config::key(Config::ConnectionLimit), d->connectionLimit);
+ c.writeEntry(Config::key(Config::FollowSymlinks), d->followSymlinks);
+ c.writeEntry(Config::key(Config::CustomErrors), d->customErrorMessages);
+ c.writeEntry(Config::key(Config::Paused), d->paused);
+ c.writeEntry(Config::key(Config::ServerName), d->serverName);
+
+ c.sync();
+ }
+
+} // End namespace KPF
+
+#include "WebServer.moc"
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/WebServer.h b/kpf/src/WebServer.h
new file mode 100644
index 00000000..ae9d9383
--- /dev/null
+++ b/kpf/src/WebServer.h
@@ -0,0 +1,346 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef KPF_WEB_SERVER_H
+#define KPF_WEB_SERVER_H
+
+#include <dcopobject.h>
+#include <qserversocket.h>
+
+#include "Defaults.h"
+#include "Request.h"
+#include "Response.h"
+
+namespace KPF
+{
+ class Server;
+
+ /**
+ * Listens on a port for incoming connections.
+ * Creates and manages Server objects.
+ * Manages bandwidth limit, dealing out bandwidth to Server objects.
+ * Maintains concurrent connection limit, using a backlog to queue incoming
+ * connections which cannot be served immediately.
+ */
+ class WebServer : public QObject, virtual public DCOPObject
+ {
+ K_DCOP
+ Q_OBJECT
+
+ public:
+
+ /**
+ * Only root dir specified - this causes an immediate loadConfig.
+ *
+ * @param root Virtual root directory for servers. Passed to all created
+ * Server objects, which much ensure that only files from the root and
+ * its child directories are served.
+ */
+ WebServer(const QString & root);
+
+ /**
+ * @param root Virtual root directory for servers. Passed to all created
+ * Server objects, which much ensure that only files from the root and
+ * its child directories are served.
+ */
+ WebServer
+ (
+ const QString & root,
+ uint listenPort,
+ uint bandwidthLimit,
+ uint connectionLimit,
+ bool followSymlinks,
+ const QString & serverName
+ );
+
+ virtual ~WebServer();
+
+ /**
+ * Load the configuration, but do not kill existing connections even if
+ * listen port is changed. Do not change listen port yet - only when
+ * asked to restart.
+ */
+ void loadConfig();
+
+ k_dcop:
+
+ /**
+ * @return virtual root.
+ */
+ QString root();
+
+ /**
+ * @return server name
+ */
+ QString serverName();
+
+ /**
+ * @return amount of bytes that may be sent out per second, in total
+ * (adding the output of all contained Server objects.)
+ */
+ ulong bandwidthLimit();
+
+ /**
+ * @return number of concurrent connections that will be served (by
+ * creating Server objects. More connections may wait in a backlog.)
+ */
+ uint connectionLimit();
+
+ /**
+ * @return port on which to listen for incoming connections.
+ */
+ uint listenPort();
+
+ /**
+ * @return true if requests may include symbolic links in their path.
+ */
+ bool followSymlinks();
+
+ /**
+ * @return true if custom error messages (set by the user) should be
+ * sent.
+ */
+ bool customErrorMessages();
+
+ /**
+ * Set the maximum amount of bytes that may be sent out per second, in
+ * total (adding the output of all contained Server objects.)
+ */
+ void setBandwidthLimit (ulong);
+
+ /**
+ * Set the number of concurrent connections that will be served (by
+ * creating Server objects. More connections may wait in a backlog.)
+ */
+ void setConnectionLimit (uint);
+
+ /**
+ * Set the port on which to listen for incoming connections. Does not
+ * take effect until restart() is called.
+ */
+ void setListenPort (uint);
+
+ /**
+ * Set server name
+ */
+ void setServerName (const QString&);
+
+ /**
+ * Set whether requests may include symbolic links in their path.
+ */
+ void setFollowSymlinks (bool);
+
+ /**
+ * Set whether custom error messages (set by the user) should be
+ * sent.
+ */
+ void setCustomErrorMessages (bool);
+
+ /**
+ * Convenience method for setting many attributes at once.
+ */
+ void set
+ (
+ uint listenPort,
+ ulong bandwidthLimit,
+ uint connectionLimit,
+ bool followSymlinks,
+ const QString& serverName
+ );
+
+ /**
+ * Kill all connections, stop listening on port, and start listening on
+ * (possibly different) port.
+ */
+ void restart();
+
+ /**
+ * Start / stop accepting new connections.
+ */
+ void pause(bool);
+
+ /**
+ * @return true if no new connections are accepted.
+ */
+ bool paused();
+
+ /**
+ * @return true if this WebServer is unable to listen on the requested
+ * port.
+ */
+ bool portContention();
+
+ /**
+ * @return number of Server objects serving requests.
+ */
+ uint connectionCount();
+
+ protected slots:
+
+ /**
+ * Called repeatedly by a timer when this WebServer is in contention for
+ * its listen port.
+ */
+ void slotBind ();
+
+ /**
+ * Called by contained socket object when a new incoming connection is
+ * made.
+ */
+ void slotConnection (int);
+
+ /**
+ * Called by a Server when it has finished all transactions and is ready
+ * to die.
+ */
+ void slotFinished (Server *);
+
+ /**
+ * Called by a Server when it has sent data to the remote client.
+ */
+ void slotOutput (Server *, ulong);
+
+ /**
+ * Called by a Server when it wishes to send data to the remote client.
+ */
+ void slotReadyToWrite (Server *);
+
+ /**
+ * Called regularly by a timer to start output allocation (to Server
+ * objects.)
+ */
+ void slotWrite ();
+
+ /**
+ * Called regularly by a timer to check output for current time slice.
+ */
+ void slotCheckOutput ();
+
+ /**
+ * Called regularly by a timer to handle connections queued in the
+ * backlog.
+ */
+ void slotClearBacklog ();
+
+ /**
+ * Called when this succesfully publishes via zeroconf, or there was an
+ * error doing so.
+ */
+ void wasPublished(bool ok);
+
+ protected:
+
+ /**
+ * Attempt to create a Server to handle a new connection.
+ * @param fd file descriptor.
+ */
+ bool handleConnection(int fd);
+
+ void saveConfig();
+
+ signals:
+
+ /**
+ * @param bytes number of bytes sent by this server during the last
+ * time slice.
+ */
+ void wholeServerOutput (ulong bytes);
+
+ /**
+ * Emitted when a Server object has received a request from its remote
+ * client.
+ */
+ void request (Server *);
+
+ /**
+ * Emitted when a Server object has created a response which it wishes
+ * to send to its remote client.
+ */
+ void response (Server *);
+
+ /**
+ * Emitted when a Server object when data has been send to remote client.
+ * @param bytes number of bytes sent to remote client.
+ */
+ void output (Server *, ulong bytes);
+
+ /**
+ * Emitted when a new Server has been created.
+ */
+ void connection (Server *);
+
+ /**
+ * Emitted when a Server has finished all transactions.
+ */
+ void finished (Server *);
+
+ /**
+ * Emitted when this WebServer is now contending, or has stopped
+ * contending, its listen port.
+ */
+ void contentionChange (bool);
+
+ /**
+ * Emitted when this WebServer is now accepting, or has stopped
+ * accepting, incoming connections.
+ */
+ void pauseChange (bool);
+
+ /**
+ * Emitted when the number active Server objects (served connections)
+ * changes.
+ */
+ void connectionCount (uint);
+
+ private:
+
+ /**
+ * @return number of bytes that may be sent out. Changes over time,
+ * depending on bandwidth limit.
+ */
+ ulong bytesLeft() const;
+
+ /**
+ * @return number of bytes that may be sent out by each Server, with the
+ * total split between the existing Server objects.
+ */
+ ulong bandwidthPerClient() const;
+
+
+ /** publish this to dns-sd (zeroconf)
+ */
+ void publish();
+ /**
+ * Cause all existing Server objects to close their connections by
+ * calling Server::cancel.
+ */
+ void killAllConnections();
+
+ class Private;
+ Private * d;
+ };
+
+} // End namespace KPF
+
+#endif // WEB_SERVER_H
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/WebServerManager.cpp b/kpf/src/WebServerManager.cpp
new file mode 100644
index 00000000..feec661d
--- /dev/null
+++ b/kpf/src/WebServerManager.cpp
@@ -0,0 +1,295 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+// KDE includes
+#include <kconfig.h>
+#include <kglobal.h>
+#include <kapplication.h>
+
+// Local includes
+#include "Defines.h"
+#include "WebServerManager.h"
+#include "WebServer.h"
+#include "WebServer_stub.h"
+
+namespace KPF
+{
+ WebServerManager * WebServerManager::instance_ = 0L;
+
+ WebServerManager *
+ WebServerManager::instance()
+ {
+ if (0 == instance_)
+ instance_ = new WebServerManager;
+
+ return instance_;
+ }
+
+ void
+ WebServerManager::shutdown()
+ {
+ delete instance_;
+ instance_ = 0;
+ }
+
+ WebServerManager::WebServerManager()
+ : DCOPObject("WebServerManager"),
+ QObject()
+ {
+ serverList_.setAutoDelete(true);
+ }
+
+ WebServerManager::~WebServerManager()
+ {
+ // Empty.
+ }
+
+ QPtrList<WebServer>
+ WebServerManager::serverListLocal()
+ {
+ return serverList_;
+ }
+
+ WebServer *
+ WebServerManager::createServerLocal
+ (
+ const QString & root,
+ uint listenPort,
+ uint bandwidthLimit,
+ uint connectionLimit,
+ bool followSymlinks,
+ const QString & serverName
+ )
+ {
+ if (0 != server(root))
+ return 0;
+ if ( listenPort == 0)
+ listenPort = nextFreePort();
+ WebServer * server =
+ new WebServer
+ (
+ root,
+ listenPort,
+ bandwidthLimit,
+ connectionLimit,
+ followSymlinks,
+ serverName
+ );
+
+ serverList_.append(server);
+
+ saveConfig();
+
+ emit(serverCreated(server));
+
+ return server;
+ }
+
+ void
+ WebServerManager::loadConfig()
+ {
+ KConfig config(Config::name());
+
+ config.setGroup("General");
+
+ QStringList serverRootList = config.readListEntry("ServerRootList");
+
+ QStringList::ConstIterator it;
+
+ for (it = serverRootList.begin(); it != serverRootList.end(); ++it)
+ {
+ WebServer * s = new WebServer(*it);
+ serverList_.append(s);
+ s->loadConfig();
+ emit(serverCreated(s));
+ }
+ }
+
+ void
+ WebServerManager::saveConfig() const
+ {
+ KConfig config(Config::name());
+
+ config.setGroup("General");
+
+ QPtrListIterator<WebServer> it(serverList_);
+
+ QStringList serverRootList;
+
+ for (; it.current(); ++it)
+ serverRootList << it.current()->root();
+
+ config.writeEntry("ServerRootList", serverRootList);
+
+ config.sync();
+ }
+
+ WebServer *
+ WebServerManager::server(const QString & root)
+ {
+ QPtrListIterator<WebServer> it(serverList_);
+
+ for (; it.current(); ++it)
+ {
+ kpfDebug << "WebServerManager::server(): found root of " <<
+ "\"" << it.current()->root() << "\"" << endl;
+
+ if (it.current()->root() == root)
+ {
+ kpfDebug
+ << "WebServerManager::server(" << root << "): found" << endl;
+ return it.current();
+ }
+ }
+
+ kpfDebug
+ << "WebServerManager::server(" << root << "): not found" << endl;
+ return 0;
+ }
+
+ bool
+ WebServerManager::disableServer(const QString & root)
+ {
+ WebServer * existing = server(root);
+
+ if (0 == existing)
+ {
+ return false;
+ }
+ else
+ {
+ emit(serverDisabled(existing));
+ // Auto-deleted by list.
+ serverList_.removeRef(existing);
+ saveConfig();
+ return true;
+ }
+ }
+
+ QValueList<DCOPRef>
+ WebServerManager::serverList()
+ {
+ QValueList<DCOPRef> l;
+
+ QPtrListIterator<WebServer> it(serverList_);
+
+ for (; it.current(); ++it)
+ l << DCOPRef(it.current());
+
+ return l;
+ }
+
+ DCOPRef
+ WebServerManager::createServer
+ (
+ QString root,
+ uint listenPort,
+ uint bandwidthLimit,
+ uint connectionLimit,
+ bool followSymlinks,
+ QString serverName
+ )
+ {
+ WebServer * server = createServerLocal
+ (root, listenPort, bandwidthLimit, connectionLimit, followSymlinks, serverName);
+
+ if (0 == server)
+ return DCOPRef();
+ else
+ return DCOPRef(server);
+ }
+
+ void
+ WebServerManager::disableServer(DCOPRef serverRef)
+ {
+ if (serverRef.isNull())
+ return;
+
+ WebServer_stub webServer
+ (serverRef.app(), serverRef.object());
+
+ QString root = webServer.root();
+
+ if (DCOPStub::CallFailed == webServer.status())
+ {
+ kpfDebug << "Real shitty mess here" << endl;
+ return;
+ }
+
+ bool ok = disableServer(root);
+
+ if (!ok)
+ {
+ kpfDebug << "Definitely a real shitty mess here" << endl;
+ return;
+ }
+ }
+
+ void
+ WebServerManager::quit()
+ {
+// kapp->quit();
+ }
+
+ bool
+ WebServerManager::hasServer(const QString & s)
+ {
+ QString root(s);
+
+ if ('/' == root.at(root.length() - 1))
+ {
+ root.truncate(root.length() - 1);
+ }
+
+ return (0 != server(root) || 0 != server(root + "/"));
+ }
+
+ uint WebServerManager::nextFreePort() const
+ {
+ for (uint port = Config::DefaultListenPort; port < 65536; ++port)
+ {
+ bool ok = true;
+
+ for (QPtrListIterator<WebServer> it(serverList_); it.current(); ++it)
+ {
+ if (it.current()->listenPort() == port)
+ {
+ ok = false;
+ break;
+ }
+ }
+
+ if (ok)
+ {
+ return port;
+ }
+ }
+
+ // Not much we can do.
+ return Config::DefaultListenPort;
+ }
+
+} // End namespace KPF
+
+#include "WebServerManager.moc"
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/WebServerManager.h b/kpf/src/WebServerManager.h
new file mode 100644
index 00000000..6faa8a30
--- /dev/null
+++ b/kpf/src/WebServerManager.h
@@ -0,0 +1,173 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef KPF_WEB_SERVER_MANAGER_H
+#define KPF_WEB_SERVER_MANAGER_H
+
+#include <dcopobject.h>
+#include <dcopref.h>
+
+#include "Defaults.h"
+
+#include <qptrlist.h>
+
+namespace KPF
+{
+ class WebServer;
+
+ /**
+ * Singleton, encapsulating a set of WebServer objects. Handles
+ * creating WebServer objects at startup (based on settings) and
+ * on demand. Destroys WebServer objects on demand.
+ */
+ class WebServerManager : public QObject, virtual public DCOPObject
+ {
+ Q_OBJECT
+ K_DCOP
+
+ public:
+
+ static WebServerManager * instance();
+
+ /**
+ * Calls delete(this).
+ */
+ void shutdown();
+
+ /**
+ * @return a list of pointers to WebServer objects managed
+ * by this object.
+ */
+ QPtrList<WebServer> serverListLocal();
+
+ /**
+ * @return a pointer to a new WebServer object, with the root
+ * as specified, or 0 if creation was impossible. Updates
+ * the configuration.
+ */
+ WebServer * createServerLocal
+ (
+ const QString & root,
+ uint listenPort,
+ uint bandwidthLimit = Config::DefaultBandwidthLimit,
+ uint connectionLimit = Config::DefaultConnectionLimit,
+ bool followSymlinks = Config::DefaultFollowSymlinks,
+ const QString & serverName = QString::null
+ );
+
+ /**
+ * Disables a WebServer and updates the configuration.
+ */
+ bool disableServer(const QString & root);
+
+ /**
+ * Loads the configuration.
+ * Creates WebServer objects to match the configuration and
+ * ensures each object loads its configuration.
+ */
+ void loadConfig();
+
+ /**
+ * Saves the configuration.
+ * Also ensures each WebServer object saves its configuration.
+ */
+ void saveConfig() const;
+
+ /**
+ * Find a WebServer or return 0.
+ */
+ WebServer * server(const QString & root);
+
+ /**
+ * Ask a server to re-read its configuration.
+ */
+ bool reconfigureServer(const QString & root);
+
+ /**
+ * Pause/unpause a server.
+ */
+ bool pauseServer(const QString & root, bool);
+
+ /**
+ * @return whether the server is paused.
+ */
+ bool serverPaused(const QString & root);
+
+ /**
+ * Restart a server.
+ */
+ bool restartServer(const QString & root);
+
+ /**
+ * @return if a Server object with the specified root exists. Handles
+ * the two possible variations of trailing slash, i.e. existing and not
+ * existing.
+ */
+ bool hasServer(const QString & root);
+
+ uint nextFreePort() const;
+
+ k_dcop:
+
+ QValueList<DCOPRef> serverList();
+
+ DCOPRef createServer
+ (
+ QString root,
+ uint listenPort,
+ uint bandwidthLimit,
+ uint connectionLimit,
+ bool followSymlinks,
+ QString serverName
+ );
+
+ void disableServer(DCOPRef);
+
+ void quit();
+
+ protected:
+
+ /**
+ * Not used, as this is a singleton.
+ */
+ WebServerManager();
+
+ virtual ~WebServerManager();
+
+ signals:
+
+ void serverCreated(WebServer *);
+ void serverDisabled(WebServer *);
+
+ private:
+
+ static WebServerManager * instance_;
+
+ void load();
+ QPtrList<WebServer> serverList_;
+ };
+
+} // End namespace KPF
+
+#endif // WEB_SERVER_MANAGER_H
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/WebServerSocket.cpp b/kpf/src/WebServerSocket.cpp
new file mode 100644
index 00000000..4dfa6626
--- /dev/null
+++ b/kpf/src/WebServerSocket.cpp
@@ -0,0 +1,44 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include "Defines.h"
+#include "WebServerSocket.h"
+
+namespace KPF
+{
+ WebServerSocket::WebServerSocket(Q_UINT16 port, uint maxconn)
+ : QServerSocket(port, maxconn, 0L)
+ {
+ // Empty.
+ }
+
+ void
+ WebServerSocket::newConnection(int fd)
+ {
+ emit(connection(fd));
+ }
+
+} // End namespace KPF
+
+#include "WebServerSocket.moc"
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/WebServerSocket.h b/kpf/src/WebServerSocket.h
new file mode 100644
index 00000000..f45e0d70
--- /dev/null
+++ b/kpf/src/WebServerSocket.h
@@ -0,0 +1,51 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef WEB_SERVER_SOCKET_H
+#define WEB_SERVER_SOCKET_H
+
+#include <qserversocket.h>
+
+namespace KPF
+{
+ /**
+ * Overridden to emit a signal on a new connection.
+ */
+ class WebServerSocket : public QServerSocket
+ {
+ Q_OBJECT
+
+ public:
+
+ WebServerSocket(Q_UINT16 port, uint maxconn);
+ virtual void newConnection(int fd);
+
+ signals:
+
+ void connection(int);
+ };
+
+} // End namespace KPF
+
+#endif // WEB_SERVER_SOCKET_H
+// vim:ts=2:sw=2:tw=78:et