generic: drop KIO HTTP(S) support

Signed-off-by: Ivailo Monev <xakepa10@gmail.com>
This commit is contained in:
Ivailo Monev 2022-02-16 08:49:13 +02:00
parent ab7d1d326a
commit 581afd3caa
71 changed files with 125 additions and 18388 deletions

View file

@ -42,7 +42,7 @@ jobs:
sudo apt-get install -qq wget
sudo wget https://raw.githubusercontent.com/fluxer/katana-ubuntu/master/katana.list -O /etc/apt/sources.list.d/katana.list
sudo apt-get update -qq
sudo apt-get install -qq cmake katie-dev libenchant-dev libmagick++-dev libmpv-dev xorg-dev mesa-common-dev libavahi-common-dev krb5-multidev libwebp-dev libudev-dev liblzma-dev libexiv2-dev libbz2-dev libattr1-dev libacl1-dev libcdio-dev strigi libdbusmenu-katie media-player-info krb5-config shared-mime-info media-player-info xdg-utils
sudo apt-get install -qq cmake katie-dev libenchant-dev libmagick++-dev libmpv-dev xorg-dev mesa-common-dev libavahi-common-dev libwebp-dev libudev-dev liblzma-dev libexiv2-dev libbz2-dev libattr1-dev libacl1-dev libcdio-dev strigi libdbusmenu-katie media-player-info shared-mime-info media-player-info xdg-utils
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL

View file

@ -181,14 +181,6 @@ set_package_properties(WebP PROPERTIES
TYPE OPTIONAL
)
macro_optional_find_package(GSSAPI)
set_package_properties(GSSAPI PROPERTIES
DESCRIPTION "Allows KIO to make use of certain HTTP authentication services"
URL "http://web.mit.edu/kerberos/www"
TYPE OPTIONAL
PURPOSE "A MIT or HEIMDAL flavor of GSSAPI can be used"
)
# v143+ required for udev_monitor_filter_add_match_subsystem_devtype()
macro_optional_find_package(UDev 143)
set_package_properties(UDev PROPERTIES

View file

@ -20,10 +20,10 @@ build_script:
sudo apt-get install -qq cmake katie-dev libenchant-dev \
libmagick++-dev libmpv-dev xorg-dev mesa-common-dev \
libavahi-common-dev krb5-multidev libwebp-dev libudev-dev \
liblzma-dev libexiv2-dev libbz2-dev libacl1-dev \
libcdio-dev strigi libdbusmenu-katie media-player-info \
krb5-config shared-mime-info media-player-info xdg-utils ccache
libavahi-common-dev libwebp-dev libudev-dev liblzma-dev \
libexiv2-dev libbz2-dev libacl1-dev libcdio-dev strigi \
libdbusmenu-katie media-player-info shared-mime-info \
media-player-info xdg-utils ccache
export PATH="/usr/lib/ccache/:$PATH"

View file

@ -1,79 +0,0 @@
# Try to detect the GSSAPI support, once done this will define:
#
# GSSAPI_FOUND - system supports GSSAPI
# GSSAPI_INCS - the GSSAPI include directory
# GSSAPI_LIBS - the libraries needed to use GSSAPI
# GSSAPI_FLAVOR - the type of API - MIT or HEIMDAL
# Copyright (c) 2006 Pino Toscano <toscano.pino@tiscali.it>
#
# Redistribution and use is allowed according to the terms of the BSD license.
# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
if(GSSAPI_LIBS AND GSSAPI_FLAVOR)
# in cache already
set(GSSAPI_FOUND TRUE)
else(GSSAPI_LIBS AND GSSAPI_FLAVOR)
find_program(KRB5_CONFIG
NAMES krb5-config krb5-config.mit
PATHS /opt/local/bin
)
mark_as_advanced(KRB5_CONFIG)
#reset vars
set(GSSAPI_INCS)
set(GSSAPI_LIBS)
set(GSSAPI_FLAVOR)
if(KRB5_CONFIG)
execute_process(
COMMAND ${KRB5_CONFIG} --libs gssapi
RESULT_VARIABLE _return_VALUE
OUTPUT_VARIABLE GSSAPI_LIBS
OUTPUT_STRIP_TRAILING_WHITESPACE
)
execute_process(
COMMAND ${KRB5_CONFIG} --cflags gssapi
RESULT_VARIABLE _return_VALUE
OUTPUT_VARIABLE GSSAPI_INCS
OUTPUT_STRIP_TRAILING_WHITESPACE
)
string(REGEX REPLACE "-I" "" GSSAPI_INCS "${GSSAPI_INCS}")
string(REGEX REPLACE "-isystem" "" GSSAPI_INCS "${GSSAPI_INCS}")
execute_process(
COMMAND ${KRB5_CONFIG} --vendor
RESULT_VARIABLE _return_VALUE
OUTPUT_VARIABLE gssapi_flavor_tmp
OUTPUT_STRIP_TRAILING_WHITESPACE
)
set(GSSAPI_FLAVOR_MIT)
if(gssapi_flavor_tmp MATCHES ".*Massachusetts.*")
set(GSSAPI_FLAVOR "MIT")
else(gssapi_flavor_tmp MATCHES ".*Massachusetts.*")
set(GSSAPI_FLAVOR "HEIMDAL")
endif(gssapi_flavor_tmp MATCHES ".*Massachusetts.*")
if (gssapi_flavor_tmp MATCHES "Sun Microsystems.*")
message(STATUS "Solaris Kerberos does not have GSSAPI; this is normal.")
set(GSSAPI_LIBS)
set(GSSAPI_INCS)
endif(gssapi_flavor_tmp MATCHES "Sun Microsystems.*")
if(GSSAPI_LIBS) # GSSAPI_INCS can be also empty, so don't rely on that
set(GSSAPI_FOUND TRUE)
message(STATUS "Found GSSAPI: ${GSSAPI_LIBS}")
mark_as_advanced(GSSAPI_INCS GSSAPI_LIBS GSSAPI_FLAVOR)
endif(GSSAPI_LIBS)
endif(KRB5_CONFIG)
endif(GSSAPI_LIBS AND GSSAPI_FLAVOR)

View file

@ -42,8 +42,6 @@ if(ENABLE_TESTING)
endif()
set(kiocore_STAT_SRCS
kio/accessmanager.cpp
kio/accessmanagerreply_p.cpp
kio/authinfo.cpp
kio/chmodjob.cpp
kio/connection.cpp
@ -255,7 +253,6 @@ generate_export_header(kio)
install(
FILES
${CMAKE_CURRENT_BINARY_DIR}/kio_export.h
kio/accessmanager.h
kio/connection.h
kio/slaveinterface.h
kio/slave.h

View file

@ -1,12 +0,0 @@
HTTPFilter and the inherited classes are used by kio_http and
khtml/kmultipart. The source code is located here for better code sharing.
Testcases:
- http://david.fullrecall.com/browser-http-compression-test?compression=deflate-http (bug 160289)
- http://demo.serv-u.com/?user=demo-WC&password=demo-WC (Content-Encoding: deflate) (bug 188935)
- http://web.davidfaure.fr/cgi-bin/deflate_test (Content-Encoding: deflate) (bug 114830)
- http://www.zlib.net/zlib_faq.html#faq39 (Content-Encoding: gzip)
- wikipedia (Content-Encoding: gzip)
- cnn.com (Content-Encoding: gzip)
- http://arstechnica.com/ (Content-Encoding: gzip)
- mailman admin interface on mail.kde.org (see r266769, but can't confirm these days)

View file

@ -1,193 +0,0 @@
/*
This file is part of the KDE libraries
Copyright (c) 2002 Waldo Bastian <bastian@kde.org>
Copyright 2009 David Faure <faure@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License version 2 as published by the Free Software Foundation.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "httpfilter.h"
#include <kgzipfilter.h>
#include <kdebug.h>
#include <klocale.h>
#include <stdio.h>
HTTPFilterBase::HTTPFilterBase()
: last(0)
{
}
HTTPFilterBase::~HTTPFilterBase()
{
delete last;
}
void
HTTPFilterBase::chain(HTTPFilterBase *previous)
{
last = previous;
connect(last, SIGNAL(output(QByteArray)),
this, SLOT(slotInput(QByteArray)));
}
HTTPFilterChain::HTTPFilterChain()
: first(0)
{
}
void
HTTPFilterChain::addFilter(HTTPFilterBase *filter)
{
if (!last)
{
first = filter;
}
else
{
disconnect(last, SIGNAL(output(QByteArray)), 0, 0);
filter->chain(last);
}
last = filter;
connect(filter, SIGNAL(output(QByteArray)),
this, SIGNAL(output(QByteArray)));
connect(filter, SIGNAL(error(QString)),
this, SIGNAL(error(QString)));
}
void
HTTPFilterChain::slotInput(const QByteArray &d)
{
if (first)
first->slotInput(d);
else
emit output(d);
}
HTTPFilterMD5::HTTPFilterMD5()
{
context = new QCryptographicHash(QCryptographicHash::Md5);
}
QString
HTTPFilterMD5::md5()
{
// it's either base64 or quoted md5
return QString::fromLatin1(context->result().toBase64());
}
void
HTTPFilterMD5::slotInput(const QByteArray &d)
{
context->addData(d);
emit output(d);
}
HTTPFilterGZip::HTTPFilterGZip(bool deflate)
: m_deflateMode(deflate),
m_firstData(true),
m_finished(false)
{
// We can't use KFilterDev because it assumes it can read as much data as necessary
// from the underlying device. It's a pull strategy, while we have to do
// a push strategy.
m_gzipFilter = new KGzipFilter;
}
HTTPFilterGZip::~HTTPFilterGZip()
{
m_gzipFilter->terminate();
delete m_gzipFilter;
}
/*
The data format used by the zlib library is described by RFCs (Request for
Comments) 1950 to 1952 in the files ftp://ds.internic.net/rfc/rfc1950.txt
(zlib format), rfc1951.txt (deflate format) and rfc1952.txt (gzip format).
Use /usr/include/zlib.h as the primary source of documentation though.
*/
void
HTTPFilterGZip::slotInput(const QByteArray &d)
{
if (d.isEmpty())
return;
//kDebug() << "Got" << d.size() << "bytes as input";
if (m_firstData) {
if (m_deflateMode) {
bool zlibHeader = true;
// Autodetect broken webservers (thanks Microsoft) who send raw-deflate
// instead of zlib-headers-deflate when saying Content-Encoding: deflate.
const unsigned char firstChar = d[0];
if ((firstChar & 0x0f) != 8) {
// In a zlib header, CM should be 8 (cf RFC 1950)
zlibHeader = false;
} else if (d.size() > 1) {
const unsigned char flg = d[1];
if ((firstChar * 256 + flg) % 31 != 0) { // Not a multiple of 31? invalid zlib header then
zlibHeader = false;
}
}
//if (!zlibHeader)
// kDebug() << "Bad webserver, uses raw-deflate instead of zlib-deflate...";
m_gzipFilter->init(QIODevice::ReadOnly, zlibHeader ? KGzipFilter::ZlibHeader : KGzipFilter::RawDeflate);
} else {
m_gzipFilter->init(QIODevice::ReadOnly, KGzipFilter::GZipHeader);
}
m_firstData = false;
}
m_gzipFilter->setInBuffer(d.constData(), d.size());
while (!m_gzipFilter->inBufferEmpty() && !m_finished) {
char buf[8192];
m_gzipFilter->setOutBuffer(buf, sizeof(buf));
KFilterBase::Result result = m_gzipFilter->uncompress();
//kDebug() << "uncompress returned" << result;
switch (result) {
case KFilterBase::Ok:
case KFilterBase::End:
{
const int bytesOut = sizeof(buf) - m_gzipFilter->outBufferAvailable();
if (bytesOut) {
emit output(QByteArray(buf, bytesOut));
}
if (result == KFilterBase::End) {
//kDebug() << "done, bHasFinished=true";
emit output(QByteArray());
m_finished = true;
}
break;
}
case KFilterBase::Error:
kWarning() << "Error from KGZipFilter";
emit error(i18n("Receiving corrupt data."));
m_finished = true; // exit this while loop
break;
}
}
}
HTTPFilterDeflate::HTTPFilterDeflate()
: HTTPFilterGZip(true)
{
}
#include "moc_httpfilter.cpp"

View file

@ -1,107 +0,0 @@
/*
This file is part of the KDE libraries
Copyright (c) 2002 Waldo Bastian <bastian@kde.org>
Copyright 2009 David Faure <faure@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License version 2 as published by the Free Software Foundation.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef _HTTPFILTER_H_
#define _HTTPFILTER_H_
#include <config.h>
class KGzipFilter;
#include <QBuffer>
#include <QObject>
#include <QCryptographicHash>
class HTTPFilterBase : public QObject
{
Q_OBJECT
public:
HTTPFilterBase();
~HTTPFilterBase();
void chain(HTTPFilterBase *previous);
public Q_SLOTS:
virtual void slotInput(const QByteArray &d) = 0;
Q_SIGNALS:
void output(const QByteArray &d);
void error(const QString &);
protected:
HTTPFilterBase *last;
};
class HTTPFilterChain : public HTTPFilterBase
{
Q_OBJECT
public:
HTTPFilterChain();
void addFilter(HTTPFilterBase *filter);
public Q_SLOTS:
void slotInput(const QByteArray &d);
private:
HTTPFilterBase *first;
};
class HTTPFilterMD5 : public HTTPFilterBase
{
Q_OBJECT
public:
HTTPFilterMD5();
QString md5();
public Q_SLOTS:
void slotInput(const QByteArray &d);
private:
QCryptographicHash *context;
};
class HTTPFilterGZip : public HTTPFilterBase
{
Q_OBJECT
public:
HTTPFilterGZip(bool deflate = false /* for subclass HTTPFilterDeflate */ );
~HTTPFilterGZip();
public Q_SLOTS:
void slotInput(const QByteArray &d);
private:
bool m_deflateMode;
bool m_firstData;
bool m_finished;
KGzipFilter* m_gzipFilter;
};
class HTTPFilterDeflate : public HTTPFilterGZip
{
Q_OBJECT
public:
HTTPFilterDeflate();
};
#endif

View file

@ -1,535 +0,0 @@
/*
* This file is part of the KDE project.
*
* Copyright (C) 2009 - 2012 Dawit Alemayehu <adawit @ kde.org>
* Copyright (C) 2008 - 2009 Urs Wolfer <uwolfer @ kde.org>
* Copyright (C) 2007 Trolltech ASA
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
#include "accessmanager.h"
#include "accessmanagerreply_p.h"
#include "job.h"
#include "scheduler.h"
#include "jobuidelegate.h"
#include "netaccess.h"
#include <kdebug.h>
#include <kconfiggroup.h>
#include <ksharedconfig.h>
#include <kprotocolinfo.h>
#include <klocalizedstring.h>
#include <QtCore/QUrl>
#include <QtCore/QPointer>
#include <QtCore/QDateTime>
#include <QtGui/QWidget>
#include <QtDBus/QDBusInterface>
#include <QtDBus/QDBusConnection>
#include <QtDBus/QDBusReply>
#include <QtNetwork/QNetworkReply>
#include <QtNetwork/QNetworkRequest>
#include <QtNetwork/QSslCipher>
#include <QtNetwork/QSslCertificate>
#include <QtNetwork/QSslConfiguration>
static qint64 sizeFromRequest(const QNetworkRequest& req)
{
const QVariant size = req.header(QNetworkRequest::ContentLengthHeader);
if (!size.isValid())
return -1;
bool ok = false;
const qlonglong value = size.toLongLong(&ok);
return (ok ? value : -1);
}
namespace KIO {
class AccessManager::AccessManagerPrivate
{
public:
AccessManagerPrivate()
: externalContentAllowed(true),
emitReadyReadOnMetaDataChange(false),
window(0)
{}
void setMetaDataForRequest(QNetworkRequest request, KIO::MetaData& metaData);
bool externalContentAllowed;
bool emitReadyReadOnMetaDataChange;
KIO::MetaData requestMetaData;
KIO::MetaData sessionMetaData;
QPointer<QWidget> window;
};
namespace Integration {
class CookieJar::CookieJarPrivate
{
public:
CookieJarPrivate()
: windowId((WId)-1),
isEnabled(true),
isStorageDisabled(false)
{}
WId windowId;
bool isEnabled;
bool isStorageDisabled;
};
}
}
using namespace KIO;
AccessManager::AccessManager(QObject *parent)
:QNetworkAccessManager(parent), d(new AccessManager::AccessManagerPrivate())
{
// KDE Cookiejar (KCookieJar) integration...
setCookieJar(new KIO::Integration::CookieJar);
}
AccessManager::~AccessManager()
{
delete d;
}
void AccessManager::setExternalContentAllowed(bool allowed)
{
d->externalContentAllowed = allowed;
}
bool AccessManager::isExternalContentAllowed() const
{
return d->externalContentAllowed;
}
void AccessManager::setWindow(QWidget* widget)
{
if (!widget) {
return;
}
d->window = widget->isWindow() ? widget : widget->window();
if (!d->window) {
return;
}
KIO::Integration::CookieJar *jar = qobject_cast<KIO::Integration::CookieJar *> (cookieJar());
if (jar) {
jar->setWindowId(d->window->winId());
}
}
QWidget* AccessManager::window() const
{
return d->window;
}
KIO::MetaData& AccessManager::requestMetaData()
{
return d->requestMetaData;
}
KIO::MetaData& AccessManager::sessionMetaData()
{
return d->sessionMetaData;
}
void AccessManager::putReplyOnHold(QNetworkReply* reply)
{
KDEPrivate::AccessManagerReply* r = qobject_cast<KDEPrivate::AccessManagerReply*>(reply);
if (!r) {
return;
}
r->putOnHold();
}
void AccessManager::setEmitReadyReadOnMetaDataChange(bool enable)
{
d->emitReadyReadOnMetaDataChange = enable;
}
QNetworkReply *AccessManager::createRequest(Operation op, const QNetworkRequest &req, QIODevice *outgoingData)
{
const KUrl reqUrl (req.url());
if (!d->externalContentAllowed &&
!KDEPrivate::AccessManagerReply::isLocalRequest(reqUrl) &&
reqUrl.scheme() != QLatin1String("data")) {
kDebug( 7044 ) << "Blocked: " << reqUrl;
return new KDEPrivate::AccessManagerReply(op, req, QNetworkReply::ContentAccessDenied, i18n("Blocked request."), this);
}
// Retrieve the KIO meta data...
KIO::MetaData metaData;
d->setMetaDataForRequest(req, metaData);
KIO::SimpleJob *kioJob = 0;
switch (op) {
case HeadOperation: {
//kDebug( 7044 ) << "HeadOperation:" << reqUrl;
kioJob = KIO::mimetype(reqUrl, KIO::HideProgressInfo);
break;
}
case GetOperation: {
//kDebug( 7044 ) << "GetOperation:" << reqUrl;
if (!reqUrl.path().isEmpty() || reqUrl.host().isEmpty())
kioJob = KIO::get(reqUrl, KIO::NoReload, KIO::HideProgressInfo);
else
kioJob = KIO::stat(reqUrl, KIO::HideProgressInfo);
// WORKAROUND: Avoid the brain damaged stuff QtWebKit does when a POST
// operation is redirected! See BR# 268694.
metaData.remove(QLatin1String("content-type")); // Remove the content-type from a GET/HEAD request!
break;
}
case PutOperation: {
//kDebug( 7044 ) << "PutOperation:" << reqUrl;
const qint64 size = sizeFromRequest(req);
if (size > 0) {
kioJob = KIO::http_post(reqUrl, outgoingData, size, KIO::HideProgressInfo);
metaData.insert(QLatin1String("CustomHTTPMethod"), QLatin1String("PUT"));
} else {
kioJob = KIO::put(reqUrl, -1, KIO::HideProgressInfo);
}
break;
}
case PostOperation: {
kioJob = KIO::http_post(reqUrl, outgoingData, sizeFromRequest(req), KIO::HideProgressInfo);
if (!metaData.contains(QLatin1String("content-type"))) {
const QVariant header = req.header(QNetworkRequest::ContentTypeHeader);
if (header.isValid()) {
metaData.insert(QLatin1String("content-type"),
(QLatin1String("Content-Type: ") + header.toString()));
} else {
metaData.insert(QLatin1String("content-type"),
QLatin1String("Content-Type: application/x-www-form-urlencoded"));
}
}
break;
}
case DeleteOperation: {
//kDebug(7044) << "DeleteOperation:" << reqUrl;
const qint64 size = sizeFromRequest(req);
if (size > 0) {
kioJob = KIO::http_post(reqUrl, outgoingData, size, KIO::HideProgressInfo);
metaData.insert(QLatin1String("CustomHTTPMethod"), QLatin1String("DELETE"));
} else {
kioJob = KIO::http_delete(reqUrl, KIO::HideProgressInfo);
}
break;
}
case CustomOperation: {
const QByteArray& method = req.attribute(QNetworkRequest::CustomVerbAttribute).toByteArray();
//kDebug(7044) << "CustomOperation:" << reqUrl << "method:" << method << "outgoing data:" << outgoingData;
if (method.isEmpty()) {
return new KDEPrivate::AccessManagerReply(op, req, QNetworkReply::ProtocolUnknownError, i18n("Unknown HTTP verb."), this);
}
const qint64 size = sizeFromRequest(req);
if (size > 0)
kioJob = KIO::http_post(reqUrl, outgoingData, size, KIO::HideProgressInfo);
else
kioJob = KIO::get(reqUrl, KIO::NoReload, KIO::HideProgressInfo);
metaData.insert(QLatin1String("CustomHTTPMethod"), method);
break;
}
default: {
kWarning(7044) << "Unsupported KIO operation requested! Defering to QNetworkAccessManager...";
return QNetworkAccessManager::createRequest(op, req, outgoingData);
}
}
// Set the job priority
switch (req.priority()) {
case QNetworkRequest::HighPriority:
KIO::Scheduler::setJobPriority(kioJob, -5);
break;
case QNetworkRequest::LowPriority:
KIO::Scheduler::setJobPriority(kioJob, 5);
break;
default:
break;
}
KDEPrivate::AccessManagerReply *reply;
/*
NOTE: Here we attempt to handle synchronous XHR requests. Unfortunately,
due to the fact that QNAM is both synchronous and multi-thread while KIO
is completely the opposite (asynchronous and not thread safe), the code
below might cause crashes like the one reported in bug# 287778 (nested
event loops are inherently dangerous).
Unfortunately, all attempts to address the crash has so far failed due to
the many regressions they caused, e.g. bug# 231932 and 297954. Hence, until
a solution is found, we have to live with the side effects of creating
nested event loops.
*/
if (req.attribute(QNetworkRequest::SynchronousRequestAttribute).toBool()) {
KUrl finalURL;
QByteArray data;
if (KIO::NetAccess::synchronousRun(kioJob, d->window, &data, &finalURL, &metaData)) {
reply = new KDEPrivate::AccessManagerReply(op, req, data, finalURL, metaData, this);
kDebug(7044) << "Synchronous XHR:" << reply << reqUrl;
} else {
kWarning(7044) << "Failed to create a synchronous XHR for" << reqUrl;
kWarning(7044) << "REASON:" << kioJob->errorString();
reply = new KDEPrivate::AccessManagerReply(op, req, QNetworkReply::UnknownNetworkError, kioJob->errorText(), this);
}
} else {
// Set the window on the the KIO ui delegate
if (d->window) {
kioJob->ui()->setWindow(d->window);
}
// Disable internal automatic redirection handling
kioJob->setRedirectionHandlingEnabled(false);
// Set the job priority
switch (req.priority()) {
case QNetworkRequest::HighPriority:
KIO::Scheduler::setJobPriority(kioJob, -5);
break;
case QNetworkRequest::LowPriority:
KIO::Scheduler::setJobPriority(kioJob, 5);
break;
default:
break;
}
// Set the meta data for this job...
kioJob->setMetaData(metaData);
// Create the reply...
reply = new KDEPrivate::AccessManagerReply(op, req, kioJob, d->emitReadyReadOnMetaDataChange, this);
//kDebug(7044) << reply << reqUrl;
}
return reply;
}
void AccessManager::AccessManagerPrivate::setMetaDataForRequest(QNetworkRequest request, KIO::MetaData& metaData)
{
// Add any meta data specified within request...
QVariant userMetaData = request.attribute (static_cast<QNetworkRequest::Attribute>(MetaData));
if (userMetaData.isValid() && userMetaData.type() == QVariant::Map)
metaData += userMetaData.toMap();
metaData.insert(QLatin1String("PropagateHttpHeader"), QLatin1String("true"));
if (request.hasRawHeader("User-Agent")) {
metaData.insert(QLatin1String("UserAgent"), request.rawHeader("User-Agent"));
request.setRawHeader("User-Agent", QByteArray());
}
if (request.hasRawHeader("Accept")) {
metaData.insert(QLatin1String("accept"), request.rawHeader("Accept"));
request.setRawHeader("Accept", QByteArray());
}
if (request.hasRawHeader("Accept-Charset")) {
metaData.insert(QLatin1String("Charsets"), request.rawHeader("Accept-Charset"));
request.setRawHeader("Accept-Charset", QByteArray());
}
if (request.hasRawHeader("Accept-Language")) {
metaData.insert(QLatin1String("Languages"), request.rawHeader("Accept-Language"));
request.setRawHeader("Accept-Language", QByteArray());
}
if (request.hasRawHeader("Referer")) {
metaData.insert(QLatin1String("referrer"), request.rawHeader("Referer"));
request.setRawHeader("Referer", QByteArray());
}
if (request.hasRawHeader("Content-Type")) {
metaData.insert(QLatin1String("content-type"), request.rawHeader("Content-Type"));
request.setRawHeader("Content-Type", QByteArray());
}
if (request.attribute(QNetworkRequest::AuthenticationReuseAttribute) == QNetworkRequest::Manual) {
metaData.insert(QLatin1String("no-preemptive-auth-reuse"), QLatin1String("true"));
}
request.setRawHeader("Content-Length", QByteArray());
request.setRawHeader("Connection", QByteArray());
request.setRawHeader("If-None-Match", QByteArray());
request.setRawHeader("If-Modified-Since", QByteArray());
request.setRawHeader("x-kdewebkit-ignore-disposition", QByteArray());
QStringList customHeaders;
Q_FOREACH(const QByteArray &key, request.rawHeaderList()) {
const QByteArray value = request.rawHeader(key);
if (value.length())
customHeaders << (key + QLatin1String(": ") + value);
}
if (!customHeaders.isEmpty()) {
metaData.insert(QLatin1String("customHTTPHeader"), customHeaders.join("\r\n"));
}
// Append per request meta data, if any...
if (!requestMetaData.isEmpty()) {
metaData += requestMetaData;
// Clear per request meta data...
requestMetaData.clear();
}
// Append per session meta data, if any...
if (!sessionMetaData.isEmpty()) {
metaData += sessionMetaData;
}
}
using namespace KIO::Integration;
static QSsl::SslProtocol qSslProtocolFromString(const QString& str)
{
if (str.compare(QLatin1String("SSLv3"), Qt::CaseInsensitive) == 0) {
return QSsl::SslV3;
}
if (str.compare(QLatin1String("TLSv1"), Qt::CaseInsensitive) == 0) {
return QSsl::TlsV1;
}
return QSsl::AnyProtocol;
}
bool KIO::Integration::sslConfigFromMetaData(const KIO::MetaData& metadata, QSslConfiguration& sslconfig)
{
bool success = false;
if (metadata.value(QLatin1String("ssl_in_use")) == QLatin1String("TRUE")) {
const QSsl::SslProtocol sslProto = qSslProtocolFromString(metadata.value(QLatin1String("ssl_protocol_version")));
QList<QSslCipher> cipherList;
cipherList << QSslCipher(metadata.value(QLatin1String("ssl_cipher_name")), sslProto);
sslconfig.setCaCertificates(QSslCertificate::fromData(metadata.value(QLatin1String("ssl_peer_chain")).toUtf8()));
sslconfig.setCiphers(cipherList);
sslconfig.setProtocol(sslProto);
success = sslconfig.isNull();
}
return success;
}
CookieJar::CookieJar(QObject* parent)
:QNetworkCookieJar(parent), d(new CookieJar::CookieJarPrivate)
{
reparseConfiguration();
}
CookieJar::~CookieJar()
{
delete d;
}
WId CookieJar::windowId() const
{
return d->windowId;
}
bool CookieJar::isCookieStorageDisabled() const
{
return d->isStorageDisabled;
}
QList<QNetworkCookie> CookieJar::cookiesForUrl(const QUrl &url) const
{
QList<QNetworkCookie> cookieList;
if (!d->isEnabled) {
return cookieList;
}
QDBusInterface kcookiejar("org.kde.kded", "/modules/kcookiejar", "org.kde.KCookieServer");
QDBusReply<QString> reply = kcookiejar.call("findDOMCookies", url.toString(QUrl::RemoveUserInfo), (qlonglong)d->windowId);
if (!reply.isValid()) {
kWarning(7044) << "Unable to communicate with the cookiejar!";
return cookieList;
}
const QString cookieStr = reply.value();
const QStringList cookies = cookieStr.split(QLatin1String("; "), QString::SkipEmptyParts);
Q_FOREACH(const QString& cookie, cookies) {
const int index = cookie.indexOf(QLatin1Char('='));
const QString name = cookie.left(index);
const QString value = cookie.right((cookie.length() - index - 1));
cookieList << QNetworkCookie(name.toUtf8(), value.toUtf8());
//kDebug(7044) << "cookie: name=" << name << ", value=" << value;
}
return cookieList;
}
bool CookieJar::setCookiesFromUrl(const QList<QNetworkCookie> &cookieList, const QUrl &url)
{
if (!d->isEnabled) {
return false;
}
QDBusInterface kcookiejar("org.kde.kded", "/modules/kcookiejar", "org.kde.KCookieServer");
Q_FOREACH(const QNetworkCookie &cookie, cookieList) {
QByteArray cookieHeader ("Set-Cookie: ");
if (d->isStorageDisabled && !cookie.isSessionCookie()) {
QNetworkCookie sessionCookie(cookie);
sessionCookie.setExpirationDate(QDateTime());
cookieHeader += sessionCookie.toRawForm();
} else {
cookieHeader += cookie.toRawForm();
}
kcookiejar.call("addCookies", url.toString(QUrl::RemoveUserInfo), cookieHeader, (qlonglong)d->windowId);
//kDebug(7044) << "[" << d->windowId << "]" << cookieHeader << " from " << url;
}
return !kcookiejar.lastError().isValid();
}
void CookieJar::setDisableCookieStorage(bool disable)
{
d->isStorageDisabled = disable;
}
void CookieJar::setWindowId(WId id)
{
d->windowId = id;
}
void CookieJar::reparseConfiguration()
{
KConfigGroup cfg = KSharedConfig::openConfig("kcookiejarrc", KConfig::NoGlobals)->group("Cookie Policy");
d->isEnabled = cfg.readEntry("Cookies", true);
}
#include "moc_accessmanager.cpp"

View file

@ -1,324 +0,0 @@
/*
* This file is part of the KDE project.
*
* Copyright (C) 2008 - 2009 Urs Wolfer <uwolfer @ kde.org>
* Copyright (C) 2009 - 2012 Dawit Alemayehu <adawit @ kde.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
#ifndef KIO_ACCESSMANAGER_H
#define KIO_ACCESSMANAGER_H
#include <kio/global.h>
#include <QtNetwork/QNetworkAccessManager>
#include <QtNetwork/QNetworkRequest>
#include <QtNetwork/QNetworkCookieJar>
#include <QWidget>
namespace KIO {
/**
* @short A KDE implementation of QNetworkAccessManager.
*
* Use this class instead of QNetworkAccessManager if you want to integrate
* with KDE's KIO and KCookieJar modules for network operations and cookie
* handling respectively.
*
* Here is a simple example that shows how to set the QtWebKit module to use KDE's
* KIO for its network operations:
* @code
* QWebView *view = new QWebView(this);
* KIO::Integration::AccessManager *manager = new KIO::Integration::AccessManager(view);
* view->page()->setNetworkAccessManager(manager);
* @endcode
*
* To access member functions in the cookiejar class at a later point in your
* code simply downcast the pointer returned by QWebPage::networkAccessManager
* as follows:
* @code
* KIO::Integration::AccessManager *manager = qobject_cast<KIO::Integration::AccessManager*>(view->page()->accessManager());
* @endcode
*
* Please note that this class is in the KIO namespace for backward compatablity.
* You should use KIO::Integration::AccessManager to access this class in your
* code.
*
* <b>IMPORTANT</b>This class is not a replacement for the standard KDE API.
* It should ONLY be used to provide KDE integration in applications that
* cannot use the standard KDE API directly.
*
* @author Urs Wolfer \<uwolfer @ kde.org\>
* @author Dawit Alemayehu \<adawit @ kde.org\>
*
* @deprecated Use the KIO::Integration::AccessManager typedef to access this class instead.
* @since 4.3
*/
class KIO_EXPORT AccessManager : public QNetworkAccessManager
{
Q_OBJECT
public:
/*!
* Extensions to QNetworkRequest::Attribute enums.
* @since 4.3.2
*/
enum Attribute {
MetaData = QNetworkRequest::User, /** < Used to send KIO MetaData back and forth. type: QVariant::Map. */
KioError /**< Used to send KIO error codes that cannot be mapped into QNetworkReply::NetworkError. type: QVariant::Int */
};
/**
* Constructor
*/
AccessManager(QObject *parent);
/**
* Destructor
*/
virtual ~AccessManager();
/**
* Set @p allowed to false if you don't want any external content to be fetched.
* By default external content is fetched.
*/
void setExternalContentAllowed(bool allowed);
/**
* Returns true if external content is going to be fetched.
*
* @see setExternalContentAllowed
*/
bool isExternalContentAllowed() const;
/**
* Sets the window associated with this network access manager.
*
* Note that @p widget will be used as a parent for dialogs in KIO as well
* as the cookie jar. If @p widget is not a window, this function will
* invoke @ref QWidget::window() to obtain the window for the given widget.
*
* @see KIO::Integration::CookieJar::setWindow.
* @since 4.7
*/
void setWindow(QWidget* widget);
/**
* Returns the window associated with this network access manager.
*
* @see setWindow
* @since 4.7
*/
QWidget* window() const;
/**
* Returns a reference to the temporary meta data container.
*
* See kdelibs/kio/DESIGN.metadata for list of supported KIO meta data.
*
* Use this function when you want to set per request KIO meta data that
* will be removed after it has been sent once.
*
* @since 4.4
*/
KIO::MetaData& requestMetaData();
/**
* Returns a reference to the persistent meta data container.
*
* See kdelibs/kio/DESIGN.metadata for list of supported KIO meta data.
*
* Use this function when you want to set per session KIO meta data that
* will be sent with every request.
*
* Unlike @p requestMetaData, the meta data values set using the reference
* returned by this function will not be deleted and will be sent with every
* request.
*
* @since 4.4
*/
KIO::MetaData& sessionMetaData();
/**
* Puts the ioslave associated with the given @p reply on hold.
*
* This function is intended to make possible the implementation of
* the special case mentioned in KIO::get's documentation within the
* KIO-QNAM integration.
*
* @see KIO::get.
* @since 4.6
*/
static void putReplyOnHold(QNetworkReply* reply);
/**
* Sets the network reply object to emit readyRead when it receives meta data.
*
* Meta data is any information that is not the actual content itself, e.g.
* HTTP response headers of the HTTP protocol.
*
* Calling this function will force the code connecting to QNetworkReply's
* readyRead signal to prematurely start dealing with the content that might
* not yet have arrived. However, it is essential to make the put ioslave on
* hold functionality of KIO work in libraries like QtWebKit.
*
* @see QNetworkReply::metaDataChanged
* @since 4.7
*/
void setEmitReadyReadOnMetaDataChange(bool);
protected:
/**
* Reimplemented for internal reasons, the API is not affected.
*
* @see QNetworkAccessManager::createRequest
* @internal
*/
virtual QNetworkReply *createRequest(Operation op, const QNetworkRequest &req, QIODevice *outgoingData = 0);
private:
class AccessManagerPrivate;
AccessManagerPrivate* const d;
};
namespace Integration {
// KDE5: Move AccessManager into the KIO::Integration namespace.
typedef KIO::AccessManager AccessManager;
/**
* Maps KIO SSL meta data into the given QSslConfiguration object.
*
* @since 4.5
* @return true if @p metadata contains ssl information and the mapping succeeded.
*/
KIO_EXPORT bool sslConfigFromMetaData(const KIO::MetaData& metadata, QSslConfiguration& sslconfig);
/**
* @short A KDE implementation of QNetworkCookieJar.
*
* Use this class in place of QNetworkCookieJar if you want to integrate with
* KDE's cookiejar instead of the one that comes with Qt.
*
* Here is a simple example that shows how to set the QtWebKit module to use KDE's
* cookiejar:
* @code
* QWebView *view = new QWebView(this);
* KIO::Integration::CookieJar *cookieJar = new KIO::Integration::CookieJar;
* cookieJar->setWindowId(view->window()->winId());
* view->page()->networkAccessManager()->setCookieJar(cookieJar);
* @endcode
*
* To access member functions in the cookiejar class at a later point in your
* code simply downcast the pointer returned by QNetworkAccessManager::cookieJar
* as follows:
* @code
* KIO::Integration::CookieJar *cookieJar = qobject_cast<KIO::Integration::CookieJar*>(view->page()->accessManager()->cookieJar());
* @endcode
*
* <b>IMPORTANT</b>This class is not a replacement for the standard KDE API.
* It should ONLY be used to to provide KDE integration in applications that
* cannot use the standard KDE API directly.
*
* @see QNetworkAccessManager::setCookieJar for details.
*
* @author Dawit Alemayehu \<adawit @ kde.org\>
* @since 4.4
*/
class KIO_EXPORT CookieJar : public QNetworkCookieJar
{
Q_OBJECT
public:
/**
* Constructs a KNetworkCookieJar with parent @p parent.
*/
explicit CookieJar(QObject *parent = 0);
/**
* Destroys the KNetworkCookieJar.
*/
~CookieJar();
/**
* Returns the currently set window id. The default value is -1.
*/
WId windowId() const;
/**
* Sets the window id of the application.
*
* This value is used by KDE's cookiejar to manage session cookies, namely
* to delete them when the last application referring to such cookies is
* closed by the end user.
*
* @see QWidget::window()
* @see QWidget::winId()
*
* @param id the value of @ref QWidget::winId() from the window that contains your widget.
*/
void setWindowId(WId id);
/**
* Reparse the KDE cookiejar configuration file.
*/
void reparseConfiguration();
/**
* Reimplemented for internal reasons, the API is not affected.
*
* @see QNetworkCookieJar::cookiesForUrl
* @internal
*/
QList<QNetworkCookie> cookiesForUrl(const QUrl &url) const;
/**
* Reimplemented for internal reasons, the API is not affected.
*
* @see QNetworkCookieJar::setCookiesFromUrl
* @internal
*/
bool setCookiesFromUrl(const QList<QNetworkCookie> &cookieList, const QUrl &url);
/**
* Returns true if persistent caching of cookies is disabled.
*
* @see setDisableCookieStorage
* @since 4.6
*/
bool isCookieStorageDisabled() const;
/**
* Prevent persistent storage of cookies.
*
* Call this function if you do not want cookies to be stored locally for
* later access without disabling the cookiejar. All cookies will be discarded
* once the sessions that are using the cookie are done.
*
* @since 4.6
*/
void setDisableCookieStorage (bool disable);
private:
class CookieJarPrivate;
CookieJarPrivate* const d;
};
}
}
#endif // KIO_ACCESSMANAGER_H

View file

@ -1,483 +0,0 @@
/*
* This file is part of the KDE project.
*
* Copyright (C) 2008 Alex Merry <alex.merry @ kdemail.net>
* Copyright (C) 2008 - 2009 Urs Wolfer <uwolfer @ kde.org>
* Copyright (C) 2009 - 2012 Dawit Alemayehu <adawit @ kde.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
#include "accessmanagerreply_p.h"
#include "accessmanager.h"
#include "job.h"
#include "scheduler.h"
#include <kdebug.h>
#include <kauthorized.h>
#include <kprotocolinfo.h>
#include <kmimetype.h>
#include <QtNetwork/QSslConfiguration>
namespace KDEPrivate {
AccessManagerReply::AccessManagerReply(const QNetworkAccessManager::Operation op,
const QNetworkRequest &request,
KIO::SimpleJob *kioJob,
bool emitReadyReadOnMetaDataChange,
QObject *parent)
:QNetworkReply(parent),
m_metaDataRead(false),
m_ignoreContentDisposition(false),
m_emitReadyReadOnMetaDataChange(emitReadyReadOnMetaDataChange),
m_kioJob(kioJob)
{
setRequest(request);
setOpenMode(QIODevice::ReadOnly);
setUrl(request.url());
setOperation(op);
setError(NoError, QString());
if (!request.sslConfiguration().isNull())
setSslConfiguration(request.sslConfiguration());
connect(kioJob, SIGNAL(redirection(KIO::Job*,KUrl)), SLOT(slotRedirection(KIO::Job*,KUrl)));
connect(kioJob, SIGNAL(percent(KJob*,ulong)), SLOT(slotPercent(KJob*,ulong)));
if (qobject_cast<KIO::StatJob*>(kioJob)) {
connect(kioJob, SIGNAL(result(KJob*)), SLOT(slotStatResult(KJob*)));
} else {
connect(kioJob, SIGNAL(result(KJob*)), SLOT(slotResult(KJob*)));
connect(kioJob, SIGNAL(data(KIO::Job*,QByteArray)),
SLOT(slotData(KIO::Job*,QByteArray)));
connect(kioJob, SIGNAL(mimetype(KIO::Job*,QString)),
SLOT(slotMimeType(KIO::Job*,QString)));
}
}
AccessManagerReply::AccessManagerReply (const QNetworkAccessManager::Operation op,
const QNetworkRequest& request,
const QByteArray& data,
const QUrl& url,
const KIO::MetaData& metaData,
QObject* parent)
:QNetworkReply (parent),
m_data(data),
m_ignoreContentDisposition(false),
m_emitReadyReadOnMetaDataChange(false)
{
setRequest(request);
setOpenMode(QIODevice::ReadOnly);
setUrl((url.isValid() ? url : request.url()));
setOperation(op);
setHeaderFromMetaData(metaData);
if (!request.sslConfiguration().isNull())
setSslConfiguration(request.sslConfiguration());
setError(NoError, QString());
emitFinished(true, Qt::QueuedConnection);
}
AccessManagerReply::AccessManagerReply (const QNetworkAccessManager::Operation op,
const QNetworkRequest& request,
QNetworkReply::NetworkError errorCode,
const QString& errorMessage,
QObject* parent)
:QNetworkReply (parent)
{
setRequest(request);
setOpenMode(QIODevice::ReadOnly);
setUrl(request.url());
setOperation(op);
setError(static_cast<QNetworkReply::NetworkError>(errorCode), errorMessage);
if (error() != QNetworkReply::NoError) {
QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection, Q_ARG(QNetworkReply::NetworkError, error()));
}
emitFinished(true, Qt::QueuedConnection);
}
AccessManagerReply::~AccessManagerReply()
{
}
void AccessManagerReply::abort()
{
if (m_kioJob)
m_kioJob.data()->disconnect(this);
m_kioJob.clear();
m_data.clear();
m_metaDataRead = false;
}
qint64 AccessManagerReply::bytesAvailable() const
{
return (QNetworkReply::bytesAvailable() + m_data.length());
}
qint64 AccessManagerReply::readData(char *data, qint64 maxSize)
{
const qint64 length = qMin(qint64(m_data.length()), maxSize);
if (length) {
::memcpy(data, m_data.constData(), length);
m_data.remove(0, length);
}
return length;
}
bool AccessManagerReply::ignoreContentDisposition (const KIO::MetaData& metaData)
{
if (m_ignoreContentDisposition) {
return true;
}
if (!metaData.contains(QLatin1String("content-disposition-type"))) {
return true;
}
bool ok = false;
const int statusCode = attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(&ok);
if (!ok || statusCode < 200 || statusCode > 299) {
return true;
}
return false;
}
void AccessManagerReply::setHeaderFromMetaData (const KIO::MetaData& _metaData)
{
if (_metaData.isEmpty()) {
return;
}
KIO::MetaData metaData(_metaData);
// Set the encryption attribute and values...
QSslConfiguration sslConfig;
const bool isEncrypted = KIO::Integration::sslConfigFromMetaData(metaData, sslConfig);
setAttribute(QNetworkRequest::ConnectionEncryptedAttribute, isEncrypted);
if (isEncrypted)
setSslConfiguration(sslConfig);
// Set the raw header information...
const QStringList httpHeaders (metaData.value(QLatin1String("HTTP-Headers")).split(QLatin1Char('\n'), QString::SkipEmptyParts));
if (httpHeaders.isEmpty()) {
if (metaData.contains(QLatin1String("charset"))) {
QString mimeType = header(QNetworkRequest::ContentTypeHeader).toString();
mimeType += QLatin1String(" ; charset=");
mimeType += metaData.value(QLatin1String("charset"));
// kDebug(7044) << "changed content-type to" << mimeType;
setHeader(QNetworkRequest::ContentTypeHeader, mimeType.toUtf8());
}
} else {
Q_FOREACH(const QString& httpHeader, httpHeaders) {
int index = httpHeader.indexOf(QLatin1Char(':'));
// Handle HTTP status line...
if (index == -1) {
// Except for the status line, all HTTP header must be an nvpair of
// type "<name>:<value>"
if (!httpHeader.startsWith(QLatin1String("HTTP/"), Qt::CaseInsensitive)) {
continue;
}
QStringList statusLineAttrs (httpHeader.split(QLatin1Char(' '), QString::SkipEmptyParts));
if (statusLineAttrs.count() > 1) {
setAttribute(QNetworkRequest::HttpStatusCodeAttribute, statusLineAttrs.at(1));
}
if (statusLineAttrs.count() > 2) {
setAttribute(QNetworkRequest::HttpReasonPhraseAttribute, statusLineAttrs.at(2));
}
continue;
}
const QString headerName = httpHeader.left(index);
QString headerValue = httpHeader.mid(index+1);
// Ignore cookie header since it is handled by the http ioslave.
if (headerName.startsWith(QLatin1String("set-cookie"), Qt::CaseInsensitive)) {
continue;
}
if (headerName.startsWith(QLatin1String("content-disposition"), Qt::CaseInsensitive) &&
ignoreContentDisposition(metaData)) {
continue;
}
// Without overridding the corrected mime-type sent by kio_http, add
// back the "charset=" portion of the content-type header if present.
if (headerName.startsWith(QLatin1String("content-type"), Qt::CaseInsensitive)) {
QString mimeType (header(QNetworkRequest::ContentTypeHeader).toString());
if (m_ignoreContentDisposition) {
// If the server returned application/octet-stream, try to determine the
// real content type from the disposition filename.
if (mimeType == KMimeType::defaultMimeType()) {
int accuracy = 0;
const QString fileName (metaData.value(QLatin1String("content-disposition-filename")));
KMimeType::Ptr mime = KMimeType::findByUrl((fileName.isEmpty() ? url().path() : fileName), 0, false, true, &accuracy);
if (!mime->isDefault() && accuracy == 100) {
mimeType = mime->name();
}
}
metaData.remove(QLatin1String("content-disposition-type"));
metaData.remove(QLatin1String("content-disposition-filename"));
}
if (!headerValue.contains(mimeType, Qt::CaseInsensitive)) {
index = headerValue.indexOf(QLatin1Char(';'));
if (index == -1) {
headerValue = mimeType;
} else {
headerValue.replace(0, index, mimeType);
}
//kDebug(7044) << "Changed mime-type from" << mimeType << "to" << headerValue;
}
}
setRawHeader(headerName.trimmed().toUtf8(), headerValue.trimmed().toUtf8());
}
}
// Set the returned meta data as attribute...
setAttribute(static_cast<QNetworkRequest::Attribute>(KIO::AccessManager::MetaData), metaData.toVariant());
}
void AccessManagerReply::setIgnoreContentDisposition(bool on)
{
// kDebug(7044) << on;
m_ignoreContentDisposition = on;
}
void AccessManagerReply::putOnHold()
{
if (!m_kioJob || isFinished())
return;
// kDebug(7044) << m_kioJob << m_data;
m_kioJob.data()->disconnect(this);
m_kioJob.data()->putOnHold();
m_kioJob.clear();
KIO::Scheduler::publishSlaveOnHold();
}
bool AccessManagerReply::isLocalRequest (const KUrl& url)
{
const QString scheme (url.protocol());
return (KProtocolInfo::isKnownProtocol(scheme) &&
KProtocolInfo::protocolClass(scheme).compare(QLatin1String(":local"), Qt::CaseInsensitive) == 0);
}
void AccessManagerReply::readHttpResponseHeaders(KIO::Job *job)
{
if (!job || m_metaDataRead)
return;
KIO::MetaData metaData (job->metaData());
if (metaData.isEmpty()) {
// Allow handling of local resources such as man pages and file url...
if (isLocalRequest(url())) {
setHeader(QNetworkRequest::ContentLengthHeader, job->totalAmount(KJob::Bytes));
setAttribute(QNetworkRequest::HttpStatusCodeAttribute, "200");
emit metaDataChanged();
}
return;
}
setHeaderFromMetaData(metaData);
m_metaDataRead = true;
emit metaDataChanged();
}
int AccessManagerReply::jobError(KJob* kJob)
{
const int errCode = kJob->error();
switch (errCode)
{
case 0:
break; // No error;
case KIO::ERR_SLAVE_DEFINED:
case KIO::ERR_NO_CONTENT: // Sent by a 204 response is not an error condition.
setError(QNetworkReply::NoError, kJob->errorText());
//kDebug(7044) << "0 -> QNetworkReply::NoError";
break;
case KIO::ERR_IS_DIRECTORY:
// This error condition can happen if you click on an ftp link that points
// to a directory instead of a file, e.g. ftp://ftp.kde.org/pub
setHeader(QNetworkRequest::ContentTypeHeader, "inode/directory");
setError(QNetworkReply::NoError, kJob->errorText());
break;
case KIO::ERR_COULD_NOT_CONNECT:
setError(QNetworkReply::ConnectionRefusedError, kJob->errorText());
kDebug(7044) << "KIO::ERR_COULD_NOT_CONNECT -> QNetworkReply::ConnectionRefusedError";
break;
case KIO::ERR_UNKNOWN_HOST:
setError(QNetworkReply::HostNotFoundError, kJob->errorText());
kDebug(7044) << "KIO::ERR_UNKNOWN_HOST -> QNetworkReply::HostNotFoundError";
break;
case KIO::ERR_SERVER_TIMEOUT:
setError(QNetworkReply::TimeoutError, kJob->errorText());
kDebug(7044) << "KIO::ERR_SERVER_TIMEOUT -> QNetworkReply::TimeoutError";
break;
case KIO::ERR_USER_CANCELED:
case KIO::ERR_ABORTED:
setError(QNetworkReply::OperationCanceledError, kJob->errorText());
kDebug(7044) << "KIO::ERR_ABORTED -> QNetworkReply::OperationCanceledError";
break;
case KIO::ERR_UNKNOWN_PROXY_HOST:
setError(QNetworkReply::ProxyNotFoundError, kJob->errorText());
kDebug(7044) << "KIO::UNKNOWN_PROXY_HOST -> QNetworkReply::ProxyNotFoundError";
break;
case KIO::ERR_ACCESS_DENIED:
setError(QNetworkReply::ContentAccessDenied, kJob->errorText());
kDebug(7044) << "KIO::ERR_ACCESS_DENIED -> QNetworkReply::ContentAccessDenied";
break;
case KIO::ERR_WRITE_ACCESS_DENIED:
setError(QNetworkReply::ContentOperationNotPermittedError, kJob->errorText());
kDebug(7044) << "KIO::ERR_WRITE_ACCESS_DENIED -> QNetworkReply::ContentOperationNotPermittedError";
break;
case KIO::ERR_DOES_NOT_EXIST:
setError(QNetworkReply::ContentNotFoundError, kJob->errorText());
kDebug(7044) << "KIO::ERR_DOES_NOT_EXIST -> QNetworkReply::ContentNotFoundError";
break;
case KIO::ERR_COULD_NOT_AUTHENTICATE:
setError(QNetworkReply::AuthenticationRequiredError, kJob->errorText());
kDebug(7044) << "KIO::ERR_COULD_NOT_AUTHENTICATE -> QNetworkReply::AuthenticationRequiredError";
break;
case KIO::ERR_UNSUPPORTED_PROTOCOL:
case KIO::ERR_NO_SOURCE_PROTOCOL:
setError(QNetworkReply::ProtocolUnknownError, kJob->errorText());
kDebug(7044) << "KIO::ERR_UNSUPPORTED_PROTOCOL -> QNetworkReply::ProtocolUnknownError";
break;
case KIO::ERR_CONNECTION_BROKEN:
setError(QNetworkReply::RemoteHostClosedError, kJob->errorText());
kDebug(7044) << "KIO::ERR_CONNECTION_BROKEN -> QNetworkReply::RemoteHostClosedError";
break;
case KIO::ERR_UNSUPPORTED_ACTION:
setError(QNetworkReply::ProtocolInvalidOperationError, kJob->errorText());
kDebug(7044) << "KIO::ERR_UNSUPPORTED_ACTION -> QNetworkReply::ProtocolInvalidOperationError";
break;
default:
setError(QNetworkReply::UnknownNetworkError, kJob->errorText());
kDebug(7044) << KIO::rawErrorDetail(errCode, QString()) << "-> QNetworkReply::UnknownNetworkError";
}
return errCode;
}
void AccessManagerReply::slotData(KIO::Job *kioJob, const QByteArray &data)
{
Q_UNUSED (kioJob);
m_data += data;
if (!data.isEmpty())
emit readyRead();
}
void AccessManagerReply::slotMimeType(KIO::Job *kioJob, const QString &mimeType)
{
//kDebug(7044) << kioJob << mimeType;
setHeader(QNetworkRequest::ContentTypeHeader, mimeType.toUtf8());
readHttpResponseHeaders(kioJob);
if (m_emitReadyReadOnMetaDataChange) {
emit readyRead();
}
}
void AccessManagerReply::slotResult(KJob *kJob)
{
const int errcode = jobError(kJob);
const QUrl redirectUrl = attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl();
if (!redirectUrl.isValid()) {
setAttribute(static_cast<QNetworkRequest::Attribute>(KIO::AccessManager::KioError), errcode);
if (errcode && errcode != KIO::ERR_NO_CONTENT)
emit error(error());
}
// Make sure HTTP response headers are always set.
if (!m_metaDataRead) {
readHttpResponseHeaders(qobject_cast<KIO::Job*>(kJob));
}
emitFinished(true);
}
void AccessManagerReply::slotStatResult(KJob* kJob)
{
if (jobError(kJob)) {
emit error (error());
emitFinished(true);
return;
}
KIO::StatJob* statJob = qobject_cast<KIO::StatJob*>(kJob);
Q_ASSERT(statJob);
KIO::UDSEntry entry = statJob->statResult();
QString mimeType = entry.stringValue(KIO::UDSEntry::UDS_MIME_TYPE);
if (mimeType.isEmpty() && entry.isDir())
mimeType = QLatin1String("inode/directory");
if (!mimeType.isEmpty())
setHeader(QNetworkRequest::ContentTypeHeader, mimeType.toUtf8());
emitFinished(true);
}
void AccessManagerReply::slotRedirection(KIO::Job* job, const KUrl& u)
{
if (!KAuthorized::authorizeUrlAction(QLatin1String("redirect"), url(), u)) {
kWarning(7007) << "Redirection from" << url() << "to" << u << "REJECTED by policy!";
setError(QNetworkReply::ContentAccessDenied, u.url());
emit error(error());
return;
}
setAttribute(QNetworkRequest::RedirectionTargetAttribute, QUrl(u));
if (job->queryMetaData(QLatin1String("redirect-to-get")) == QLatin1String("true")) {
setOperation(QNetworkAccessManager::GetOperation);
}
}
void AccessManagerReply::slotPercent(KJob *job, unsigned long percent)
{
qulonglong bytesTotal = job->totalAmount(KJob::Bytes);
qulonglong bytesProcessed = bytesTotal * (percent / 100);
if (operation() == QNetworkAccessManager::PutOperation ||
operation() == QNetworkAccessManager::PostOperation) {
emit uploadProgress(bytesProcessed, bytesTotal);
return;
}
emit downloadProgress(bytesProcessed, bytesTotal);
}
void AccessManagerReply::emitFinished (bool state, Qt::ConnectionType type)
{
setFinished(state);
emit QMetaObject::invokeMethod(this, "finished", type);
}
}
#include "moc_accessmanagerreply_p.cpp"

View file

@ -1,106 +0,0 @@
/*
* This file is part of the KDE project.
*
* Copyright (C) 2008 - 2009 Urs Wolfer <uwolfer @ kde.org>
* Copyright (C) 2009 - 2012 Dawit Alemayehu <adawit @ kde.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
#ifndef KIO_ACCESSMANAGERREPLY_P_H
#define KIO_ACCESSMANAGERREPLY_P_H
#include <QtCore/qsharedpointer.h>
#include <QtNetwork/QNetworkReply>
namespace KIO
{
class Job;
class SimpleJob;
class MetaData;
}
class KJob;
class KUrl;
namespace KDEPrivate {
/**
* Used for KIO::AccessManager; KDE implementation of QNetworkReply.
*
* @since 4.3
* @author Urs Wolfer \<uwolfer @ kde.org\>
*/
class AccessManagerReply : public QNetworkReply
{
Q_OBJECT
public:
explicit AccessManagerReply(const QNetworkAccessManager::Operation op,
const QNetworkRequest &request,
KIO::SimpleJob *kioJob,
bool emitReadyReadOnMetaDataChange = false,
QObject *parent = 0);
explicit AccessManagerReply(const QNetworkAccessManager::Operation op,
const QNetworkRequest &request,
const QByteArray& data,
const QUrl& url,
const KIO::MetaData& metaData,
QObject *parent = 0);
explicit AccessManagerReply(const QNetworkAccessManager::Operation op,
const QNetworkRequest &request,
QNetworkReply::NetworkError errorCode,
const QString& errorMessage,
QObject *parent = 0);
virtual ~AccessManagerReply();
virtual qint64 bytesAvailable() const;
virtual void abort();
void setIgnoreContentDisposition(bool on);
void putOnHold();
static bool isLocalRequest(const KUrl& url);
protected:
virtual qint64 readData(char *data, qint64 maxSize);
bool ignoreContentDisposition(const KIO::MetaData&);
void setHeaderFromMetaData(const KIO::MetaData&);
void readHttpResponseHeaders(KIO::Job *);
int jobError(KJob *kJob);
void emitFinished(bool state, Qt::ConnectionType type = Qt::AutoConnection);
private Q_SLOTS:
void slotData(KIO::Job *kioJob, const QByteArray &data);
void slotMimeType(KIO::Job *kioJob, const QString &mimeType);
void slotResult(KJob *kJob);
void slotStatResult(KJob *kJob);
void slotRedirection(KIO::Job *job, const KUrl &url);
void slotPercent(KJob *job, unsigned long percent);
private:
QByteArray m_data;
bool m_metaDataRead;
bool m_ignoreContentDisposition;
bool m_emitReadyReadOnMetaDataChange;
QWeakPointer<KIO::SimpleJob> m_kioJob;
};
}
#endif // KIO_ACCESSMANAGERREPLY_P_H

View file

@ -42,12 +42,6 @@
extern bool kio_resolve_local_urls; // from copyjob.cpp, abused here to save a symbol.
static bool isHttpProtocol(const QString& protocol)
{
return (protocol.startsWith(QLatin1String("webdav"), Qt::CaseInsensitive) ||
protocol.startsWith(QLatin1String("http"), Qt::CaseInsensitive));
}
namespace KIO
{
enum DeleteJobState {
@ -292,10 +286,7 @@ void DeleteJobPrivate::deleteNextFile()
} else
{ // if remote - or if unlink() failed (we'll use the job's error handling in that case)
//kDebug(7007) << "calling file_delete on" << *it;
if (isHttpProtocol(it->protocol()))
job = KIO::http_delete( *it, KIO::HideProgressInfo );
else
job = KIO::file_delete( *it, KIO::HideProgressInfo );
job = KIO::file_delete( *it, KIO::HideProgressInfo );
Scheduler::setJobPriority(job, 1);
m_currentURL=(*it);
}
@ -332,15 +323,8 @@ void DeleteJobPrivate::deleteNextDir()
} else {
// Call rmdir - works for kioslaves with canDeleteRecursive too,
// CMD_DEL will trigger the recursive deletion in the slave.
SimpleJob* job;
if (isHttpProtocol(it->protocol())) {
KUrl url (*it);
url.adjustPath(KUrl::AddTrailingSlash);
job = KIO::http_delete(url, KIO::HideProgressInfo);
} else {
job = KIO::rmdir(*it);
job->addMetaData(QString::fromLatin1("recurse"), "true");
}
SimpleJob* job = KIO::rmdir(*it);
job->addMetaData(QString::fromLatin1("recurse"), "true");
Scheduler::setJobPriority(job, 1);
dirs.erase(it);
q->addSubjob( job );

View file

@ -1381,277 +1381,6 @@ public:
}
};
namespace KIO {
class PostErrorJob : public StoredTransferJob
{
public:
PostErrorJob(int _error, const QString& url, const QByteArray &packedArgs, const QByteArray &postData)
: StoredTransferJob(*new StoredTransferJobPrivate(KUrl(), CMD_SPECIAL, packedArgs, postData))
{
setError( _error );
setErrorText( url );
}
PostErrorJob(int _error, const QString& url, const QByteArray &packedArgs, QIODevice* ioDevice)
: StoredTransferJob(*new StoredTransferJobPrivate(KUrl(), CMD_SPECIAL, packedArgs, ioDevice))
{
setError( _error );
setErrorText( url );
}
};
}
static int isUrlPortBad(const KUrl& url)
{
int _error = 0;
// filter out some malicious ports
static const int bad_ports[] = {
1, // tcpmux
7, // echo
9, // discard
11, // systat
13, // daytime
15, // netstat
17, // qotd
19, // chargen
20, // ftp-data
21, // ftp-cntl
22, // ssh
23, // telnet
25, // smtp
37, // time
42, // name
43, // nicname
53, // domain
77, // priv-rjs
79, // finger
87, // ttylink
95, // supdup
101, // hostriame
102, // iso-tsap
103, // gppitnp
104, // acr-nema
109, // pop2
110, // pop3
111, // sunrpc
113, // auth
115, // sftp
117, // uucp-path
119, // nntp
123, // NTP
135, // loc-srv / epmap
139, // netbios
143, // imap2
179, // BGP
389, // ldap
512, // print / exec
513, // login
514, // shell
515, // printer
526, // tempo
530, // courier
531, // Chat
532, // netnews
540, // uucp
556, // remotefs
587, // sendmail
601, //
989, // ftps data
990, // ftps
992, // telnets
993, // imap/SSL
995, // pop3/SSL
1080, // SOCKS
2049, // nfs
4045, // lockd
6000, // x11
6667, // irc
0};
if (url.port() != 80)
{
const int port = url.port();
for (int cnt=0; bad_ports[cnt] && bad_ports[cnt] <= port; ++cnt)
if (port == bad_ports[cnt])
{
_error = KIO::ERR_POST_DENIED;
break;
}
}
if ( _error )
{
static bool override_loaded = false;
static QList< int >* overriden_ports = NULL;
if( !override_loaded ) {
KConfig cfg( "kio_httprc" );
overriden_ports = new QList< int >;
*overriden_ports = cfg.group(QString()).readEntry( "OverriddenPorts", QList<int>() );
override_loaded = true;
}
for( QList< int >::ConstIterator it = overriden_ports->constBegin();
it != overriden_ports->constEnd();
++it ) {
if( overriden_ports->contains( url.port())) {
_error = 0;
}
}
}
// filter out non https? protocols
if ((url.protocol() != "http") && (url.protocol() != "https" ))
_error = KIO::ERR_POST_DENIED;
if (!_error && !KAuthorized::authorizeUrlAction("open", KUrl(), url))
_error = KIO::ERR_ACCESS_DENIED;
return _error;
}
static KIO::PostErrorJob* precheckHttpPost( const KUrl& url, QIODevice* ioDevice, JobFlags flags )
{
// if request is not valid, return an invalid transfer job
const int _error = isUrlPortBad(url);
if (_error)
{
KIO_ARGS << (int)1 << url;
PostErrorJob * job = new PostErrorJob(_error, url.pathOrUrl(), packedArgs, ioDevice);
job->setUiDelegate(new JobUiDelegate());
if (!(flags & HideProgressInfo)) {
KIO::getJobTracker()->registerJob(job);
}
return job;
}
// all is ok, return 0
return 0;
}
static KIO::PostErrorJob* precheckHttpPost( const KUrl& url, const QByteArray& postData, JobFlags flags )
{
// if request is not valid, return an invalid transfer job
const int _error = isUrlPortBad(url);
if (_error)
{
KIO_ARGS << (int)1 << url;
PostErrorJob * job = new PostErrorJob(_error, url.pathOrUrl(), packedArgs, postData);
job->setUiDelegate(new JobUiDelegate());
if (!(flags & HideProgressInfo)) {
KIO::getJobTracker()->registerJob(job);
}
return job;
}
// all is ok, return 0
return 0;
}
TransferJob *KIO::http_post( const KUrl& url, const QByteArray &postData, JobFlags flags )
{
QBuffer* device = new QBuffer;
device->setData(postData);
device->open(QIODevice::ReadOnly);
TransferJob* job = http_post(url, device, device->size(), flags);
device->setParent(job);
return job;
}
TransferJob *KIO::http_post( const KUrl& url, QIODevice* ioDevice, qint64 size, JobFlags flags )
{
bool redirection = false;
KUrl _url(url);
if (_url.path().isEmpty())
{
redirection = true;
_url.setPath("/");
}
TransferJob* job = precheckHttpPost(_url, ioDevice, flags);
if (job)
return job;
// If no size is specified and the QIODevice is not a sequential one,
// attempt to obtain the size information from it.
Q_ASSERT(ioDevice);
if (size < 0)
size = ((ioDevice && !ioDevice->isSequential()) ? ioDevice->size() : -1);
// Send http post command (1), decoded path and encoded query
KIO_ARGS << (int)1 << _url << size;
job = TransferJobPrivate::newJob(_url, CMD_SPECIAL, packedArgs, ioDevice, flags);
if (redirection)
QTimer::singleShot(0, job, SLOT(slotPostRedirection()));
return job;
}
TransferJob* KIO::http_delete(const KUrl& url, JobFlags flags)
{
// Send decoded path and encoded query
KIO_ARGS << url;
TransferJob * job = TransferJobPrivate::newJob(url, CMD_DEL, packedArgs,
QByteArray(), flags);
return job;
}
StoredTransferJob *KIO::storedHttpPost( const QByteArray& postData, const KUrl& url, JobFlags flags )
{
KUrl _url(url);
if (_url.path().isEmpty())
{
_url.setPath("/");
}
StoredTransferJob* job = precheckHttpPost(_url, postData, flags);
if (job)
return job;
// Send http post command (1), decoded path and encoded query
KIO_ARGS << (int)1 << _url << static_cast<qint64>(postData.size());
job = StoredTransferJobPrivate::newJob(_url, CMD_SPECIAL, packedArgs, postData, flags );
return job;
}
StoredTransferJob *KIO::storedHttpPost( QIODevice* ioDevice, const KUrl& url, qint64 size, JobFlags flags )
{
KUrl _url(url);
if (_url.path().isEmpty())
{
_url.setPath("/");
}
StoredTransferJob* job = precheckHttpPost(_url, ioDevice, flags);
if (job)
return job;
// If no size is specified and the QIODevice is not a sequential one,
// attempt to obtain the size information from it.
Q_ASSERT(ioDevice);
if (size < 0)
size = ((ioDevice && !ioDevice->isSequential()) ? ioDevice->size() : -1);
// Send http post command (1), decoded path and encoded query
KIO_ARGS << (int)1 << _url << size;
job = StoredTransferJobPrivate::newJob(_url, CMD_SPECIAL, packedArgs, ioDevice, flags );
return job;
}
// http post got redirected from http://host to http://host/ by TransferJob
// We must do this redirection ourselves because redirections by the
// slave change post jobs into get jobs.
void TransferJobPrivate::slotPostRedirection()
{
Q_Q(TransferJob);
kDebug(7007) << "TransferJob::slotPostRedirection(" << m_url << ")";
// Tell the user about the new url.
emit q->redirection(q, m_url);
}
TransferJob *KIO::put( const KUrl& url, int permissions, JobFlags flags )
{
KIO_ARGS << url << qint8( (flags & Overwrite) ? 1 : 0 ) << qint8( (flags & Resume) ? 1 : 0 ) << permissions;

View file

@ -114,7 +114,7 @@ namespace KIO {
/**
* Execute any command that is specific to one slave (protocol).
*
* Examples are : HTTP POST, mount and unmount (kio_file)
* Examples are : mount and unmount (kio_file)
*
* @param url The URL isn't passed to the slave, but is used to know
* which slave to send it to :-)
@ -247,76 +247,6 @@ namespace KIO {
KIO_EXPORT TransferJob *put( const KUrl& url, int permissions,
JobFlags flags = DefaultFlags );
/**
* HTTP POST (for form data).
*
* Example:
* \code
* job = KIO::http_post( url, postData, KIO::HideProgressInfo);
* job->addMetaData("content-type", contentType);
* job->addMetaData("referrer", referrerURL);
* \endcode
*
* You MUST specify the "content-type" meta data. It is mandatory.
* It can be preceeded with the optional text "Content-Type:", e.g.
* "Content-Type: application/x-www-form-urlencoded".
*
* @p postData is usually an encoded ASCII string (without null-termination!)
* and can contain spaces, linefeeds and percent escaped characters such as %20,
* %0A and %25.
*
* @param url Where to write the data.
* @param postData Encoded data to post.
* @param flags Can be HideProgressInfo here
* @return the job handling the operation.
*/
KIO_EXPORT TransferJob *http_post( const KUrl& url, const QByteArray &postData,
JobFlags flags = DefaultFlags );
/**
* HTTP POST.
*
* This function, unlike the one that accepts a QByteArray, accepts an IO device
* from which to read the encoded data to be posted to the server in order to
* to avoid holding the content of very large post requests, e.g. multimedia file
* uploads, in memory.
*
* Example:
* \code
* job = KIO::http_post( url, device, device->size(), KIO::HideProgressInfo);
* job->addMetaData("content-type", contentType);
* job->addMetaData("referrer", referrerURL);
* \endcode
*
* You MUST specify the "content-type" meta data. It is mandatory.
* It can be preceeded with the optional text "Content-Type:", e.g.
* "Content-Type: application/x-www-form-urlencoded".
*
* @param url Where to write the data.
* @param device the QIODevice that provides the encoded post data.
* @param size Size of the encoded post data.
* @param flags Can be HideProgressInfo here
* @return the job handling the operation.
*
* @since 4.7
*/
KIO_EXPORT TransferJob *http_post( const KUrl& url, QIODevice* device,
qint64 size = -1, JobFlags flags = DefaultFlags );
/**
* HTTP DELETE.
*
* Though this function servers the same purpose as KIO::file_delete, unlike
* file_delete it accomodates HTTP sepecific actions such as redirections.
*
* @param src url resource to delete.
* @param flags Can be HideProgressInfo here
* @return the job handling the operation.
*
* @since 4.7.3
*/
KIO_EXPORT TransferJob *http_delete( const KUrl& url, JobFlags flags = DefaultFlags );
/**
* Get (a.k.a. read), into a single QByteArray.
* @see StoredTransferJob
@ -342,33 +272,6 @@ namespace KIO {
KIO_EXPORT StoredTransferJob *storedPut( const QByteArray& arr, const KUrl& url, int permissions,
JobFlags flags = DefaultFlags );
/**
* HTTP POST (a.k.a. write) data from a single QByteArray.
* @see StoredTransferJob
*
* @param arr The data to write
* @param url Where to write data.
* @param flags Can be HideProgressInfo here.
* @return the job handling the operation.
* @since 4.2
*/
KIO_EXPORT StoredTransferJob *storedHttpPost( const QByteArray& arr, const KUrl& url,
JobFlags flags = DefaultFlags );
/**
* HTTP POST (a.k.a. write) data from the given IO device.
* @see StoredTransferJob
*
* @param device Device from which the encoded data to be posted is read.
* @param url Where to write data.
* @param size Size of the encoded data to be posted.
* @param flags Can be HideProgressInfo here.
* @return the job handling the operation.
*
* @since 4.7
*/
KIO_EXPORT StoredTransferJob *storedHttpPost( QIODevice* device, const KUrl& url,
qint64 size = -1, JobFlags flags = DefaultFlags );
/**
* Creates a new multiple get job.
*

View file

@ -309,7 +309,6 @@ namespace KIO {
void slotErrorPage();
void slotCanResume( KIO::filesize_t offset );
void slotPostRedirection();
void slotNeedSubUrlData();
void slotSubUrlData(KIO::Job*, const QByteArray &);

View file

@ -666,7 +666,6 @@ namespace KIO {
private:
Q_PRIVATE_SLOT(d_func(), void slotErrorPage())
Q_PRIVATE_SLOT(d_func(), void slotCanResume( KIO::filesize_t offset ))
Q_PRIVATE_SLOT(d_func(), void slotPostRedirection())
Q_PRIVATE_SLOT(d_func(), void slotNeedSubUrlData())
Q_PRIVATE_SLOT(d_func(), void slotSubUrlData(KIO::Job*, const QByteArray &))
Q_PRIVATE_SLOT(d_func(), void slotDataReqFromDevice())

View file

@ -1,69 +1,12 @@
project(kioslave-http)
if(GSSAPI_FOUND)
set(HAVE_LIBGSSAPI 1)
if(GSSAPI_FLAVOR STREQUAL "MIT")
set(GSSAPI_MIT 1)
else(GSSAPI_FLAVOR STREQUAL "MIT")
set(GSSAPI_MIT 0)
endif(GSSAPI_FLAVOR STREQUAL "MIT")
include_directories(${GSSAPI_INCS})
else(GSSAPI_FOUND)
set(HAVE_LIBGSSAPI 0)
set(GSSAPI_MIT 0)
endif(GSSAPI_FOUND)
configure_file(
config-gssapi.h.cmake
${CMAKE_CURRENT_BINARY_DIR}/config-gssapi.h
)
include_directories(
${KDE4_KIO_INCLUDES}
${ZLIB_INCLUDE_DIR}
${CMAKE_SOURCE_DIR}/solid
${CMAKE_BINARY_DIR}/solid
${CMAKE_SOURCE_DIR}/interfaces
${CMAKE_SOURCE_DIR}/kio/httpfilter
)
add_subdirectory(kcookiejar)
if(ENABLE_TESTING)
add_subdirectory(tests)
endif()
include_directories(${KDE4_KIO_INCLUDES})
########### next target ###############
add_executable(kio_http_cache_cleaner http_cache_cleaner.cpp)
kde4_add_plugin(kio_http http.cpp)
target_link_libraries(kio_http_cache_cleaner
${KDE4_KIO_LIBS}
${ZLIB_LIBRARY}
)
install(TARGETS kio_http_cache_cleaner DESTINATION ${KDE4_LIBEXEC_INSTALL_DIR})
########### next target ###############
set(kio_http_PART_SRCS
http.cpp
httpauthentication.cpp
${httpfilter_STAT_SRCS}
${CMAKE_SOURCE_DIR}/kio/httpfilter/httpfilter.cc
)
kde4_add_plugin(kio_http ${kio_http_PART_SRCS})
target_link_libraries(kio_http
${KDE4_KIO_LIBS}
${KDE4_SOLID_LIBS}
kntlm
${ZLIB_LIBRARY}
)
if(GSSAPI_FOUND)
target_link_libraries(kio_http ${GSSAPI_LIBS})
endif()
target_link_libraries(kio_http ${KDE4_KDECORE_LIBS} ${KDE4_KIO_LIBS})
install(TARGETS kio_http DESTINATION ${KDE4_PLUGIN_INSTALL_DIR})
@ -71,10 +14,7 @@ install(TARGETS kio_http DESTINATION ${KDE4_PLUGIN_INSTALL_DIR})
install(
FILES
http_cache_cleaner.desktop
http.protocol
https.protocol
webdav.protocol
webdavs.protocol
http.protocol https.protocol
webdav.protocol webdavs.protocol
DESTINATION ${KDE4_SERVICES_INSTALL_DIR}
)

View file

@ -1,20 +0,0 @@
khttpcache README
=================
khttpcache checks the HTTP Cache of a user
and throws out expired entries.
TODO:
* Skip entries which end in .new and are younger than
30 minutes / delte entries which end in .new and are
older than 30 minutes.
* Let kio_http fill in expire dates other than 0.
DONE:
* Start khttpcache from kio_http if the file "cleaned"
is older than 30(?) minutes.
* Accept command line parameteres

View file

@ -1,184 +0,0 @@
This document describes how to add support for extended webdav features (locking,
properties etc.) to your webdav-aware application.
Author: Hamish Rodda, rodda@kde.org
Version: 0.3
Compatable with (tested on):
Apache + mod_dav version 1 and 2
Zope
Silverstream webdav server
Applications supporting extended webdav features
(include name and contact email, in case the interface has to change):
[none currently]
Much of the info here is elaborated by rfc #2518; the rest can be understood by reading
davPropStat() in http.cc, specifically the setMetaData() calls.
Extended information is transferred via kio's metadata system...
=== MISCELLANEOUS ===
Display Names (names suitable for presentation to the user) are passed as the metadata
element davDisplayName.
Source template locations (href, usually an absolute URL w/o host info)
are passed as element davSource.
Content languages are passed as element davContentLanguage.
Extra webdav headers are passed as metadata element davHeader
For doing a webdav SEARCH, use listDir() and set the metadata element
davSearchQuery to the search query. The root element of this query should be like
<d:basicsearch> or <d:sql>.
For doing a generic webdav action, call a special request, with
the following data:
int, value 7 (WEBDAV generic)
KUrl url
int method - the HTTP/WEBDAV method to call
Send the xml request and receive the xml response in the usual way.
=== CREATING A LOCK ===
To create a lock, call a special request, with the following data:
int, value 5 (LOCK request)
KUrl url - the location of the resource to lock
QString scope - the scope of the lock, currently "exclusive" or "shared"
QString type - the type of the lock, currently only "write"
QString owner (optional) - owner contact details (url)
Additionally, the lock timeout requested from the server may be altered from the default
of Infinity by setting the metadata "davTimeout" to the number of seconds, or 0 for
infinity.
=== REMOVING A LOCK ===
To remove a lock, call a special request, with the following data:
int, value 5 (LOCK request)
KUrl url - the location of the resource to unlock
metadata required:
davLockToken - the lock token to remove
and, of course, any other lock information as below required for the operation
to suceed.
=== SETTING LOCK INFORMATION ===
To provide lock data so that urls can be accessed, you need to pass the following metadata:
davLockCount: (uint) the number of locks you are providing
davLockToken%1: (string) the token
(optional) davLockURL%1: (string) the absolute URL specified by the lock token
(optional) davLockNot%1: (value ignored) the presence of this meta key negates the lock
(ie. requires the lock to not be set)
Example data:
=============
davLockCount: 2
davLockToken1: opaquelocktoken:f81de2ad-7f3d-a1b2-4f3c-00a0c91a9d76A
davLockNot1: (value ignored)
davLockToken2: opaquelocktoken:f81de2ad-7f3d-a1b2-4f3c-00a0c91a9d76B
davLockURL2: http://www.foo.bar/container2/
=== RECEIVING LOCK INFORMATION ===
For each file, stat/listdir always returns two pieces of information:
davSupportedLockCount: (uint) the number of lock types discovered for this resource.
davLockCount: (uint) the number of locks discovered on this resource.
for each count, additional information is returned:
===================
Information about the locks on a resource:
davLockCount: %1 (the number of locks to be described, as below)
*** Required items ***
davLockScope%1 - The scope of this lock. May be exclusive, shared, or a custom type.
davLockType%1 - The type of the lock.
davLockDepth%1 - The depth to which this lock applies
(0=only this resource, 1=this collection, infinity=applies recursively)
*** Optional items ***
davLockOwner%1 - The owner of this lock.
davLockTimeout%1 - The timeout parameter. Possibilities: see section 9.8, rfc #2518
davLockToken%1 - The token which iden
===================
Information about the lock types supported by the resource
davSupportedLockCount: %1 (the number of locks types to be described, as below)
davSupportedLockScope%1 - The scope of the lock (exclusive, shared, other custom type)
davSupportedLockType%1 - The type of the lock (webdav 1.0 supports only the "write" type)
===================
Example Metadata which would be supplied if the response was the example XML below:
davSupportedLockCount: 2
davLockCount: 2
davLockScope1: exclusive
davLockType1: write
davLockDepth1: 0
davLockOwner1: Jane Smith
davLockTimeout1: infinite
davLockToken1: opaquelocktoken:f81de2ad-7f3d-a1b2-4f3c-00a0c91a9d76A
davLockScope2: shared
davLockType2: write
davLockDepth2: 1
davLockOwner2: John Doe
davLockToken2: opaquelocktoken:f81de2ad-7f3d-a1b2-4f3c-00a0c91a9d76B
davSupportedLockScope1: exclusive
davSupportedLockType1: write
davSupportedLockScope2: shared
davSupportedLockType2: write
(example XML:)
<?xml version="1.0" encoding="utf-8" ?>
<D:multistatus xmlns:D='DAV:'>
<D:response>
<D:href>http://www.foo.bar/container/</D:href>
<D:propstat>
<D:prop>
<D:lockdiscovery>
<D:activelock>
<D:locktype><D:write/></D:locktype>
<D:lockscope><D:exclusive/></D:lockscope>
<D:depth>0</D:depth>
<D:owner>Jane Smith</D:owner>
<D:timeout>Infinite</D:timeout>
<D:locktoken>
<D:href>
opaquelocktoken:f81de2ad-7f3d-a1b2-4f3c-00a0c91a9d76A
</D:href>
</D:locktoken>
</D:activelock>
<D:activelock>
<D:locktype><D:write/></D:locktype>
<D:lockscope><D:shared/></D:lockscope>
<D:depth>1</D:depth>
<D:owner>John Doe</D:owner>
<D:locktoken>
<D:href>
opaquelocktoken:f81de2ad-7f3d-a1b2-4f3c-00a0c91a9d76B
</D:href>
</D:locktoken>
</D:activelock>
</D:lockdiscovery>
<D:supportedlock>
<D:lockentry>
<D:lockscope><D:exclusive/></D:lockscope>
<D:locktype><D:write/></D:locktype>
</D:lockentry>
<D:lockentry>
<D:lockscope><D:shared/></D:lockscope>
<D:locktype><D:write/></D:locktype>
</D:lockentry>
</D:supportedlock>
</D:prop>
<D:status>HTTP/1.1 200 OK</D:status>
</D:propstat>
</D:response>
</D:multistatus>

View file

@ -1,7 +0,0 @@
2518: HTTP Extensions for Distributed Authoring -- WEBDAV
2616: Hypertext Transfer Protocol -- HTTP/1.1
2617: HTTP Authentication: Basic and Digest Access Authentication
2817: Upgrading to TLS Within HTTP/1.1
2818: HTTP Over TLS
3229: Delta encoding in HTTP
3253: Versioning Extensions to WebDAV

View file

@ -1,28 +0,0 @@
Here's a few ideas for those with blistered hands and nothing better to
do:
SSL certificate verification:
We do establish SSL connections, but we never actually verify a
certificate!
HTTP/1.1 Persistant Connections:
The header often specifies the timeout value used for connections.
Close the connection ourselves when the timeout has expired. That way
we don't loose time sending stuff to an already closed connection.
Rating(s) support. http://www.w3.org/PICS
This might involve an external program to parse the labels, and something
to configure access.
WebDAV support. MSIE5 calls it web folders support, and a similar
approach would probably be a good idea. Perhaps with an exists()
function.. one could tell if an http url was part of a WebDAV collection..
and this could be used for some kind of integration with kfile... to
provide seamless integration. Uhm, also, this might entail an external
program (xml parser and such).
"Friendly" error messages. How often have you seen a useless 404 message?
Again something I first notied in MSIE5, and that would be some sort of
translation of what an error really means. Yes this would have to be
i18n'd and easily turned off. But this could also be extended to all the
slaves (ftp, pop3, etc, etc).

View file

@ -1,35 +0,0 @@
The following is a list of items that are currently missing or partially implemented
in kio_http:
- HTTP/1.1 Persistant Connections:
The header often specifies the timeout value used for connections.
Close the connection ourselves when the timeout has expired. That way
we don't loose time sending stuff to an already closed connection.
- HTTP/1.1 Pipelining support
This more of an optimization of the http io-slave that is intended to make it
faster while using as few resources as possible. Work on this is currently
being done to add this support for KDE 3.x version.
- WebDAV support:
The majority of the work for this is done, see README.webdav. GUI integration
into konqueror as a konqueror part would be nice, to add GUI support for
features such as locking.
- Rating(s) support. http://www.w3.org/PICS:
This might involve an external program to parse the labels, and something to
configure access accordingly. There is only some basic things that need to be
added to kio_http to support this. The majority of the work has to be done at the
application level. A khtml plugin in kdeaddons to do this might be a nice idea.
- P3P support:
This can also be implemented as a plugin to konqueror and does
not need any speical support in HTTP except perhaps sending a
flag that indicates that the web page provides some P3P information.
This is something that can be added as a plugin to kdeaddons.
Maintainers
Waldo Bastian <bastian@kde.org>
Dawit Alemayehu <adawit@kde.org>
WebDAV support: Hamish Rodda <rodda@kde.org>

View file

@ -1,5 +0,0 @@
/* Define if you have GSSAPI libraries */
#cmakedefine HAVE_LIBGSSAPI 1
/* Define if you have the MIT Kerberos libraries */
#cmakedefine GSSAPI_MIT 1

File diff suppressed because it is too large Load diff

View file

@ -1,602 +1,42 @@
/*
Copyright (C) 2000,2001 Dawit Alemayehu <adawit@kde.org>
Copyright (C) 2000,2001 Waldo Bastian <bastian@kde.org>
Copyright (C) 2000,2001 George Staikos <staikos@kde.org>
Copyright (C) 2001,2002 Hamish Rodda <rodda@kde.org>
Copyright (C) 2007 Daniel Nicoletti <mirttex@users.sourceforge.net>
Copyright (C) 2008,2009 Andreas Hartmetz <ahartmetz@gmail.com>
/* This file is part of the KDE libraries
Copyright (C) 2022 Ivailo Monev <xakepa10@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License version 2, as published by the Free Software Foundation.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef HTTP_H
#define HTTP_H
#ifndef KDELIBS_HTTP_H
#define KDELIBS_HTTP_H
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdio.h>
#include <time.h>
#include <QtCore/QList>
#include <QtCore/QStringList>
#include <QtNetwork/QLocalSocket>
#include <config.h>
#include <kurl.h>
#include "kio/tcpslavebase.h"
#include "kio/http.h"
#include <kio/slavebase.h>
#include <QDomNodeList>
#include <QFile>
#include <QIODevice>
namespace KIO {
class AuthInfo;
}
class HeaderTokenizer;
class KAbstractHttpAuthentication;
class HTTPProtocol : public QObject, public KIO::TCPSlaveBase
class HttpProtocol : public QObject, public KIO::SlaveBase
{
Q_OBJECT
public:
HTTPProtocol( const QByteArray &protocol, const QByteArray &pool,
const QByteArray &app );
virtual ~HTTPProtocol();
Q_OBJECT
/** HTTP version **/
enum HTTP_REV {HTTP_None, HTTP_Unknown, HTTP_10, HTTP_11, SHOUTCAST};
public:
HttpProtocol( const QByteArray &pool, const QByteArray &app );
virtual ~HttpProtocol();
/** Authorization method used **/
enum AUTH_SCHEME {AUTH_None, AUTH_Basic, AUTH_NTLM, AUTH_Digest, AUTH_Negotiate};
virtual void setHost( const QString& host, quint16 port, const QString& user, const QString& pass );
/** DAV-specific request elements for the current connection **/
struct DAVRequest
{
DAVRequest ()
{
overwrite = false;
depth = 0;
}
QString desturl;
bool overwrite;
int depth;
};
enum CacheIOMode {
NoCache = 0,
ReadFromCache = 1,
WriteToCache = 2
};
struct CacheTag
{
CacheTag()
{
useCache = false;
ioMode = NoCache;
bytesCached = 0;
file = 0;
expireDate = 0;
servedDate = 0;
lastModifiedDate = 0;
fileUseCount = 0;
}
enum CachePlan {
UseCached = 0,
ValidateCached,
IgnoreCached
};
CachePlan plan(int maxCacheAge) const;
QByteArray serialize() const;
bool deserialize(const QByteArray &);
KIO::CacheControl policy; // ### initialize in the constructor?
bool useCache; // Whether the cache should be used
enum CacheIOMode ioMode; // Write to cache file, read from it, or don't use it.
quint32 fileUseCount;
quint32 bytesCached;
QString etag; // entity tag header as described in the HTTP standard.
QFile *file; // file on disk - either a QTemporaryFile (write) or QFile (read)
qint64 servedDate; // Date when the resource was served by the origin server
qint64 lastModifiedDate; // Last modified.
qint64 expireDate; // Date when the cache entry will expire
QString charset;
};
/** The request for the current connection **/
struct HTTPRequest
{
HTTPRequest ()
{
method = KIO::HTTP_UNKNOWN;
offset = 0;
endoffset = 0;
allowTransferCompression = false;
disablePassDialog = false;
doNotWWWAuthenticate = false;
doNotProxyAuthenticate = false;
preferErrorPage = false;
useCookieJar = false;
}
QByteArray methodString() const;
KUrl url;
QString encoded_hostname; //### can be calculated on-the-fly
// Persistent connections
bool isKeepAlive;
int keepAliveTimeout; // Timeout in seconds.
KIO::HTTP_METHOD method;
QString methodStringOverride; // Overrides method if non-empty.
QByteArray sentMethodString; // Stores http method actually sent
KIO::filesize_t offset;
KIO::filesize_t endoffset;
QString windowId; // Window Id this request is related to.
// Header fields
QString referrer;
QString charsets;
QString languages;
QString userAgent;
// Previous and current response codes
unsigned int responseCode;
unsigned int prevResponseCode;
// Miscellaneous
QString id;
DAVRequest davData;
KUrl redirectUrl;
KUrl proxyUrl;
QStringList proxyUrls;
bool isPersistentProxyConnection;
bool allowTransferCompression;
bool disablePassDialog;
bool doNotWWWAuthenticate;
bool doNotProxyAuthenticate;
// Indicates whether an error page or error message is preferred.
bool preferErrorPage;
// Use the cookie jar (or pass cookies to the application as metadata instead)
bool useCookieJar;
// Cookie flags
enum { CookiesAuto, CookiesManual, CookiesNone } cookieMode;
CacheTag cacheTag;
};
/** State of the current connection to the server **/
struct HTTPServerState
{
HTTPServerState()
{
isKeepAlive = false;
isPersistentProxyConnection = false;
}
void initFrom(const HTTPRequest &request)
{
url = request.url;
encoded_hostname = request.encoded_hostname;
isKeepAlive = request.isKeepAlive;
proxyUrl = request.proxyUrl;
isPersistentProxyConnection = request.isPersistentProxyConnection;
}
void updateCredentials(const HTTPRequest &request)
{
if (url.host() == request.url.host() && url.port() == request.url.port()) {
url.setUserName(request.url.userName());
url.setPassword(request.url.password());
}
if (proxyUrl.host() == request.proxyUrl.host() &&
proxyUrl.port() == request.proxyUrl.port()) {
proxyUrl.setUserName(request.proxyUrl.userName());
proxyUrl.setPassword(request.proxyUrl.password());
}
}
void clear()
{
url.clear();
encoded_hostname.clear();
proxyUrl.clear();
isKeepAlive = false;
isPersistentProxyConnection = false;
}
KUrl url;
QString encoded_hostname;
KUrl proxyUrl;
bool isKeepAlive;
bool isPersistentProxyConnection;
};
//---------------------- Re-implemented methods ----------------
virtual void setHost(const QString& host, quint16 port, const QString& user,
const QString& pass);
virtual void slave_status();
virtual void get( const KUrl& url );
virtual void put( const KUrl& url, int _mode, KIO::JobFlags flags );
//----------------- Re-implemented methods for WebDAV -----------
virtual void listDir( const KUrl& url );
virtual void mkdir( const KUrl& url, int _permissions );
virtual void rename( const KUrl& src, const KUrl& dest, KIO::JobFlags flags );
virtual void copy( const KUrl& src, const KUrl& dest, int _permissions, KIO::JobFlags flags );
virtual void del( const KUrl& url, bool _isfile );
// ask the host whether it supports WebDAV & cache this info
bool davHostOk();
// send generic DAV request
void davGeneric( const KUrl& url, KIO::HTTP_METHOD method, qint64 size = -1 );
// Send requests to lock and unlock resources
void davLock( const KUrl& url, const QString& scope,
const QString& type, const QString& owner );
void davUnlock( const KUrl& url );
// Calls httpClose() and finished()
void davFinished();
// Handle error conditions
QString davError( int code = -1, const QString &url = QString() );
//---------------------------- End WebDAV -----------------------
/**
* Special commands supported by this slave :
* 1 - HTTP POST
* 2 - Cache has been updated
* 3 - SSL Certificate Cache has been updated
* 4 - HTTP multi get
* 5 - DAV LOCK (see
* 6 - DAV UNLOCK README.webdav)
*/
virtual void special( const QByteArray &data );
virtual void mimetype( const KUrl& url);
virtual void stat( const KUrl& url );
virtual void reparseConfiguration();
/**
* Forced close of connection
*/
virtual void closeConnection();
void post( const KUrl& url, qint64 size = -1 );
void multiGet(const QByteArray &data);
bool maybeSetRequestUrl(const KUrl &);
/**
* Generate and send error message based on response code.
*/
bool sendHttpError();
/**
* Call SlaveBase::errorPage() and remember that we've called it
*/
bool sendErrorPageNotification();
/**
* Check network status
*/
bool isOffline();
protected Q_SLOTS:
void slotData(const QByteArray &);
void slotFilterError(const QString &text);
void error(int errid, const QString &text);
void proxyAuthenticationForSocket(const QNetworkProxy &, QAuthenticator *);
void saveProxyAuthenticationForSocket();
protected:
int readChunked(); ///< Read a chunk
int readLimited(); ///< Read maximum m_iSize bytes.
int readUnlimited(); ///< Read as much as possible.
/**
* A thin wrapper around TCPSlaveBase::write() that will retry writing as
* long as no error occurs.
*/
ssize_t write(const void *buf, size_t nbytes);
using SlaveBase::write;
/**
* Add an encoding on to the appropriate stack this
* is nececesary because transfer encodings and
* content encodings must be handled separately.
*/
void addEncoding(const QString &, QStringList &);
quint16 defaultPort() const;
// The methods between here and sendQuery() are helpers for sendQuery().
/**
* Return true if the request is already "done", false otherwise.
*
* @p cacheHasPage will be set to true if the page was found, false otherwise.
*/
bool satisfyRequestFromCache(bool *cacheHasPage);
QString formatRequestUri() const;
/**
* create HTTP authentications response(s), if any
*/
QString authenticationHeader();
bool sendQuery();
/**
* Close transfer
*/
void httpClose(bool keepAlive);
/**
* Open connection
*/
bool httpOpenConnection();
/**
* Close connection
*/
void httpCloseConnection();
/**
* Check whether to keep or close the connection.
*/
bool httpShouldCloseConnection();
void forwardHttpResponseHeader(bool forwardImmediately = true);
/**
* fix common mimetype errors by webservers.
*
* Helper for readResponseHeader().
*/
void fixupResponseMimetype();
/**
* fix common content-encoding errors by webservers.
*
* Helper for readResponseHeader().
*/
void fixupResponseContentEncoding();
bool readResponseHeader();
bool parseHeaderFromCache();
void parseContentDisposition(const QString &disposition);
bool sendBody();
bool sendCachedBody();
// where dataInternal == true, the content is to be made available
// to an internal function.
bool readBody( bool dataInternal = false );
/**
* Performs a WebDAV stat or list
*/
void davSetRequest( const QByteArray& requestXML );
void davStatList( const KUrl& url, bool stat = true );
void davParsePropstats( const QDomNodeList& propstats, KIO::UDSEntry& entry );
void davParseActiveLocks( const QDomNodeList& activeLocks,
uint& lockCount );
/**
* Returns the error code from a "HTTP/1.1 code Code Name" string
*/
int codeFromResponse( const QString& response );
/**
* Extracts locks from metadata
* Returns the appropriate If: header
*/
QString davProcessLocks();
/**
* Send a cookie to the cookiejar
*/
void addCookies( const QString &url, const QByteArray &cookieHeader);
/**
* Look for cookies in the cookiejar
*/
QString findCookies( const QString &url);
void cacheParseResponseHeader(const HeaderTokenizer &tokenizer);
QString cacheFilePathFromUrl(const KUrl &url) const;
bool cacheFileOpenRead();
bool cacheFileOpenWrite();
void cacheFileClose();
void sendCacheCleanerCommand(const QByteArray &command);
QByteArray cacheFileReadPayload(int maxLength);
void cacheFileWritePayload(const QByteArray &d);
void cacheFileWriteTextHeader();
/**
* check URL to guard against hash collisions, and load the etag for validation
*/
bool cacheFileReadTextHeader1(const KUrl &desiredUrl);
/**
* load the rest of the text fields
*/
bool cacheFileReadTextHeader2();
void setCacheabilityMetadata(bool cachingAllowed);
/**
* Do everything proceedUntilResponseHeader does, and also get the response body.
* This is being used as a replacement for proceedUntilResponseHeader() in
* situations where we actually expect the response to have a body / payload data.
*
* where dataInternal == true, the content is to be made available
* to an internal function.
*/
void proceedUntilResponseContent( bool dataInternal = false );
/**
* Ensure we are connected, send our query, and get the response header.
*/
bool proceedUntilResponseHeader();
/**
* Resets any per session settings.
*/
void resetSessionSettings();
/**
* Resets variables related to parsing a response.
*/
void resetResponseParsing();
/**
* Resets any per connection settings. These are different from
* per-session settings in that they must be invalidated every time
* a request is made, e.g. a retry to re-send the header to the
* server, as compared to only when a new request arrives.
*/
void resetConnectionSettings();
/**
* Caches the POST data in a temporary buffer.
*
* Depending on size of content, the temporary buffer might be
* created either in memory or on disk as (a temporary file).
*/
void cachePostData(const QByteArray&);
/**
* Clears the POST data buffer.
*
* Note that calling this function results in the POST data buffer
* getting completely deleted.
*/
void clearPostDataBuffer();
/**
* Returns true on successful retrieval of all content data.
*/
bool retrieveAllData();
/**
* Saves HTTP authentication data.
*/
void saveAuthenticationData();
/**
* Handles HTTP authentication.
*/
bool handleAuthenticationHeader(const HeaderTokenizer* tokenizer);
/**
* Handles file -> webdav put requests.
*/
void copyPut(const KUrl& src, const KUrl& dest, KIO::JobFlags flags);
/**
* Stats a remote DAV file and returns true if it exists.
*/
bool davStatDestination();
protected:
HTTPServerState m_server;
HTTPRequest m_request;
QList<HTTPRequest> m_requestQueue;
// Processing related
KIO::filesize_t m_iSize; ///< Expected size of message
KIO::filesize_t m_iPostDataSize;
KIO::filesize_t m_iBytesLeft; ///< # of bytes left to receive in this message.
KIO::filesize_t m_iContentLeft; ///< # of content bytes left
QByteArray m_receiveBuf; ///< Receive buffer
bool m_dataInternal; ///< Data is for internal consumption
bool m_isChunked; ///< Chunked transfer encoding
bool m_isBusy; ///< Busy handling request queue.
bool m_isEOF;
bool m_isEOD;
//--- Settings related to a single response only
bool m_isRedirection; ///< Indicates current request is a redirection
QStringList m_responseHeaders; ///< All headers
// Language/Encoding related
QStringList m_transferEncodings;
QStringList m_contentEncodings;
QString m_contentMD5;
QString m_mimeType; // TODO QByteArray?
//--- WebDAV
// Data structure to hold data which will be passed to an internal func.
QByteArray m_webDavDataBuf;
QStringList m_davCapabilities;
bool m_davHostOk;
bool m_davHostUnsupported;
//----------
// Mimetype determination
bool m_cpMimeBuffer;
QByteArray m_mimeTypeBuffer;
// Holds the POST data so it won't get lost on if we
// happend to get a 401/407 response when submitting
// a form.
QIODevice* m_POSTbuf;
// Cache related
int m_maxCacheAge; ///< Maximum age of a cache entry in seconds.
long m_maxCacheSize; ///< Maximum cache size in Kb.
QString m_strCacheDir; ///< Location of the cache.
QLocalSocket m_cacheCleanerConnection; ///< Connection to the cache cleaner process
// Operation mode
QByteArray m_protocol;
KAbstractHttpAuthentication *m_wwwAuth;
KAbstractHttpAuthentication *m_proxyAuth;
// For proxy auth when it's handled by the Qt/KDE socket classes
QAuthenticator *m_socketProxyAuth;
// Indicates whether there was some error.
int m_iError;
// Whether we are loading an error page (we should close the connection afterwards)
bool m_isLoadingErrorPage;
// Values that determine the remote connection timeouts.
int m_remoteRespTimeout;
// EOF Retry count
quint8 m_iEOFRetryCount;
QByteArray m_unreadBuf;
void clearUnreadBuffer();
void unread(char *buf, size_t size);
size_t readBuffered(char *buf, size_t size, bool unlimited = true);
bool readDelimitedText(char *buf, int *idx, int end, int numNewlines);
virtual void stat( const KUrl &url );
virtual void get( const KUrl& url );
virtual void put( const KUrl& url, int permissions, KIO::JobFlags flags );
};
#endif
#endif // KDELIBS_HTTP_H

View file

@ -3,13 +3,19 @@ exec=kio_http
protocol=http
input=none
output=filesystem
copyToFile=false
copyFromFile=false
listing=
reading=true
deleting=true
writing=true
defaultMimetype=application/octet-stream
determineMimetypeFromExtension=false
makedir=false
deleting=false
moving=false
ProxiedBy=http
Icon=text-html
maxInstances=20
maxInstancesPerHost=5
defaultMimetype=application/octet-stream
determineMimetypeFromExtension=false
X-DocPath=kioslave/http/index.html
Class=:internet

View file

@ -1,868 +0,0 @@
/*
This file is part of KDE
Copyright (C) 1999-2000 Waldo Bastian (bastian@kde.org)
Copyright (C) 2009 Andreas Hartmetz (ahartmetz@gmail.com)
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, 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 HTTP Cache cleanup tool
#include <cstring>
#include <time.h>
#include <stdlib.h>
#include <zlib.h>
#include <QtCore/QDir>
#include <QtCore/QString>
#include <QtCore/qdatetime.h>
#include <QtDBus/QtDBus>
#include <QtNetwork/QLocalServer>
#include <QtNetwork/QLocalSocket>
#include <QCoreApplication>
#include <QCryptographicHash>
#include <kcmdlineargs.h>
#include <kcomponentdata.h>
#include <kdatetime.h>
#include <kdebug.h>
#include <kglobal.h>
#include <klocale.h>
#include <kprotocolmanager.h>
#include <kstandarddirs.h>
#include <unistd.h>
time_t g_currentDate;
int g_maxCacheAge;
qint64 g_maxCacheSize;
static const char appFullName[] = "org.kde.kio_http_cache_cleaner";
static const char appName[] = "kio_http_cache_cleaner";
// !START OF SYNC!
// Keep the following in sync with the cache code in http.cpp
static const int s_hashedUrlBits = 160; // this number should always be divisible by eight
static const int s_hashedUrlNibbles = s_hashedUrlBits / 4;
static const int s_hashedUrlBytes = s_hashedUrlBits / 8;
static const char version[] = "A\n";
// never instantiated, on-disk / wire format only
struct SerializedCacheFileInfo {
// from http.cpp
quint8 version[2];
quint8 compression; // for now fixed to 0
quint8 reserved; // for now; also alignment
static const int useCountOffset = 4;
qint32 useCount;
qint64 servedDate;
qint64 lastModifiedDate;
qint64 expireDate;
qint32 bytesCached;
static const int size = 36;
QString url;
QString etag;
QString mimeType;
QStringList responseHeaders; // including status response like "HTTP 200 OK"
};
static QString dateString(qint64 date)
{
KDateTime dt;
dt.setTime_t(date);
return dt.toString(KDateTime::ISODate);
}
struct MiniCacheFileInfo {
// data from cache entry file, or from scoreboard file
qint32 useCount;
// from filesystem
qint64 lastUsedDate;
qint64 sizeOnDisk;
// we want to delete the least "useful" files and we'll have to sort a list for that...
bool operator<(const MiniCacheFileInfo &other) const;
void debugPrint() const
{
kDebug(7113) << "useCount:" << useCount
<< "\nlastUsedDate:" << lastUsedDate
<< "\nsizeOnDisk:" << sizeOnDisk << '\n';
}
};
struct CacheFileInfo : MiniCacheFileInfo {
quint8 version[2];
quint8 compression; // for now fixed to 0
quint8 reserved; // for now; also alignment
qint64 servedDate;
qint64 lastModifiedDate;
qint64 expireDate;
qint32 bytesCached;
QString baseName;
QString url;
QString etag;
QString mimeType;
QStringList responseHeaders; // including status response like "HTTP 200 OK"
void prettyPrint() const
{
QTextStream out(stdout, QIODevice::WriteOnly);
out << "File " << baseName << " version " << version[0] << version[1];
out << "\n cached bytes " << bytesCached << " useCount " << useCount;
out << "\n servedDate " << dateString(servedDate);
out << "\n lastModifiedDate " << dateString(lastModifiedDate);
out << "\n expireDate " << dateString(expireDate);
out << "\n entity tag " << etag;
out << "\n encoded URL " << url;
out << "\n mimetype " << mimeType;
out << "\nResponse headers follow...\n";
Q_FOREACH (const QString &h, responseHeaders) {
out << h << '\n';
}
}
};
bool MiniCacheFileInfo::operator<(const MiniCacheFileInfo &other) const
{
const int thisUseful = useCount / qMax(g_currentDate - lastUsedDate, qint64(1));
const int otherUseful = other.useCount / qMax(g_currentDate - other.lastUsedDate, qint64(1));
return thisUseful < otherUseful;
}
bool CacheFileInfoPtrLessThan(const CacheFileInfo *cf1, const CacheFileInfo *cf2)
{
return *cf1 < *cf2;
}
enum OperationMode {
CleanCache = 0,
DeleteCache,
FileInfo
};
static bool timeSizeFits(qint64 intTime)
{
time_t tTime = static_cast<time_t>(intTime);
qint64 check = static_cast<qint64>(tTime);
return check == intTime;
}
static bool readBinaryHeader(const QByteArray &d, CacheFileInfo *fi)
{
if (d.size() < SerializedCacheFileInfo::size) {
kDebug(7113) << "readBinaryHeader(): file too small?";
return false;
}
QDataStream stream(d);
stream >> fi->version[0];
stream >> fi->version[1];
if (fi->version[0] != version[0] || fi->version[1] != version[1]) {
kDebug(7113) << "readBinaryHeader(): wrong magic bytes";
return false;
}
stream >> fi->compression;
stream >> fi->reserved;
stream >> fi->useCount;
stream >> fi->servedDate;
stream >> fi->lastModifiedDate;
stream >> fi->expireDate;
bool timeSizeOk = timeSizeFits(fi->servedDate) && timeSizeFits(fi->lastModifiedDate) &&
timeSizeFits(fi->expireDate);
stream >> fi->bytesCached;
return timeSizeOk;
}
static QString filenameFromUrl(const QByteArray &url)
{
QCryptographicHash hash(QCryptographicHash::Sha1);
hash.addData(url);
return QString::fromLatin1(hash.result().toHex());
}
static QString filePath(const QString &baseName)
{
QString cacheDirName = KGlobal::dirs()->saveLocation("cache", "http");
if (!cacheDirName.endsWith('/')) {
cacheDirName.append('/');
}
return cacheDirName + baseName;
}
static bool readLineChecked(QIODevice *dev, QByteArray *line)
{
*line = dev->readLine(8192);
// if nothing read or the line didn't fit into 8192 bytes(!)
if (line->isEmpty() || !line->endsWith('\n')) {
return false;
}
// we don't actually want the newline!
line->chop(1);
return true;
}
static bool readTextHeader(QFile *file, CacheFileInfo *fi, OperationMode mode)
{
bool ok = true;
QByteArray readBuf;
ok = ok && readLineChecked(file, &readBuf);
fi->url = QString::fromLatin1(readBuf);
if (filenameFromUrl(readBuf) != QFileInfo(*file).baseName()) {
kDebug(7103) << "You have witnessed a very improbable hash collision!";
return false;
}
// only read the necessary info for cache cleaning. Saves time and (more importantly) memory.
if (mode != FileInfo) {
return true;
}
ok = ok && readLineChecked(file, &readBuf);
fi->etag = QString::fromLatin1(readBuf);
ok = ok && readLineChecked(file, &readBuf);
fi->mimeType = QString::fromLatin1(readBuf);
// read as long as no error and no empty line found
while (true) {
ok = ok && readLineChecked(file, &readBuf);
if (ok && !readBuf.isEmpty()) {
fi->responseHeaders.append(QString::fromLatin1(readBuf));
} else {
break;
}
}
return ok; // it may still be false ;)
}
// TODO common include file with http.cpp?
enum CacheCleanerCommand {
InvalidCommand = 0,
CreateFileNotificationCommand,
UpdateFileCommand
};
static bool readCacheFile(const QString &baseName, CacheFileInfo *fi, OperationMode mode)
{
QFile file(filePath(baseName));
if (!file.open(QIODevice::ReadOnly)) {
return false;
}
fi->baseName = baseName;
QByteArray header = file.read(SerializedCacheFileInfo::size);
// do *not* modify/delete the file if we're in file info mode.
if (!(readBinaryHeader(header, fi) && readTextHeader(&file, fi, mode)) && mode != FileInfo) {
kDebug(7113) << "read(Text|Binary)Header() returned false, deleting file" << baseName;
file.remove();
return false;
}
// get meta-information from the filesystem
QFileInfo fileInfo(file);
fi->lastUsedDate = fileInfo.lastModified().toTime_t();
fi->sizeOnDisk = fileInfo.size();
return true;
}
class Scoreboard;
class CacheIndex
{
public:
explicit CacheIndex(const QString &baseName)
{
QByteArray ba = baseName.toLatin1();
const int sz = ba.size();
const char *input = ba.constData();
Q_ASSERT(sz == s_hashedUrlNibbles);
int translated = 0;
for (int i = 0; i < sz; i++) {
int c = input[i];
if (c >= '0' && c <= '9') {
translated |= c - '0';
} else if (c >= 'a' && c <= 'f') {
translated |= c - 'a' + 10;
} else {
Q_ASSERT(false);
}
if (i & 1) {
// odd index
m_index[i >> 1] = translated;
translated = 0;
} else {
translated = translated << 4;
}
}
computeHash();
}
bool operator==(const CacheIndex &other) const
{
const bool isEqual = memcmp(m_index, other.m_index, s_hashedUrlBytes) == 0;
if (isEqual) {
Q_ASSERT(m_hash == other.m_hash);
}
return isEqual;
}
private:
explicit CacheIndex(const QByteArray &index)
{
Q_ASSERT(index.length() >= s_hashedUrlBytes);
memcpy(m_index, index.constData(), s_hashedUrlBytes);
computeHash();
}
void computeHash()
{
uint hash = 0;
const int ints = s_hashedUrlBytes / sizeof(uint);
for (int i = 0; i < ints; i++) {
hash ^= reinterpret_cast<uint *>(&m_index[0])[i];
}
if (const int bytesLeft = s_hashedUrlBytes % sizeof(uint)) {
// dead code until a new url hash algorithm or architecture with sizeof(uint) != 4 appears.
// we have the luxury of ignoring endianness because the hash is never written to disk.
// just merge the bits into the the hash in some way.
const int offset = ints * sizeof(uint);
for (int i = 0; i < bytesLeft; i++) {
hash ^= static_cast<uint>(m_index[offset + i]) << (i * 8);
}
}
m_hash = hash;
}
friend uint qHash(const CacheIndex &);
friend class Scoreboard;
quint8 m_index[s_hashedUrlBytes]; // packed binary version of the hexadecimal name
uint m_hash;
};
uint qHash(const CacheIndex &ci)
{
return ci.m_hash;
}
static CacheCleanerCommand readCommand(const QByteArray &cmd, CacheFileInfo *fi)
{
readBinaryHeader(cmd, fi);
QDataStream stream(cmd);
stream.skipRawData(SerializedCacheFileInfo::size);
quint32 ret;
stream >> ret;
QByteArray baseName;
baseName.resize(s_hashedUrlNibbles);
stream.readRawData(baseName.data(), s_hashedUrlNibbles);
Q_ASSERT(stream.atEnd());
fi->baseName = QString::fromLatin1(baseName);
Q_ASSERT(ret == CreateFileNotificationCommand || ret == UpdateFileCommand);
return static_cast<CacheCleanerCommand>(ret);
}
// never istantiated, on-disk format only
struct ScoreboardEntry {
// from scoreboard file
quint8 index[s_hashedUrlBytes];
static const int indexSize = s_hashedUrlBytes;
qint32 useCount;
// from scoreboard file, but compared with filesystem to see if scoreboard has current data
qint64 lastUsedDate;
qint32 sizeOnDisk;
static const int size = 36;
// we want to delete the least "useful" files and we'll have to sort a list for that...
bool operator<(const MiniCacheFileInfo &other) const;
};
class Scoreboard
{
public:
Scoreboard()
{
// read in the scoreboard...
QFile sboard(filePath(QLatin1String("scoreboard")));
sboard.open(QIODevice::ReadOnly);
while (true) {
QByteArray baIndex = sboard.read(ScoreboardEntry::indexSize);
QByteArray baRest = sboard.read(ScoreboardEntry::size - ScoreboardEntry::indexSize);
if (baIndex.size() + baRest.size() != ScoreboardEntry::size) {
break;
}
const QString entryBasename = QString::fromLatin1(baIndex.toHex());
MiniCacheFileInfo mcfi;
if (readAndValidateMcfi(baRest, entryBasename, &mcfi)) {
m_scoreboard.insert(CacheIndex(baIndex), mcfi);
}
}
}
void writeOut()
{
// write out the scoreboard
QFile sboard(filePath(QLatin1String("scoreboard")));
if (!sboard.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
return;
}
QDataStream stream(&sboard);
QHash<CacheIndex, MiniCacheFileInfo>::ConstIterator it = m_scoreboard.constBegin();
for (; it != m_scoreboard.constEnd(); ++it) {
const char *indexData = reinterpret_cast<const char *>(it.key().m_index);
stream.writeRawData(indexData, s_hashedUrlBytes);
stream << it.value().useCount;
stream << it.value().lastUsedDate;
stream << it.value().sizeOnDisk;
}
}
bool fillInfo(const QString &baseName, MiniCacheFileInfo *mcfi)
{
QHash<CacheIndex, MiniCacheFileInfo>::ConstIterator it =
m_scoreboard.constFind(CacheIndex(baseName));
if (it == m_scoreboard.constEnd()) {
return false;
}
*mcfi = it.value();
return true;
}
qint64 runCommand(const QByteArray &cmd)
{
// execute the command; return number of bytes if a new file was created, zero otherwise.
Q_ASSERT(cmd.size() == 80);
CacheFileInfo fi;
const CacheCleanerCommand ccc = readCommand(cmd, &fi);
QString fileName = filePath(fi.baseName);
switch (ccc) {
case CreateFileNotificationCommand:
kDebug(7113) << "CreateNotificationCommand for" << fi.baseName;
if (!readBinaryHeader(cmd, &fi)) {
return 0;
}
break;
case UpdateFileCommand: {
kDebug(7113) << "UpdateFileCommand for" << fi.baseName;
QFile file(fileName);
file.open(QIODevice::ReadWrite);
CacheFileInfo fiFromDisk;
QByteArray header = file.read(SerializedCacheFileInfo::size);
if (!readBinaryHeader(header, &fiFromDisk) || fiFromDisk.bytesCached != fi.bytesCached) {
return 0;
}
// adjust the use count, to make sure that we actually count up. (slaves read the file
// asynchronously...)
const quint32 newUseCount = fiFromDisk.useCount + 1;
QByteArray newHeader = cmd.mid(0, SerializedCacheFileInfo::size);
{
QDataStream stream(&newHeader, QIODevice::WriteOnly);
stream.skipRawData(SerializedCacheFileInfo::useCountOffset);
stream << newUseCount;
}
file.seek(0);
file.write(newHeader);
file.close();
if (!readBinaryHeader(newHeader, &fi)) {
return 0;
}
break;
}
default:
kDebug(7113) << "received invalid command";
return 0;
}
QFileInfo fileInfo(fileName);
fi.lastUsedDate = fileInfo.lastModified().toTime_t();
fi.sizeOnDisk = fileInfo.size();
fi.debugPrint();
// a CacheFileInfo is-a MiniCacheFileInfo which enables the following assignment...
add(fi);
// finally, return cache dir growth (only relevant if a file was actually created!)
return ccc == CreateFileNotificationCommand ? fi.sizeOnDisk : 0;
}
void add(const CacheFileInfo &fi)
{
m_scoreboard[CacheIndex(fi.baseName)] = fi;
}
void remove(const QString &basename)
{
m_scoreboard.remove(CacheIndex(basename));
}
// keep memory usage reasonably low - otherwise entries of nonexistent files don't hurt.
void maybeRemoveStaleEntries(const QList<CacheFileInfo *> &fiList)
{
// don't bother when there are a few bogus entries
if (m_scoreboard.count() < fiList.count() + 100) {
return;
}
kDebug(7113) << "we have too many fake/stale entries, cleaning up...";
QSet<CacheIndex> realFiles;
Q_FOREACH (CacheFileInfo *fi, fiList) {
realFiles.insert(CacheIndex(fi->baseName));
}
QHash<CacheIndex, MiniCacheFileInfo>::Iterator it = m_scoreboard.begin();
while (it != m_scoreboard.end()) {
if (realFiles.contains(it.key())) {
++it;
} else {
it = m_scoreboard.erase(it);
}
}
}
private:
bool readAndValidateMcfi(const QByteArray &rawData, const QString &basename, MiniCacheFileInfo *mcfi)
{
QDataStream stream(rawData);
stream >> mcfi->useCount;
// check those against filesystem
stream >> mcfi->lastUsedDate;
stream >> mcfi->sizeOnDisk;
QFileInfo fileInfo(filePath(basename));
if (!fileInfo.exists()) {
return false;
}
bool ok = true;
ok = ok && fileInfo.lastModified().toTime_t() == mcfi->lastUsedDate;
ok = ok && fileInfo.size() == mcfi->sizeOnDisk;
if (!ok) {
// size or last-modified date not consistent with entry file; reload useCount
// note that avoiding to open the file is the whole purpose of the scoreboard - we only
// open the file if we really have to.
QFile entryFile(fileInfo.absoluteFilePath());
if (!entryFile.open(QIODevice::ReadOnly)) {
return false;
}
if (entryFile.size() < SerializedCacheFileInfo::size) {
return false;
}
QDataStream stream(&entryFile);
stream.skipRawData(SerializedCacheFileInfo::useCountOffset);
stream >> mcfi->useCount;
mcfi->lastUsedDate = fileInfo.lastModified().toTime_t();
mcfi->sizeOnDisk = fileInfo.size();
ok = true;
}
return ok;
}
QHash<CacheIndex, MiniCacheFileInfo> m_scoreboard;
};
// Keep the above in sync with the cache code in http.cpp
// !END OF SYNC!
// remove files and directories used by earlier versions of the HTTP cache.
static void removeOldFiles()
{
const char *oldDirs = "0abcdefghijklmnopqrstuvwxyz";
const int n = strlen(oldDirs);
QDir cacheRootDir(filePath(QString()));
for (int i = 0; i < n; i++) {
QString dirName = QString::fromLatin1(&oldDirs[i], 1);
// delete files in directory...
Q_FOREACH (const QString &baseName, QDir(filePath(dirName)).entryList()) {
QFile::remove(filePath(dirName + '/' + baseName));
}
// delete the (now hopefully empty!) directory itself
cacheRootDir.rmdir(dirName);
}
QFile::remove(filePath(QLatin1String("cleaned")));
}
class CacheCleaner
{
public:
CacheCleaner(const QDir &cacheDir)
: m_totalSizeOnDisk(0)
{
kDebug(7113);
m_fileNameList = cacheDir.entryList();
}
// Delete some of the files that need to be deleted. Return true when done, false otherwise.
// This makes interleaved cleaning / serving ioslaves possible.
bool processSlice(Scoreboard *scoreboard = 0)
{
QTime t;
t.start();
// phase one: gather information about cache files
if (!m_fileNameList.isEmpty()) {
while (t.elapsed() < 100 && !m_fileNameList.isEmpty()) {
QString baseName = m_fileNameList.takeFirst();
// check if the filename is of the $s_hashedUrlNibbles letters, 0...f type
if (baseName.length() < s_hashedUrlNibbles) {
continue;
}
bool nameOk = true;
for (int i = 0; i < s_hashedUrlNibbles && nameOk; i++) {
QChar c = baseName[i];
nameOk = (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f');
}
if (!nameOk) {
continue;
}
if (baseName.length() > s_hashedUrlNibbles) {
if (g_currentDate - QFileInfo(filePath(baseName)).lastModified().toTime_t() > 15*60) {
// it looks like a temporary file that hasn't been touched in > 15 minutes...
QFile::remove(filePath(baseName));
}
// the temporary file might still be written to, leave it alone
continue;
}
CacheFileInfo *fi = new CacheFileInfo();
fi->baseName = baseName;
bool gotInfo = false;
if (scoreboard) {
gotInfo = scoreboard->fillInfo(baseName, fi);
}
if (!gotInfo) {
gotInfo = readCacheFile(baseName, fi, CleanCache);
if (gotInfo && scoreboard) {
scoreboard->add(*fi);
}
}
if (gotInfo) {
m_fiList.append(fi);
m_totalSizeOnDisk += fi->sizeOnDisk;
} else {
delete fi;
}
}
kDebug(7113) << "total size of cache files is" << m_totalSizeOnDisk;
if (m_fileNameList.isEmpty()) {
// final step of phase one
qSort(m_fiList.begin(), m_fiList.end(), CacheFileInfoPtrLessThan);
}
return false;
}
// phase two: delete files until cache is under maximum allowed size
// TODO: delete files larger than allowed for a single file
while (t.elapsed() < 100) {
if (m_totalSizeOnDisk <= g_maxCacheSize || m_fiList.isEmpty()) {
kDebug(7113) << "total size of cache files after cleaning is" << m_totalSizeOnDisk;
if (scoreboard) {
scoreboard->maybeRemoveStaleEntries(m_fiList);
scoreboard->writeOut();
}
qDeleteAll(m_fiList);
m_fiList.clear();
return true;
}
CacheFileInfo *fi = m_fiList.takeFirst();
QString filename = filePath(fi->baseName);
if (QFile::remove(filename)) {
m_totalSizeOnDisk -= fi->sizeOnDisk;
if (scoreboard) {
scoreboard->remove(fi->baseName);
}
}
delete fi;
}
return false;
}
private:
QStringList m_fileNameList;
QList<CacheFileInfo *> m_fiList;
qint64 m_totalSizeOnDisk;
};
int main(int argc, char **argv)
{
KCmdLineArgs::init(argc, argv, appName, "kdelibs4",
ki18n("KDE HTTP cache maintenance tool"), version,
ki18n("KDE HTTP cache maintenance tool"), KCmdLineArgs::CmdLineArgNone);
KCmdLineOptions options;
options.add("clear-all", ki18n("Empty the cache"));
options.add("file-info <filename>", ki18n("Display information about cache file"));
KCmdLineArgs::addCmdLineOptions( options );
KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
KComponentData componentData(appName);
// we need a QCoreApplication so QCoreApplication::processEvents() works as intended
QCoreApplication app(argc, argv);
OperationMode mode = CleanCache;
if (args->isSet("clear-all")) {
mode = DeleteCache;
} else if (args->isSet("file-info")) {
mode = FileInfo;
}
// file info mode: no scanning of directories, just output info and exit.
if (mode == FileInfo) {
CacheFileInfo fi;
if (!readCacheFile(args->getOption("file-info"), &fi, mode)) {
return 1;
}
fi.prettyPrint();
return 0;
}
// make sure we're the only running instance of the cleaner service
if (mode == CleanCache) {
if (!QDBusConnection::sessionBus().isConnected()) {
QDBusError error(QDBusConnection::sessionBus().lastError());
fprintf(stderr, "%s: Could not connect to D-Bus! (%s: %s)\n", appName,
qPrintable(error.name()), qPrintable(error.message()));
return 1;
}
if (!QDBusConnection::sessionBus().registerService(appFullName)) {
fprintf(stderr, "%s: Already running!\n", appName);
return 0;
}
}
g_currentDate = time(0);
g_maxCacheAge = KProtocolManager::maxCacheAge();
g_maxCacheSize = mode == DeleteCache ? -1 : KProtocolManager::maxCacheSize() * 1024;
QString cacheDirName = KGlobal::dirs()->saveLocation("cache", "http");
QDir cacheDir(cacheDirName);
if (!cacheDir.exists()) {
fprintf(stderr, "%s: '%s' does not exist.\n", appName, qPrintable(cacheDirName));
return 0;
}
removeOldFiles();
if (mode == DeleteCache) {
QTime t;
t.start();
cacheDir.refresh();
//qDebug() << "time to refresh the cacheDir QDir:" << t.elapsed();
CacheCleaner cleaner(cacheDir);
while (!cleaner.processSlice()) { }
return 0;
}
QLocalServer lServer;
QString socketFileName = KStandardDirs::locateLocal("socket", "kio_http_cache_cleaner");
// we need to create the file by opening the socket, otherwise it won't work
QFile::remove(socketFileName);
lServer.listen(socketFileName);
QList<QLocalSocket *> sockets;
qint64 newBytesCounter = LLONG_MAX; // force cleaner run on startup
Scoreboard scoreboard;
CacheCleaner *cleaner = 0;
while (true) {
g_currentDate = time(0);
if (cleaner) {
QCoreApplication::processEvents(QEventLoop::AllEvents, 100);
} else {
// We will not immediately know when a socket was disconnected. Causes:
// - WaitForMoreEvents does not make processEvents() return when a socket disconnects
// - WaitForMoreEvents *and* a timeout is not possible.
QCoreApplication::processEvents(QEventLoop::WaitForMoreEvents);
}
if (!lServer.isListening()) {
return 1;
}
lServer.waitForNewConnection(1);
while (QLocalSocket *sock = lServer.nextPendingConnection()) {
sock->waitForConnected();
sockets.append(sock);
}
for (int i = 0; i < sockets.size(); i++) {
QLocalSocket *sock = sockets[i];
if (sock->state() != QLocalSocket::ConnectedState) {
if (sock->state() != QLocalSocket::UnconnectedState) {
sock->waitForDisconnected();
}
delete sock;
sockets.removeAll(sock);
i--;
continue;
}
sock->waitForReadyRead(0);
while (true) {
QByteArray recv = sock->read(80);
if (recv.isEmpty()) {
break;
}
Q_ASSERT(recv.size() == 80);
newBytesCounter += scoreboard.runCommand(recv);
}
}
// interleave cleaning with serving ioslaves to reduce "garbage collection pauses"
if (cleaner) {
if (cleaner->processSlice(&scoreboard)) {
// that was the last slice, done
delete cleaner;
cleaner = 0;
}
} else if (newBytesCounter > (g_maxCacheSize / 8)) {
cacheDir.refresh();
cleaner = new CacheCleaner(cacheDir);
newBytesCounter = 0;
}
}
return 0;
}

View file

@ -1,195 +0,0 @@
[Desktop Entry]
Type=Service
Name=HTTP Cache Cleaner
Name[af]=HTTP Kasskoonmaker
Name[ar]=منظف مخبأ HTTP
Name[as]=HTTP ি
Name[ast]=Llimpiador de la caché HTTP
Name[be]=Ачыстка кэшу HTTP
Name[be@latin]=Vyčyščeńnie padručnaj pamiaci HTTP
Name[bg]=Изчистване на кеш-паметта на HTTP
Name[bn]=-ি-ি-ি
Name[bn_IN]=HTTP িি
Name[br]=Naeter Krubuilh HTTP
Name[bs]=Čistač HTTP keša
Name[ca]=Netejador de la memòria cau HTTP
Name[ca@valencia]=Neteja la memòria cau del HTTP
Name[cs]=Nástroj pro vyprázdnění cache protokolu HTTP
Name[csb]=Czëszczenié cache HTTP
Name[cy]=Glanhauwr Storfa HTTP
Name[da]=HTTP-cache-rydder
Name[de]=Aufräumprogramm für den HTTP-Zwischenspeicher
Name[el]=Καθαριστής λανθάνουσας μνήμης HTTP
Name[en_GB]=HTTP Cache Cleaner
Name[eo]=HTTP-kaŝmemora purigilo
Name[es]=Limpiador de la caché de HTTP
Name[et]=HTTP vahemälu puhastaja
Name[eu]=HTTP cache-garbitzailea
Name[fa]=پاککننده نهانگاه قام
Name[fi]=HTTP-välimuistin tyhjentäjä
Name[fr]=Nettoyeur du cache HTTP
Name[fy]=HTTP Cache oprommer
Name[ga]=Glantóir Taisce HTTP
Name[gl]=Limpador da caché de HTTP
Name[gu]=HTTP
Name[he]=מנקה מטמון ה־HTTP
Name[hi]=
Name[hne]=
Name[hr]=Brisač HTTP-ove međumemorije
Name[hsb]=HTTP Cache Cleaner
Name[hu]=HTTP gyorstártisztító
Name[hy]=HTTP-ի պահեստը մաքրող
Name[ia]=Mundator de le HTTP Cache
Name[id]=Pembersih Tembolok HTTP
Name[is]=Hreinsiforrit HTTP skyndiminnis
Name[it]=Ripulitore della cache HTTP
Name[ja]=HTTP
Name[kk]=HTTP кэшін босату
Name[km]= HTTP
Name[kn]=HTTP ಿಿ () ಿ ()
Name[ko]=HTTP
Name[ku]=Jê bira Nav-bîra HTTP
Name[lb]=Opraumer fir den HTTP-Zwëschespäicher
Name[lt]=HTTP krepšio ištuštintojas
Name[lv]=HTTP kešatmiņas tīrītājs
Name[mai]=
Name[mk]=Бришење на HTTP-кешот
Name[ml]=ിിി ി
Name[mr]=HTTP
Name[ms]=Pembersih Penyimpan HTTP
Name[nb]=HTTP-hurtiglagerrenser
Name[nds]=Reenmaker för HTTP-Twischenspieker
Name[ne]= ि
Name[nl]=HTTP Cache opschonen
Name[nn]=Opprydding av HTTP-mellomlager
Name[or]=HTTP
Name[pa]=HTTP
Name[pl]=Czyszczenie bufora HTTP
Name[ps]=پټياد پاکوونکی HTTP
Name[pt]=Limpeza da 'Cache' de HTTP
Name[pt_BR]=Limpador de cache HTTP
Name[ro]=Curățător cache HTTP
Name[ru]=Очистка кэша HTTP
Name[se]=HTTP gaskarádjosa buhtisteaddji
Name[si]=HTTP
Name[sk]=Čistič vyrovnávacej pamäte HTTP
Name[sl]=Čistilnik predpomnilnika HTTP
Name[sr]=Чистач ХТТП кеша
Name[sr@ijekavian]=Чистач ХТТП кеша
Name[sr@ijekavianlatin]=Čistač HTTP keša
Name[sr@latin]=Čistač HTTP keša
Name[sv]=HTTP-cache-rensare
Name[ta]=HTTP ி ி
Name[te]= ిిి ి
Name[tg]=Тозакунаки HTTP Cache
Name[th]= HTTP
Name[tr]=HTTP Önbellek Temizleyici
Name[tt]=HTTP кешын бушату
Name[ug]=HTTP غەملەك تازىلىغۇچ
Name[uk]=Очищувач кешу HTTP
Name[uz]=HTTP kesh boʻshatgich
Name[uz@cyrillic]=HTTP кэш бўшатгич
Name[vi]=B làm sch b nh tm HTTP
Name[wa]=Netieu del muchete HTTP
Name[xh]=Umcoci wendawo efihlakeleyo yokugcina we HTTP
Name[x-test]=xxHTTP Cache Cleanerxx
Name[zh_CN]=HTTP
Name[zh_HK]=HTTP
Name[zh_TW]=HTTP
Exec=kio_http_cache_cleaner
Comment=Cleans up old entries from the HTTP cache
Comment[af]=Skoonmaak begin ou inskrywings van die Http kas
Comment[ar]=يزيل المدخلات القديمة من مخبأ HTTP
Comment[as]=HTTP ি ি
Comment[ast]=Llimpia entráes antigües de la caché HTTP
Comment[be]=Выдаляе старыя запісы з кэшу HTTP
Comment[be@latin]=Vyčyščaje staryja zapisy z padručnaj pamiaci HTTP
Comment[bg]=Изчистване на старите данни в кеш-паметта на HTTP
Comment[bn]=HTTP
Comment[bn_IN]=HTTP
Comment[br]=Skarañ enmontoù kozh diwar ar grubuilh HTTP
Comment[bs]=Čisti stare stavke iz HTTP keša
Comment[ca]=Neteja les entrades antigues de la memòria cau HTTP
Comment[ca@valencia]=Neteja les entrades antigues de la memòria cau del HTTP
Comment[cs]=Odstraňuje staré položky z HTTP cache
Comment[csb]=Rëmô stôré wpisënczi z cache HTTP
Comment[cy]=Glanhau'r hen gofnodion o'r storfa HTTP
Comment[da]=Rydder op i gamle indgange fra HTTP-cachen
Comment[de]=Löscht alte Einträge aus dem HTTP-Zwischenspeicher
Comment[el]=Καθαρίζει παλιές καταχωρήσεις από τη λανθάνουσα μνήμη HTTP
Comment[en_GB]=Cleans up old entries from the HTTP cache
Comment[eo]=Forigas malnovajn erojn el HTTP-kaŝmemoro
Comment[es]=Elimina las entradas antiguas de la caché de HTTP
Comment[et]=Puhastab HTTP vahemälu vanadest kirjetest
Comment[eu]=HTTP cachearen sarrera zaharrak garbitzen ditu
Comment[fa]=مدخلهای قدیمی را از نهانگاه قام پاک میکند
Comment[fi]=Puhdistaa vanhat tiedot HTTP-välimuistista
Comment[fr]=Efface les anciennes entrées du cache HTTP
Comment[fy]=Ferwidert âlde items út de HTTP-cache
Comment[ga]=Glanann seaniontrálacha ón taisce HTTP
Comment[gl]=Elimina as entradas antigas da caché de HTTP
Comment[gu]=HTTP
Comment[he]=מנקה רשומות ישנות ממטמון ה־HTTP
Comment[hi]= िि
Comment[hne]= िि
Comment[hr]=Uklanjanje starih unosa iz HTTP-ove međumemorije
Comment[hsb]=Wumaza stare zapisy z temporerneho pomjatka HTTP
Comment[hu]=Kitörli a régi bejegyzéseket a HTTP gyorstárból
Comment[hy]=Մաքրում է հին գրառումները HTTP-ի պահեստից
Comment[ia]=Il munda le vetere entratas ab le cache HTTP
Comment[id]=Membersihkan entri lama dari tembolok HTTP
Comment[is]=Hreinsar gamlar færslur úr HTTP skyndiminninu
Comment[it]=Ripulisce la cache HTTP dalle voci vecchie
Comment[ja]=HTTP
Comment[kk]=HTTP кэшін ескі жазулардан тазалау
Comment[km]= HTTP
Comment[kn]=HTTP ಿಿ () ಿ ಿಿ
Comment[ko]=HTTP
Comment[ku]=Têketin ên kevn jê nav-bîra HTTP dibe
Comment[lb]=Entfernt al Entréen aus dem HTTP-Zwëschespäicher
Comment[lt]=Išvalo senus įrašus iš HTTP krepšio
Comment[lv]=Iztīra vecos ierakstus no HTTP kešatmiņas
Comment[mai]= िि ि
Comment[mk]=Ги брише старите работи од HTTP кешот
Comment[ml]= ി ിിി ി
Comment[mr]=HTTP
Comment[ms]=Membersihkan masukan lama daripada penyimpan HTTP
Comment[nb]=Fjerner gamle oppføringer fra hurtiglageret for HTTP
Comment[nds]=Smitt ole Indrääg ut den HTTP-Twischenspieker rut
Comment[ne]= िि
Comment[nl]=Verwijdert oude items uit de HTTP-cache
Comment[nn]=Reinskar opp i gamle oppføringar i HTTP-mellomlageret
Comment[or]=HTTP ି ି
Comment[pa]=HTTP
Comment[pl]=Usuwa stare wpisy z bufora HTTP
Comment[ps]=پټياد څخه زړې ننوتنې پاکوي HTTP د
Comment[pt]=Limpa os itens antigos da 'cache' de HTTP
Comment[pt_BR]=Limpa itens antigos do cache HTTP
Comment[ro]=Elimină înregistrările vechi din cache-ul HTTP
Comment[ru]=Удаление устаревших элементов из кэша HTTP
Comment[se]=Buhtista boares merkošiid HTTP gaskarádjosis
Comment[sk]=Vyčistí staré položky z vyrovnávacej pamäte HTTP
Comment[sl]=Počisti stare vnose iz predpomnilnika HTTP
Comment[sr]=Чисти старе ставке из ХТТП кеша
Comment[sr@ijekavian]=Чисти старе ставке из ХТТП кеша
Comment[sr@ijekavianlatin]=Čisti stare stavke iz HTTP keša
Comment[sr@latin]=Čisti stare stavke iz HTTP keša
Comment[sv]=Rensar bort gamla poster från HTTP-cachen
Comment[ta]=HTTP ிிி ி
Comment[te]= ిిి ి ి
Comment[tg]=Воридҳои кӯҳна аз HTTP cache тоза мекунад
Comment[th]= HTTP
Comment[tr]=HTTP önbelleğinden eski girdileri siler
Comment[tt]=HTTP кэшенда булган иске керемнәрне бетерү
Comment[ug]=HTTP غەملەكتىن كونا تۈرلەرنى تازىلايدۇ
Comment[uk]=Вичищає старі елементи з кешу HTTP
Comment[uz]=HTTP keshidagi eski elementlarni oʻchiradi
Comment[uz@cyrillic]=HTTP кэшидаги эски элементларни ўчиради
Comment[vi]=Xóa các mc cũ khi b đm HTTP
Comment[wa]=Neteye les viyès intrêyes del muchete HTTP
Comment[xh]=Icoca amangeno amadala asuka kwindawo efihlakeleyo yokugcina ye HTTP
Comment[x-test]=xxCleans up old entries from the HTTP cachexx
Comment[zh_CN]= HTTP
Comment[zh_HK]= HTTP
Comment[zh_TW]= HTTP
X-KDE-StartupNotify=false

View file

@ -1,927 +0,0 @@
/* This file is part of the KDE libraries
Copyright (C) 2008, 2009 Andreas Hartmetz <ahartmetz@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "httpauthentication.h"
#ifdef HAVE_LIBGSSAPI
#ifdef GSSAPI_MIT
#include <gssapi/gssapi.h>
#else
#include <gssapi.h>
#endif /* GSSAPI_MIT */
// Catch uncompatible crap (BR86019)
#if defined(GSS_RFC_COMPLIANT_OIDS) && (GSS_RFC_COMPLIANT_OIDS == 0)
#include <gssapi/gssapi_generic.h>
#define GSS_C_NT_HOSTBASED_SERVICE gss_nt_service_name
#endif
#endif /* HAVE_LIBGSSAPI */
#include <krandom.h>
#include <kdebug.h>
#include <klocale.h>
#include <kglobal.h>
#include <kconfiggroup.h>
#include <kio/authinfo.h>
#include <misc/kntlm/kntlm.h>
#include <QtCore/QTextCodec>
#include <QCryptographicHash>
static bool isWhiteSpace(char ch)
{
return (ch == ' ' || ch == '\t' || ch == '\v' || ch == '\f');
}
static bool isWhiteSpaceOrComma(char ch)
{
return (ch == ',' || isWhiteSpace(ch));
}
static bool containsScheme(const char input[], int start, int end)
{
// skip any comma or white space
while (start < end && isWhiteSpaceOrComma(input[start])) {
start++;
}
while (start < end) {
if (isWhiteSpace(input[start])) {
return true;
}
start++;
}
return false;
}
// keys on even indexes, values on odd indexes. Reduces code expansion for the templated
// alternatives.
// If "ba" starts with empty content it will be removed from ba to simplify later calls
static QList<QByteArray> parseChallenge(QByteArray &ba, QByteArray *scheme, QByteArray* nextAuth = 0)
{
QList<QByteArray> values;
const char *b = ba.constData();
int len = ba.count();
int start = 0, end = 0, pos = 0, pos2 = 0;
// parse scheme
while (start < len && isWhiteSpaceOrComma(b[start])) {
start++;
}
end = start;
while (end < len && !isWhiteSpace(b[end])) {
end++;
}
// drop empty stuff from the given string, it would have to be skipped over and over again
if (start != 0) {
ba = ba.mid(start);
end -= start;
len -= start;
start = 0;
b = ba.constData();
}
Q_ASSERT(scheme);
*scheme = ba.left(end);
while (end < len) {
start = end;
while (end < len && b[end] != '=') {
end++;
}
pos = end; // save the end position
while (end - 1 > start && isWhiteSpace(b[end - 1])) { // trim whitespace
end--;
}
pos2 = start;
while (pos2 < end && isWhiteSpace(b[pos2])) { // skip whitespace
pos2++;
}
if (containsScheme(b, start, end) || (b[pos2] == ',' && b[pos] != '=' && pos == len)) {
if (nextAuth) {
*nextAuth = QByteArray (b + start);
}
break; // break on start of next scheme.
}
while (start < len && isWhiteSpaceOrComma(b[start])) {
start++;
}
values.append(QByteArray (b + start, end - start));
end = pos; // restore the end position
if (end == len) {
break;
}
// parse value
start = end + 1; //skip '='
while (start < len && isWhiteSpace(b[start])) {
start++;
}
if (b[start] == '"') {
//quoted string
bool hasBs = false;
bool hasErr = false;
end = ++start;
while (end < len) {
if (b[end] == '\\') {
end++;
if (end + 1 >= len) {
hasErr = true;
break;
} else {
hasBs = true;
end++;
}
} else if (b[end] == '"') {
break;
} else {
end++;
}
}
if (hasErr || (end == len)) {
// remove the key we already inserted
kDebug(7113) << "error in quoted text for key" << values.last();
values.removeLast();
break;
}
QByteArray value = QByteArray(b + start, end - start);
if (hasBs) {
// skip over the next character, it might be an escaped backslash
int i = -1;
while ( (i = value.indexOf('\\', i + 1)) >= 0 ) {
value.remove(i, 1);
}
}
values.append(value);
end++;
} else {
//unquoted string
end = start;
while (end < len && b[end] != ',' && !isWhiteSpace(b[end])) {
end++;
}
values.append(QByteArray(b + start, end - start));
}
//the quoted string has ended, but only a comma ends a key-value pair
while (end < len && isWhiteSpace(b[end])) {
end++;
}
// garbage, here should be end or field delimiter (comma)
if (end < len && b[end] != ',') {
kDebug(7113) << "unexpected character" << b[end] << "found in WWW-authentication header where token boundary (,) was expected";
break;
}
}
// ensure every key has a value
// WARNING: Do not remove the > 1 check or parsing a Type 1 NTLM
// authentication challenge will surely fail.
if (values.count() > 1 && values.count() % 2) {
values.removeLast();
}
return values;
}
static QByteArray valueForKey(const QList<QByteArray> &ba, const QByteArray &key)
{
for (int i = 0, count = ba.count(); (i + 1) < count; i += 2) {
if (ba[i] == key) {
return ba[i + 1];
}
}
return QByteArray();
}
KAbstractHttpAuthentication::KAbstractHttpAuthentication(KConfigGroup *config)
:m_config(config), m_finalAuthStage(false)
{
reset();
}
KAbstractHttpAuthentication::~KAbstractHttpAuthentication()
{
}
QByteArray KAbstractHttpAuthentication::bestOffer(const QList<QByteArray> &offers)
{
// choose the most secure auth scheme offered
QByteArray negotiateOffer;
QByteArray digestOffer;
QByteArray ntlmOffer;
QByteArray basicOffer;
Q_FOREACH (const QByteArray &offer, offers) {
const QByteArray scheme = offer.mid(0, offer.indexOf(' ')).toLower();
#ifdef HAVE_LIBGSSAPI
if (scheme == "negotiate") { // krazy:exclude=strings
negotiateOffer = offer;
} else
#endif
if (scheme == "digest") { // krazy:exclude=strings
digestOffer = offer;
} else if (scheme == "ntlm") { // krazy:exclude=strings
ntlmOffer = offer;
} else if (scheme == "basic") { // krazy:exclude=strings
basicOffer = offer;
}
}
if (!negotiateOffer.isEmpty()) {
return negotiateOffer;
}
if (!digestOffer.isEmpty()) {
return digestOffer;
}
if (!ntlmOffer.isEmpty()) {
return ntlmOffer;
}
return basicOffer; //empty or not...
}
KAbstractHttpAuthentication *KAbstractHttpAuthentication::newAuth(const QByteArray &offer, KConfigGroup* config)
{
const QByteArray scheme = offer.mid(0, offer.indexOf(' ')).toLower();
#ifdef HAVE_LIBGSSAPI
if (scheme == "negotiate") { // krazy:exclude=strings
return new KHttpNegotiateAuthentication(config);
} else
#endif
if (scheme == "digest") { // krazy:exclude=strings
return new KHttpDigestAuthentication();
} else if (scheme == "ntlm") { // krazy:exclude=strings
return new KHttpNtlmAuthentication(config);
} else if (scheme == "basic") { // krazy:exclude=strings
return new KHttpBasicAuthentication();
}
return 0;
}
QList< QByteArray > KAbstractHttpAuthentication::splitOffers(const QList< QByteArray >& offers)
{
// first detect if one entry may contain multiple offers
QList<QByteArray> alloffers;
foreach(QByteArray offer, offers) {
QByteArray scheme, cont;
parseChallenge(offer, &scheme, &cont);
while (!cont.isEmpty()) {
offer.chop(cont.length());
alloffers << offer;
offer = cont;
cont.clear();
parseChallenge(offer, &scheme, &cont);
}
alloffers << offer;
}
return alloffers;
}
void KAbstractHttpAuthentication::reset()
{
m_scheme.clear();
m_challenge.clear();
m_challengeText.clear();
m_resource.clear();
m_httpMethod.clear();
m_isError = false;
m_needCredentials = true;
m_forceKeepAlive = false;
m_forceDisconnect = false;
m_keepPassword = false;
m_headerFragment.clear();
m_username.clear();
m_password.clear();
}
void KAbstractHttpAuthentication::setChallenge(const QByteArray &c, const KUrl &resource,
const QByteArray &httpMethod)
{
reset();
m_challengeText = c.trimmed();
m_challenge = parseChallenge(m_challengeText, &m_scheme);
Q_ASSERT(m_scheme.toLower() == scheme().toLower());
m_resource = resource;
m_httpMethod = httpMethod;
}
QString KAbstractHttpAuthentication::realm() const
{
const QByteArray realm = valueForKey(m_challenge, "realm");
// TODO: Find out what this is supposed to address. The site mentioned below does not exist.
if (KGlobal::locale()->language().contains(QLatin1String("ru"))) {
//for sites like lib.homelinux.org
return QTextCodec::codecForName("CP1251")->toUnicode(realm);
}
return QString::fromLatin1(realm.constData(), realm.length());
}
void KAbstractHttpAuthentication::authInfoBoilerplate(KIO::AuthInfo *a) const
{
a->url = m_resource;
a->username = m_username;
a->password = m_password;
a->verifyPath = supportsPathMatching();
a->realmValue = realm();
a->digestInfo = QLatin1String(authDataToCache());
a->keepPassword = m_keepPassword;
}
void KAbstractHttpAuthentication::generateResponseCommon(const QString &user, const QString &password)
{
if (m_scheme.isEmpty() || m_httpMethod.isEmpty()) {
m_isError = true;
return;
}
if (m_needCredentials) {
m_username = user;
m_password = password;
}
m_isError = false;
m_forceKeepAlive = false;
m_forceDisconnect = false;
m_finalAuthStage = true;
}
QByteArray KHttpBasicAuthentication::scheme() const
{
return "Basic";
}
void KHttpBasicAuthentication::fillKioAuthInfo(KIO::AuthInfo *ai) const
{
authInfoBoilerplate(ai);
}
void KHttpBasicAuthentication::generateResponse(const QString &user, const QString &password)
{
generateResponseCommon(user, password);
if (m_isError) {
return;
}
m_headerFragment = "Basic ";
m_headerFragment += QByteArray(m_username.toLatin1() + ':' + m_password.toLatin1()).toBase64();
m_headerFragment += "\r\n";
}
QByteArray KHttpDigestAuthentication::scheme() const
{
return "Digest";
}
void KHttpDigestAuthentication::setChallenge(const QByteArray &c, const KUrl &resource,
const QByteArray &httpMethod)
{
QString oldUsername;
QString oldPassword;
if (valueForKey(m_challenge, "stale").toLower() == "true") {
// stale nonce: the auth failure that triggered this round of authentication is an artifact
// of digest authentication. the credentials are probably still good, so keep them.
oldUsername = m_username;
oldPassword = m_password;
}
KAbstractHttpAuthentication::setChallenge(c, resource, httpMethod);
if (!oldUsername.isEmpty() && !oldPassword.isEmpty()) {
// keep credentials *and* don't ask for new ones
m_needCredentials = false;
m_username = oldUsername;
m_password = oldPassword;
}
}
void KHttpDigestAuthentication::fillKioAuthInfo(KIO::AuthInfo *ai) const
{
authInfoBoilerplate(ai);
}
struct DigestAuthInfo
{
QByteArray nc;
QByteArray qop;
QByteArray realm;
QByteArray nonce;
QByteArray method;
QByteArray cnonce;
QByteArray username;
QByteArray password;
KUrl::List digestURIs;
QByteArray algorithm;
QByteArray entityBody;
};
//calculateResponse() from the original HTTPProtocol
static QByteArray calculateResponse(const DigestAuthInfo &info, const KUrl &resource)
{
QCryptographicHash md(QCryptographicHash::Md5);
QByteArray HA1;
QByteArray HA2;
// Calculate H(A1)
QByteArray authStr = info.username;
authStr += ':';
authStr += info.realm;
authStr += ':';
authStr += info.password;
md.addData( authStr );
if ( info.algorithm.toLower() == "md5-sess" )
{
authStr = md.result().toHex();
authStr += ':';
authStr += info.nonce;
authStr += ':';
authStr += info.cnonce;
md.reset();
md.addData( authStr );
}
HA1 = md.result().toHex();
kDebug(7113) << "A1 => " << HA1;
// Calcualte H(A2)
authStr = info.method;
authStr += ':';
authStr += resource.encodedPathAndQuery(KUrl::LeaveTrailingSlash, KUrl::AvoidEmptyPath).toLatin1();
if ( info.qop == "auth-int" )
{
authStr += ':';
md.reset();
md.addData(info.entityBody);
authStr += md.result().toHex();
}
md.reset();
md.addData( authStr );
HA2 = md.result().toHex();
kDebug(7113) << "A2 => " << HA2;
// Calcualte the response.
authStr = HA1;
authStr += ':';
authStr += info.nonce;
authStr += ':';
if ( !info.qop.isEmpty() )
{
authStr += info.nc;
authStr += ':';
authStr += info.cnonce;
authStr += ':';
authStr += info.qop;
authStr += ':';
}
authStr += HA2;
md.reset();
md.addData( authStr );
const QByteArray response = md.result().toHex();
kDebug(7113) << "Response =>" << response;
return response;
}
void KHttpDigestAuthentication::generateResponse(const QString &user, const QString &password)
{
generateResponseCommon(user, password);
if (m_isError) {
return;
}
// magic starts here (this part is slightly modified from the original in HTTPProtocol)
DigestAuthInfo info;
info.username = m_username.toLatin1(); //### charset breakage
info.password = m_password.toLatin1(); //###
// info.entityBody = p; // FIXME: send digest of data for POST action ??
info.realm = "";
info.nonce = "";
info.qop = "";
// cnonce is recommended to contain about 64 bits of entropy
#ifdef ENABLE_HTTP_AUTH_NONCE_SETTER
info.cnonce = m_nonce;
#else
info.cnonce = KRandom::randomString(16).toLatin1();
#endif
// HACK: Should be fixed according to RFC 2617 section 3.2.2
info.nc = "00000001";
// Set the method used...
info.method = m_httpMethod;
// Parse the Digest response....
info.realm = valueForKey(m_challenge, "realm");
info.algorithm = valueForKey(m_challenge, "algorithm");
if (info.algorithm.isEmpty()) {
info.algorithm = valueForKey(m_challenge, "algorith");
}
if (info.algorithm.isEmpty()) {
info.algorithm = "MD5";
}
Q_FOREACH (const QByteArray &path, valueForKey(m_challenge, "domain").split(' ')) {
KUrl u(m_resource, QString::fromLatin1(path));
if (u.isValid()) {
info.digestURIs.append(u);
}
}
info.nonce = valueForKey(m_challenge, "nonce");
QByteArray opaque = valueForKey(m_challenge, "opaque");
info.qop = valueForKey(m_challenge, "qop");
// NOTE: Since we do not have access to the entity body, we cannot support
// the "auth-int" qop value ; so if the server returns a comma separated
// list of qop values, prefer "auth".See RFC 2617 sec 3.2.2 for the details.
// If "auth" is not present or it is set to "auth-int" only, then we simply
// print a warning message and disregard the qop option altogether.
if (info.qop.contains(',')) {
const QList<QByteArray> values = info.qop.split(',');
if (info.qop.contains("auth"))
info.qop = "auth";
else {
kWarning(7113) << "Unsupported digest authentication qop parameters:" << values;
info.qop.clear();
}
} else if (info.qop == "auth-int") {
kWarning(7113) << "Unsupported digest authentication qop parameter:" << info.qop;
info.qop.clear();
}
if (info.realm.isEmpty() || info.nonce.isEmpty()) {
// ### proper error return
m_isError = true;
return;
}
// If the "domain" attribute was not specified and the current response code
// is authentication needed, add the current request url to the list over which
// this credential can be automatically applied.
if (info.digestURIs.isEmpty() /*###&& (m_request.responseCode == 401 || m_request.responseCode == 407)*/)
info.digestURIs.append (m_resource);
else
{
// Verify whether or not we should send a cached credential to the
// server based on the stored "domain" attribute...
bool send = true;
// Determine the path of the request url...
QString requestPath = m_resource.directory(KUrl::AppendTrailingSlash | KUrl::ObeyTrailingSlash);
if (requestPath.isEmpty())
requestPath = QLatin1Char('/');
Q_FOREACH (const KUrl &u, info.digestURIs)
{
send &= (m_resource.protocol().toLower() == u.protocol().toLower());
send &= (m_resource.host().toLower() == u.host().toLower());
if (m_resource.port() > 0 && u.port() > 0)
send &= (m_resource.port() == u.port());
QString digestPath = u.directory (KUrl::AppendTrailingSlash | KUrl::ObeyTrailingSlash);
if (digestPath.isEmpty())
digestPath = QLatin1Char('/');
send &= (requestPath.startsWith(digestPath));
if (send)
break;
}
if (!send) {
m_isError = true;
return;
}
}
kDebug(7113) << "RESULT OF PARSING:";
kDebug(7113) << " algorithm: " << info.algorithm;
kDebug(7113) << " realm: " << info.realm;
kDebug(7113) << " nonce: " << info.nonce;
kDebug(7113) << " opaque: " << opaque;
kDebug(7113) << " qop: " << info.qop;
// Calculate the response...
const QByteArray response = calculateResponse(info, m_resource);
QByteArray auth = "Digest username=\"";
auth += info.username;
auth += "\", realm=\"";
auth += info.realm;
auth += "\"";
auth += ", nonce=\"";
auth += info.nonce;
auth += "\", uri=\"";
auth += m_resource.encodedPathAndQuery(KUrl::LeaveTrailingSlash, KUrl::AvoidEmptyPath).toLatin1();
if (!info.algorithm.isEmpty()) {
auth += "\", algorithm=";
auth += info.algorithm;
}
if ( !info.qop.isEmpty() )
{
auth += ", qop=";
auth += info.qop;
auth += ", cnonce=\"";
auth += info.cnonce;
auth += "\", nc=";
auth += info.nc;
}
auth += ", response=\"";
auth += response;
if ( !opaque.isEmpty() )
{
auth += "\", opaque=\"";
auth += opaque;
}
auth += "\"\r\n";
// magic ends here
// note that auth already contains \r\n
m_headerFragment = auth;
}
#ifdef ENABLE_HTTP_AUTH_NONCE_SETTER
void KHttpDigestAuthentication::setDigestNonceValue(const QByteArray& nonce)
{
m_nonce = nonce;
}
#endif
QByteArray KHttpNtlmAuthentication::scheme() const
{
return "NTLM";
}
void KHttpNtlmAuthentication::setChallenge(const QByteArray &c, const KUrl &resource,
const QByteArray &httpMethod)
{
QString oldUsername, oldPassword;
if (!m_finalAuthStage && !m_username.isEmpty() && !m_password.isEmpty()) {
oldUsername = m_username;
oldPassword = m_password;
}
KAbstractHttpAuthentication::setChallenge(c, resource, httpMethod);
if (!oldUsername.isEmpty() && !oldPassword.isEmpty()) {
m_username = oldUsername;
m_password = oldPassword;
}
// The type 1 message we're going to send needs no credentials;
// they come later in the type 3 message.
m_needCredentials = m_challenge.isEmpty();
}
void KHttpNtlmAuthentication::fillKioAuthInfo(KIO::AuthInfo *ai) const
{
authInfoBoilerplate(ai);
// Every auth scheme is supposed to supply a realm according to the RFCs. Of course this doesn't
// prevent Microsoft from not doing it... Dummy value!
// we don't have the username yet which may (may!) contain a domain, so we really have no choice
ai->realmValue = QLatin1String("NTLM");
}
void KHttpNtlmAuthentication::generateResponse(const QString &_user, const QString &password)
{
generateResponseCommon(_user, password);
if (m_isError) {
return;
}
QByteArray buf;
if (m_challenge.isEmpty()) {
m_finalAuthStage = false;
// first, send type 1 message (with empty domain, workstation..., but it still works)
if (!KNTLM::getNegotiate(buf)) {
kWarning(7113) << "Error while constructing Type 1 NTLM authentication request";
m_isError = true;
return;
}
} else {
m_finalAuthStage = true;
// we've (hopefully) received a valid type 2 message: send type 3 message as last step
QString user, domain;
if (m_username.contains(QLatin1Char('\\'))) {
domain = m_username.section(QLatin1Char('\\'), 0, 0);
user = m_username.section(QLatin1Char('\\'), 1);
} else {
user = m_username;
}
m_forceKeepAlive = true;
const QByteArray challenge = QByteArray::fromBase64(m_challenge[0]);
KNTLM::AuthFlags flags = KNTLM::Add_LM;
if (!m_config || !m_config->readEntry("EnableNTLMv2Auth", false)) {
flags |= KNTLM::Force_V1;
}
if (!KNTLM::getAuth(buf, challenge, user, m_password, domain, QLatin1String("WORKSTATION"), flags)) {
kWarning(7113) << "Error while constructing Type 3 NTLM authentication request";
m_isError = true;
return;
}
}
m_headerFragment = "NTLM ";
m_headerFragment += buf.toBase64();
m_headerFragment += "\r\n";
return;
}
//////////////////////////
#ifdef HAVE_LIBGSSAPI
// just an error message formatter
static QByteArray gssError(int major_status, int minor_status)
{
OM_uint32 new_status;
OM_uint32 msg_ctx = 0;
gss_buffer_desc major_string;
gss_buffer_desc minor_string;
OM_uint32 ret;
QByteArray errorstr;
do {
ret = gss_display_status(&new_status, major_status, GSS_C_GSS_CODE, GSS_C_NULL_OID, &msg_ctx, &major_string);
errorstr += (const char *)major_string.value;
errorstr += ' ';
ret = gss_display_status(&new_status, minor_status, GSS_C_MECH_CODE, GSS_C_NULL_OID, &msg_ctx, &minor_string);
errorstr += (const char *)minor_string.value;
errorstr += ' ';
} while (!GSS_ERROR(ret) && msg_ctx != 0);
return errorstr;
}
QByteArray KHttpNegotiateAuthentication::scheme() const
{
return "Negotiate";
}
void KHttpNegotiateAuthentication::setChallenge(const QByteArray &c, const KUrl &resource,
const QByteArray &httpMethod)
{
KAbstractHttpAuthentication::setChallenge(c, resource, httpMethod);
// GSSAPI knows how to get the credentials on its own
m_needCredentials = false;
}
void KHttpNegotiateAuthentication::fillKioAuthInfo(KIO::AuthInfo *ai) const
{
authInfoBoilerplate(ai);
//### does GSSAPI supply anything realm-like? dummy value for now.
ai->realmValue = QLatin1String("Negotiate");
}
void KHttpNegotiateAuthentication::generateResponse(const QString &user, const QString &password)
{
generateResponseCommon(user, password);
if (m_isError) {
return;
}
OM_uint32 major_status, minor_status;
gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
gss_name_t server;
gss_ctx_id_t ctx;
gss_OID mech_oid;
static gss_OID_desc krb5_oid_desc = {9, (void *) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02"};
static gss_OID_desc spnego_oid_desc = {6, (void *) "\x2b\x06\x01\x05\x05\x02"};
gss_OID_set mech_set;
gss_OID tmp_oid;
ctx = GSS_C_NO_CONTEXT;
mech_oid = &krb5_oid_desc;
// see whether we can use the SPNEGO mechanism
major_status = gss_indicate_mechs(&minor_status, &mech_set);
if (GSS_ERROR(major_status)) {
kDebug(7113) << "gss_indicate_mechs failed: " << gssError(major_status, minor_status);
} else {
for (uint i = 0; i < mech_set->count; i++) {
tmp_oid = &mech_set->elements[i];
if (tmp_oid->length == spnego_oid_desc.length &&
!memcmp(tmp_oid->elements, spnego_oid_desc.elements, tmp_oid->length)) {
kDebug(7113) << "found SPNEGO mech";
mech_oid = &spnego_oid_desc;
break;
}
}
gss_release_oid_set(&minor_status, &mech_set);
}
// the service name is "HTTP/f.q.d.n"
QByteArray servicename = "HTTP@";
servicename += m_resource.host().toLatin1();
input_token.value = (void *)servicename.data();
input_token.length = servicename.length() + 1;
major_status = gss_import_name(&minor_status, &input_token,
GSS_C_NT_HOSTBASED_SERVICE, &server);
input_token.value = NULL;
input_token.length = 0;
if (GSS_ERROR(major_status)) {
kDebug(7113) << "gss_import_name failed: " << gssError(major_status, minor_status);
m_isError = true;
return;
}
OM_uint32 req_flags;
if (m_config && m_config->readEntry("DelegateCredentialsOn", false))
req_flags = GSS_C_DELEG_FLAG;
else
req_flags = 0;
// GSSAPI knows how to get the credentials its own way, so don't ask for any
major_status = gss_init_sec_context(&minor_status, GSS_C_NO_CREDENTIAL,
&ctx, server, mech_oid,
req_flags, GSS_C_INDEFINITE,
GSS_C_NO_CHANNEL_BINDINGS,
GSS_C_NO_BUFFER, NULL, &output_token,
NULL, NULL);
if (GSS_ERROR(major_status) || (output_token.length == 0)) {
kDebug(7113) << "gss_init_sec_context failed: " << gssError(major_status, minor_status);
gss_release_name(&minor_status, &server);
if (ctx != GSS_C_NO_CONTEXT) {
gss_delete_sec_context(&minor_status, &ctx, GSS_C_NO_BUFFER);
ctx = GSS_C_NO_CONTEXT;
}
m_isError = true;
return;
}
m_headerFragment = "Negotiate ";
m_headerFragment += QByteArray::fromRawData(static_cast<const char *>(output_token.value),
output_token.length).toBase64();
m_headerFragment += "\r\n";
// free everything
gss_release_name(&minor_status, &server);
if (ctx != GSS_C_NO_CONTEXT) {
gss_delete_sec_context(&minor_status, &ctx, GSS_C_NO_BUFFER);
ctx = GSS_C_NO_CONTEXT;
}
gss_release_buffer(&minor_status, &output_token);
}
#endif // HAVE_LIBGSSAPI

View file

@ -1,251 +0,0 @@
/* This file is part of the KDE libraries
Copyright (C) 2008, 2009 Andreas Hartmetz <ahartmetz@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef HTTPAUTHENTICATION_H
#define HTTPAUTHENTICATION_H
#include <config-gssapi.h>
#include <kurl.h>
#include <QtCore/QByteArray>
#include <QtCore/QString>
#include <QtCore/QList>
namespace KIO {
class AuthInfo;
}
class KConfigGroup;
class KAbstractHttpAuthentication
{
public:
KAbstractHttpAuthentication(KConfigGroup *config = 0);
virtual ~KAbstractHttpAuthentication();
/**
* Choose the best authentication mechanism from the offered ones
*
* This will return the most secure mechanism from the list of
* mechanisms retuned by the server.
*/
static QByteArray bestOffer(const QList<QByteArray> &offers);
/**
* Returns authentication object instance appropriate for @p offer.
*
* @param offer the header from which an authentication object is created.
* @param config the config object to read stored authentication information.
*/
static KAbstractHttpAuthentication *newAuth(const QByteArray &offer, KConfigGroup *config = 0);
/**
* Split all headers containing multiple authentication offers.
*
* @param offers the offers from multiple HTTP authentication header lines.
* @return a list where each entry contains only a single offer
*/
static QList<QByteArray> splitOffers(const QList<QByteArray> &offers);
/**
* reset to state after default construction.
*/
void reset();
/**
* the authentication scheme: "Negotiate", "Digest", "Basic", "NTLM"
*/
virtual QByteArray scheme() const = 0;
/**
* initiate authentication with challenge string (from HTTP header)
*/
virtual void setChallenge(const QByteArray &c, const KUrl &resource, const QByteArray &httpMethod);
/**
* return value updated by setChallenge()
*
* if this is false user and password passed to generateResponse
* will be ignored and may be empty.
*/
bool needCredentials() const { return m_needCredentials; }
/**
* KIO compatible data to find cached credentials.
*
* Note that username and/or password as well as UI text will NOT be filled in.
*/
virtual void fillKioAuthInfo(KIO::AuthInfo *ai) const = 0;
/**
* what to do in response to challenge
*/
virtual void generateResponse(const QString &user,
const QString &password) = 0;
/**
* returns true when the final stage of authentication is reached.
*
* Unless the authentication scheme requires multiple stages like NTLM this
* function will always return true.
*/
bool wasFinalStage() const { return m_finalAuthStage; }
/**
* Returns true if the authentication scheme supports path matching to identify
* resources that belong to the same protection space (realm).
*
* See RFC 2617.
*/
virtual bool supportsPathMatching() const { return false; }
// the following accessors return useful data after generateResponse() has been called.
// clients process the following fields top to bottom: highest priority is on top
// malformed challenge and similar problems - it is advisable to reconnect
bool isError() const { return m_isError; }
/**
* force keep-alive connection because the authentication method requires it
*/
bool forceKeepAlive() const { return m_forceKeepAlive; }
/**
* force disconnection because the authentication method requires it
*/
bool forceDisconnect() const { return m_forceDisconnect; }
/**
* insert this into the next request header after "Authorization: "
* or "Proxy-Authorization: "
*/
QByteArray headerFragment() const { return m_headerFragment; }
/**
* Returns the realm sent by the server.
*
* This is mainly for GUI shown to the user. This is the identification of
* the protected area on the server (e.g. "Konquis home directory" or
* "KDE files").
*/
QString realm() const;
/**
* Sets the cache password flag to @p enable.
*/
void setCachePasswordEnabled(bool enable) { m_keepPassword = enable; }
#ifdef ENABLE_HTTP_AUTH_NONCE_SETTER
// NOTE: FOR USE in unit testing ONLY.
virtual void setDigestNonceValue(const QByteArray&) {}
#endif
protected:
void authInfoBoilerplate(KIO::AuthInfo *a) const;
/**
* Returns any authentication data that should be cached for future use.
*
* NOTE: Do not reimplement this function for connection based authentication
* schemes such as NTLM.
*/
virtual QByteArray authDataToCache() const { return QByteArray(); }
void generateResponseCommon(const QString &user, const QString &password);
KConfigGroup *m_config;
QByteArray m_scheme; ///< this is parsed from the header and not necessarily == scheme().
QByteArray m_challengeText;
QList<QByteArray> m_challenge;
KUrl m_resource;
QByteArray m_httpMethod;
bool m_isError;
bool m_needCredentials;
bool m_forceKeepAlive;
bool m_forceDisconnect;
bool m_finalAuthStage;
bool m_keepPassword;
QByteArray m_headerFragment;
QString m_username;
QString m_password;
};
class KHttpBasicAuthentication : public KAbstractHttpAuthentication
{
public:
virtual QByteArray scheme() const;
virtual void fillKioAuthInfo(KIO::AuthInfo *ai) const;
virtual void generateResponse(const QString &user, const QString &password);
virtual bool supportsPathMatching() const { return true; }
protected:
virtual QByteArray authDataToCache() const { return m_challengeText; }
private:
friend class KAbstractHttpAuthentication;
KHttpBasicAuthentication(KConfigGroup *config = 0)
: KAbstractHttpAuthentication(config) {}
};
class KHttpDigestAuthentication : public KAbstractHttpAuthentication
{
public:
virtual QByteArray scheme() const;
virtual void setChallenge(const QByteArray &c, const KUrl &resource, const QByteArray &httpMethod);
virtual void fillKioAuthInfo(KIO::AuthInfo *ai) const;
virtual void generateResponse(const QString &user, const QString &password);
virtual bool supportsPathMatching() const { return true; }
#ifdef ENABLE_HTTP_AUTH_NONCE_SETTER
virtual void setDigestNonceValue(const QByteArray&);
#endif
protected:
virtual QByteArray authDataToCache() const { return m_challengeText; }
private:
friend class KAbstractHttpAuthentication;
KHttpDigestAuthentication(KConfigGroup *config = 0)
: KAbstractHttpAuthentication(config) {}
#ifdef ENABLE_HTTP_AUTH_NONCE_SETTER
QByteArray m_nonce;
#endif
};
class KHttpNtlmAuthentication : public KAbstractHttpAuthentication
{
public:
virtual QByteArray scheme() const;
virtual void setChallenge(const QByteArray &c, const KUrl &resource, const QByteArray &httpMethod);
virtual void fillKioAuthInfo(KIO::AuthInfo *ai) const;
virtual void generateResponse(const QString &user, const QString &password);
private:
friend class KAbstractHttpAuthentication;
KHttpNtlmAuthentication(KConfigGroup *config = 0)
: KAbstractHttpAuthentication(config) {}
};
#ifdef HAVE_LIBGSSAPI
class KHttpNegotiateAuthentication : public KAbstractHttpAuthentication
{
public:
virtual QByteArray scheme() const;
virtual void setChallenge(const QByteArray &c, const KUrl &resource, const QByteArray &httpMethod);
virtual void fillKioAuthInfo(KIO::AuthInfo *ai) const;
virtual void generateResponse(const QString &user, const QString &password);
private:
friend class KAbstractHttpAuthentication;
KHttpNegotiateAuthentication(KConfigGroup *config = 0)
: KAbstractHttpAuthentication(config) {}
};
#endif // HAVE_LIBGSSAPI
#endif // HTTPAUTHENTICATION_H

View file

@ -3,14 +3,19 @@ exec=kio_http
protocol=https
input=none
output=filesystem
copyToFile=false
copyFromFile=false
listing=
reading=true
deleting=true
writing=true
defaultMimetype=application/octet-stream
determineMimetypeFromExtension=false
makedir=false
deleting=false
moving=false
ProxiedBy=http
Icon=text-html
maxInstances=20
maxInstancesPerHost=5
config=http
defaultMimetype=application/octet-stream
determineMimetypeFromExtension=false
X-DocPath=kioslave/http/index.html
Class=:internet

View file

@ -1,62 +0,0 @@
if(ENABLE_TESTING)
add_subdirectory(tests)
endif()
####### kcookiejar4: command line tool for talking to the kded kcookiejar module #######
set(kcookiejar_SRCS
main.cpp
)
qt4_generate_dbus_interface(${CMAKE_SOURCE_DIR}/kded/kdedadaptor.h org.kde.kded.xml )
qt4_add_dbus_interfaces(kcookiejar_SRCS ${CMAKE_CURRENT_BINARY_DIR}/org.kde.kded.xml)
qt4_generate_dbus_interface(kcookieserver.h org.kde.KCookieServer.xml)
set(kcookieserver_xml ${CMAKE_CURRENT_BINARY_DIR}/org.kde.KCookieServer.xml)
set_source_files_properties(${kcookieserver_xml} PROPERTIES
INCLUDE "kcookiejar_include.h"
)
qt4_add_dbus_interfaces(kcookiejar_SRCS ${kcookieserver_xml})
add_executable(kcookiejar4 ${kcookiejar_SRCS})
target_link_libraries(kcookiejar4 ${KDE4_KDECORE_LIBS})
install(TARGETS kcookiejar4 ${INSTALL_TARGETS_DEFAULT_ARGS} )
########### kded kcookiejar module ###############
set(kded_kcookiejar_SRCS
kcookiejar.cpp
kcookieserver.cpp
kcookiewin.cpp
)
qt4_add_dbus_adaptor(kded_kcookiejar_SRCS
${CMAKE_CURRENT_BINARY_DIR}/org.kde.KCookieServer.xml
kcookieserver.h
KCookieServer
)
kde4_add_plugin(kded_kcookiejar ${kded_kcookiejar_SRCS})
target_link_libraries(kded_kcookiejar ${KDE4_KIO_LIBS} ${X11_LIBRARIES})
install(TARGETS kded_kcookiejar DESTINATION ${KDE4_PLUGIN_INSTALL_DIR})
########### install files ###############
install(
FILES
kcookiejar.desktop
DESTINATION ${KDE4_SERVICES_INSTALL_DIR}/kded
)
install(
FILES
${CMAKE_CURRENT_BINARY_DIR}/org.kde.KCookieServer.xml
DESTINATION ${KDE4_DBUS_INTERFACES_INSTALL_DIR}
)

File diff suppressed because it is too large Load diff

View file

@ -1,137 +0,0 @@
[Desktop Entry]
Type=Service
Name=Cookie Jar
Name[ar]=كعكة Jar
Name[bg]=Буркан за бисквитки
Name[bn]=ি
Name[bs]=Kolekcija kolačića
Name[ca]=Repositori de galetes
Name[ca@valencia]=Repositori de galetes
Name[cs]=Cookie Jar
Name[da]=Cookie Jar
Name[de]=Cookie-Behälter
Name[el]=Cookie Jar
Name[en_GB]=Cookie Jar
Name[es]=Almacén de cookies
Name[et]=Küpsisekarp
Name[eu]=Cookie ontzia
Name[fa]=ظرف کوکی
Name[fi]=Evästeiden säilytys
Name[fr]=Boîte à cookies
Name[ga]=Crúiscín Fianán
Name[gl]=Bote de cookies
Name[gu]=
Name[he]=צנצנת עוגיות
Name[hi]=
Name[hr]=Cookie Jar
Name[hu]=Cookie-tároló
Name[hy]=Քուքի բանկա
Name[ia]=Cookie Jar
Name[id]=Jar Kuki
Name[is]=Smákökukrukka
Name[it]=Modulo Jar dei cookie
Name[ja]= Jar
Name[kk]=Cookie сауыты
Name[km]= Jar
Name[ko]=
Name[ku]=Cookie Jar
Name[lv]=Sīkdatņu trauks
Name[mr]=
Name[nb]=Lager for informasjonskapsler
Name[nds]=Kookjepleeg
Name[nl]=Cookie-depot
Name[pa]=
Name[pl]=Przechowywanie ciasteczek
Name[pt]=Repositório de 'Cookies'
Name[pt_BR]=Repositório de cookies
Name[ro]=Modul Cookie JAR
Name[ru]=Служба cookie
Name[se]=Diehtočoahkorádju
Name[sk]=Úložisko cookie
Name[sl]=Posoda s piškotki
Name[sq]=Kavanozi i biskotave
Name[sr]=Тегла за колачиће
Name[sr@ijekavian]=Тегла за колачиће
Name[sr@ijekavianlatin]=Tegla za kolačiće
Name[sr@latin]=Tegla za kolačiće
Name[sv]=Kakburk
Name[ta]=Cookie Jar
Name[tg]=Cookie Jar
Name[th]=
Name[tr]=Cookie Jar
Name[tt]=Cookie Jar
Name[ug]=Cookie Jar
Name[uk]=Глечик з куками
Name[wa]=Coûke Jar
Name[x-test]=xxCookie Jarxx
Name[zh_CN]=Cookie Jar
Name[zh_TW]=Cookie Jar
Comment=Stores network cookies
Comment[ar]=يخزن كعكات (cookies) الشبكة
Comment[bg]=Съхранява мрежовите бисквитки
Comment[bs]=Skladišti mrežne kolačiće
Comment[ca]=Emmagatzema les galetes de la xarxa
Comment[ca@valencia]=Emmagatzema les galetes de la xarxa
Comment[cs]=Ukládá síťová cookie
Comment[da]=Gemmer netværkscookies
Comment[de]=Netzwerk-Cookies speichern
Comment[el]=Αποθήκευση δικτυακών cookies
Comment[en_GB]=Stores network cookies
Comment[es]=Almacena cookies de la red
Comment[et]=Võrguküpsiste salvestamine
Comment[eu]=Sareko cookie-ak biltegiratzen ditu
Comment[fi]=Säilyttää verkkoevästeitä
Comment[fr]=Enregistre les cookies réseau
Comment[ga]=Stórálann sé seo fianáin líonra
Comment[gl]=Garda cookies da rede
Comment[gu]=
Comment[he]=שומר עוגיות רשת
Comment[hi]=
Comment[hr]=Sprema mrežne kolačiće
Comment[hu]=Hálózati sütik tárolása
Comment[hy]=Պահում է ցանցի քուքիները
Comment[ia]=Il immagazina le cookies de rete
Comment[id]=Simpan kuki jaringan
Comment[is]=Geymir smákökur af netum
Comment[it]=Salva cookie di rete
Comment[ja]=
Comment[kk]=Желі cookie сақтайтын орын
Comment[km]=
Comment[ko]=
Comment[ku]=Çerezên torê tomar dike
Comment[lv]=Galbā tīkla sīkdatnes
Comment[mr]=
Comment[nb]=Lagrer informasjonskapsler fra nettverket
Comment[nds]=Wohrt Nettwark-Kookjes
Comment[nl]=Slaat netwerk-cookies op
Comment[pa]=
Comment[pl]=Przechowuje ciasteczka sieciowe
Comment[pt]=Guarda os 'cookies' da rede
Comment[pt_BR]=Armazena os cookies da rede
Comment[ro]=Stochează cookie-uri de rețea
Comment[ru]=Сохранение сетевых cookie
Comment[se]=Vurke fierpmádatdiehtočoahkuid
Comment[sk]=Ukladá sieťové cookies
Comment[sl]=Hrani omrežne piškotke
Comment[sr]=Складишти мрежне колачиће
Comment[sr@ijekavian]=Складишти мрежне колачиће
Comment[sr@ijekavianlatin]=Skladišti mrežne kolačiće
Comment[sr@latin]=Skladišti mrežne kolačiće
Comment[sv]=Lagrar nätverkskakor
Comment[ta]=ி ிி ிி
Comment[tg]=Нигоҳдории хусиятҳои шабака
Comment[th]=
Comment[tr]=Ağ çerezlerini saklar
Comment[tt]=Челтәрле cookies'ны саклау
Comment[ug]=تور cookie نى ساقلايدۇ
Comment[uk]=Зберігання кук мережі
Comment[vi]=Lưu cookies mng
Comment[wa]=Wåde les coûkes del rantoele
Comment[x-test]=xxStores network cookiesxx
Comment[zh_CN]= cookie
Comment[zh_TW]= cookie
X-KDE-ServiceTypes=KDEDModule
X-KDE-Library=kcookiejar
X-KDE-DBus-ModuleName=kcookiejar
X-KDE-Kded-autoload=false
X-KDE-Kded-load-on-demand=true

View file

@ -1,394 +0,0 @@
/*
This file is part of the KDE File Manager
Copyright (C) 1998 Waldo Bastian (bastian@kde.org)
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public License
as published by the Free Software Foundation; either
version 2, or (at your option) version 3.
This software is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this library; see the file COPYING. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
//----------------------------------------------------------------------------
//
// KDE File Manager -- HTTP Cookies
#ifndef KCOOKIEJAR_H
#define KCOOKIEJAR_H
#include <QtCore/QString>
#include <QtCore/QStringList>
#include <QtCore/QHash>
#include <QtCore/QSet>
class KConfig;
class KCookieJar;
class KHttpCookie;
class KHttpCookieList;
typedef KHttpCookie *KHttpCookiePtr;
enum KCookieAdvice
{
KCookieDunno=0,
KCookieAccept,
KCookieAcceptForSession,
KCookieReject,
KCookieAsk
};
class KHttpCookie
{
friend class KCookieJar;
friend class KHttpCookieList;
friend QDebug operator<<(QDebug, const KHttpCookie&); // for cookieStr()
protected:
QString mHost;
QString mDomain;
QString mPath;
QString mName;
QString mValue;
qint64 mExpireDate;
int mProtocolVersion;
bool mSecure;
bool mCrossDomain;
bool mHttpOnly;
bool mExplicitPath;
QList<long> mWindowIds;
QList<int> mPorts;
KCookieAdvice mUserSelectedAdvice;
QString cookieStr(bool useDOMFormat) const;
public:
explicit KHttpCookie(const QString &_host=QString(),
const QString &_domain=QString(),
const QString &_path=QString(),
const QString &_name=QString(),
const QString &_value=QString(),
qint64 _expireDate=0,
int _protocolVersion=0,
bool _secure = false,
bool _httpOnly = false,
bool _explicitPath = false);
QString domain() const { return mDomain; }
QString host() const { return mHost; }
QString path() const { return mPath; }
QString name() const { return mName; }
QString value() const { return mValue; }
QList<long> &windowIds() { return mWindowIds; }
const QList<long> &windowIds() const { return mWindowIds; }
const QList<int> &ports() const { return mPorts; }
void fixDomain(const QString &domain) { mDomain = domain; }
qint64 expireDate() const { return mExpireDate; }
int protocolVersion() const { return mProtocolVersion; }
bool isSecure() const { return mSecure; }
/**
* If currentDate is -1, the default, then the current timestamp in UTC
* is used for comparison against this cookie's expiration date.
*/
bool isExpired(qint64 currentDate = -1) const;
bool isCrossDomain() const { return mCrossDomain; }
bool isHttpOnly() const { return mHttpOnly; }
bool hasExplicitPath() const { return mExplicitPath; }
bool match(const QString &fqdn, const QStringList &domainList, const QString &path, int port=-1) const;
KCookieAdvice getUserSelectedAdvice() const { return mUserSelectedAdvice; }
void setUserSelectedAdvice(KCookieAdvice advice) { mUserSelectedAdvice = advice; }
};
QDebug operator<<(QDebug, const KHttpCookie&);
class KHttpCookieList : public QList<KHttpCookie>
{
public:
KHttpCookieList() : QList<KHttpCookie>(), advice( KCookieDunno )
{ }
virtual ~KHttpCookieList() { }
KCookieAdvice getAdvice() const { return advice; }
void setAdvice(KCookieAdvice _advice) { advice = _advice; }
private:
KCookieAdvice advice;
};
QDebug operator<<(QDebug, const KHttpCookieList&);
class KCookieJar
{
public:
/**
* Constructs a new cookie jar
*
* One jar should be enough for all cookies.
*/
KCookieJar();
/**
* Destructs the cookie jar
*
* Poor little cookies, they will all be eaten by the cookie monster!
*/
~KCookieJar();
/**
* Returns whether the cookiejar has been changed
*/
bool changed() const { return m_cookiesChanged || m_configChanged; }
/**
* Store all the cookies in a safe(?) place
*/
bool saveCookies(const QString &_filename);
/**
* Load all the cookies from file and add them to the cookie jar.
*/
bool loadCookies(const QString &_filename);
/**
* Save the cookie configuration
*/
void saveConfig(KConfig *_config);
/**
* Load the cookie configuration
*/
void loadConfig(KConfig *_config, bool reparse = false);
/**
* Looks for cookies in the cookie jar which are appropriate for _url.
* Returned is a string containing all appropriate cookies in a format
* which can be added to a HTTP-header without any additional processing.
*
* If @p useDOMFormat is true, the string is formatted in a format
* in compliance with the DOM standard.
* @p pendingCookies contains a list of cookies that have not been
* approved yet by the user but that will be included in the result
* none the less.
*/
QString findCookies(const QString &_url, bool useDOMFormat, long windowId, KHttpCookieList *pendingCookies=0);
/**
* This function parses cookie_headers and returns a linked list of
* valid KHttpCookie objects for all cookies found in cookie_headers.
* If no cookies could be found 0 is returned.
*
* cookie_headers should be a concatenation of all lines of a HTTP-header
* which start with "Set-Cookie". The lines should be separated by '\n's.
*/
KHttpCookieList makeCookies(const QString &_url, const QByteArray &cookie_headers, long windowId);
/**
* This function parses cookie_headers and returns a linked list of
* valid KHttpCookie objects for all cookies found in cookie_headers.
* If no cookies could be found 0 is returned.
*
* cookie_domstr should be a concatenation of "name=value" pairs, separated
* by a semicolon ';'.
*/
KHttpCookieList makeDOMCookies(const QString &_url, const QByteArray &cookie_domstr, long windowId);
/**
* This function hands a KHttpCookie object over to the cookie jar.
*/
void addCookie(KHttpCookie &cookie);
/**
* This function tells whether a single KHttpCookie object should
* be considered persistent. Persistent cookies do not get deleted
* at the end of the session and are saved on disk.
*/
bool cookieIsPersistent(const KHttpCookie& cookie) const;
/**
* This function advices whether a single KHttpCookie object should
* be added to the cookie jar.
*
* Possible return values are:
* - KCookieAccept, the cookie should be added
* - KCookieAcceptForSession, the cookie should be added as session cookie
* - KCookieReject, the cookie should not be added
* - KCookieAsk, the user should decide what to do
*
* Before sending cookies back to a server this function is consulted,
* so that cookies having advice KCookieReject are not sent back.
*/
KCookieAdvice cookieAdvice(const KHttpCookie& cookie) const;
/**
* This function gets the advice for all cookies originating from
* _domain.
*
* - KCookieDunno, no specific advice for _domain
* - KCookieAccept, accept all cookies for _domain
* - KCookieAcceptForSession, accept all cookies for _domain as session cookies
* - KCookieReject, reject all cookies for _domain
* - KCookieAsk, the user decides what to do with cookies for _domain
*/
KCookieAdvice getDomainAdvice(const QString &_domain) const;
/**
* This function sets the advice for all cookies originating from
* _domain.
*
* _advice can have the following values:
* - KCookieDunno, no specific advice for _domain
* - KCookieAccept, accept all cookies for _domain
* - KCookieAcceptForSession, accept all cookies for _domain as session cookies
* - KCookieReject, reject all cookies for _domain
* - KCookieAsk, the user decides what to do with cookies for _domain
*/
void setDomainAdvice(const QString &_domain, KCookieAdvice _advice);
/**
* This function sets the advice for all cookies originating from
* the same domain as _cookie
*
* _advice can have the following values:
* - KCookieDunno, no specific advice for _domain
* - KCookieAccept, accept all cookies for _domain
* - KCookieAcceptForSession, accept all cookies for _domain as session cookies
* - KCookieReject, reject all cookies for _domain
* - KCookieAsk, the user decides what to do with cookies for _domain
*/
void setDomainAdvice(const KHttpCookie& _cookie, KCookieAdvice _advice);
/**
* Get the global advice for cookies
*
* The returned advice can have the following values:
* - KCookieAccept, accept cookies
* - KCookieAcceptForSession, accept cookies as session cookies
* - KCookieReject, reject cookies
* - KCookieAsk, the user decides what to do with cookies
*
* The global advice is used if the domain has no advice set.
*/
KCookieAdvice getGlobalAdvice() const { return m_globalAdvice; }
/**
* This function sets the global advice for cookies
*
* _advice can have the following values:
* - KCookieAccept, accept cookies
* - KCookieAcceptForSession, accept cookies as session cookies
* - KCookieReject, reject cookies
* - KCookieAsk, the user decides what to do with cookies
*
* The global advice is used if the domain has no advice set.
*/
void setGlobalAdvice(KCookieAdvice _advice);
/**
* Get a list of all domains known to the cookie jar.
* A domain is known to the cookie jar if:
* - It has a cookie originating from the domain
* - It has a specific advice set for the domain
*/
const QStringList& getDomainList();
/**
* Get a list of all cookies in the cookie jar originating from _domain.
*/
KHttpCookieList *getCookieList(const QString & _domain,
const QString& _fqdn );
/**
* Remove & delete a cookie from the jar.
*
* cookieIterator should be one of the entries in a KHttpCookieList.
* Update your KHttpCookieList by calling getCookieList after
* calling this function.
*/
void eatCookie(KHttpCookieList::iterator cookieIterator);
/**
* Remove & delete all cookies for @p domain.
*/
void eatCookiesForDomain(const QString &domain);
/**
* Remove & delete all cookies
*/
void eatAllCookies();
/**
* Removes all end of session cookies set by the
* session @p windId.
*/
void eatSessionCookies( long windowId );
/**
* Removes all end of session cookies set by the
* session @p windId.
*/
void eatSessionCookies( const QString& fqdn, long windowId, bool isFQDN = true );
/**
* Parses _url and returns the FQDN (_fqdn) and path (_path).
*/
static bool parseUrl(const QString &_url, QString &_fqdn, QString &_path, int *port = 0);
/**
* Returns a list of domains in @p _domainList relevant for this host.
* The list is sorted with the FQDN listed first and the top-most
* domain listed last
*/
static void extractDomains(const QString &_fqdn, QStringList &_domainList);
static QString adviceToStr(KCookieAdvice _advice);
static KCookieAdvice strToAdvice(const QString &_str);
enum KCookieDefaultPolicy {
ApplyToShownCookiesOnly = 0,
ApplyToCookiesFromDomain = 1,
ApplyToAllCookies = 2
};
/** Returns the user's choice in the cookie window */
KCookieDefaultPolicy preferredDefaultPolicy() const { return m_preferredPolicy; }
/** Returns the */
bool showCookieDetails () const { return m_showCookieDetails; }
/**
* Sets the user's default preference cookie policy.
*/
void setPreferredDefaultPolicy (KCookieDefaultPolicy value) { m_preferredPolicy = value; }
/**
* Sets the user's preference of level of detail displayed
* by the cookie dialog.
*/
void setShowCookieDetails (bool value) { m_showCookieDetails = value; }
protected:
void stripDomain(const QString &_fqdn, QString &_domain) const;
QString stripDomain(const KHttpCookie& cookie) const;
protected:
QStringList m_domainList;
KCookieAdvice m_globalAdvice;
QHash<QString, KHttpCookieList*> m_cookieDomains;
bool m_configChanged;
bool m_cookiesChanged;
bool m_showCookieDetails;
bool m_rejectCrossDomainCookies;
bool m_autoAcceptSessionCookies;
KCookieDefaultPolicy m_preferredPolicy;
};
#endif

View file

@ -1,11 +0,0 @@
// krazy:excludeall=copyright,license (nothing substantial in this file)
// remove as soon as dbusxml2cpp is fixed, Alex
#include <QtDBus/QtDBus>
#ifndef QDECLAREQLISTINT_HACK_H
#define QDECLAREQLISTINT_HACK_H
Q_DECLARE_METATYPE (QList<int>)
#endif

View file

@ -1,575 +0,0 @@
/*
This file is part of KDE
Copyright (C) 1998-2000 Waldo Bastian (bastian@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, 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 Cookie Server
#include "kcookieserver.h"
#define SAVE_DELAY 3 // Save after 3 minutes
#include <QtCore/QTimer>
#include <QtCore/QFile>
#include <QtDBus/QtDBus>
#include <kconfig.h>
#include <kdebug.h>
#include <kcmdlineargs.h>
#include <kstandarddirs.h>
#include <kpluginfactory.h>
#include <kpluginloader.h>
#include <kwindowsystem.h>
#include "kcookiejar.h"
#include "kcookiewin.h"
#include "kcookieserveradaptor.h"
#define QL1S(x) QLatin1String(x)
#define QL1C(x) QLatin1Char(x)
K_PLUGIN_FACTORY(KdedCookieServerFactory,
registerPlugin<KCookieServer>();
)
K_EXPORT_PLUGIN(KdedCookieServerFactory("kcookiejar"))
// Cookie field indexes
enum CookieDetails { CF_DOMAIN=0, CF_PATH, CF_NAME, CF_HOST,
CF_VALUE, CF_EXPIRE, CF_PROVER, CF_SECURE };
class CookieRequest {
public:
QDBusMessage reply;
QString url;
bool DOM;
qlonglong windowId;
};
class RequestList : public QList<CookieRequest*>
{
public:
RequestList() : QList<CookieRequest*>() { }
};
KCookieServer::KCookieServer(QObject* parent, const QList<QVariant>&)
: KDEDModule(parent)
{
(void)new KCookieServerAdaptor(this);
mCookieJar = new KCookieJar;
mPendingCookies = new KHttpCookieList;
mRequestList = new RequestList;
mAdvicePending = false;
mTimer = new QTimer();
mTimer->setSingleShot(true);
connect(mTimer, SIGNAL(timeout()), SLOT(slotSave()));
mConfig = new KConfig("kcookiejarrc");
mCookieJar->loadConfig(mConfig);
mCookieJar->loadCookies(KStandardDirs::locateLocal("data", "kcookiejar/cookies"));
connect(this, SIGNAL(windowUnregistered(qlonglong)),
this, SLOT(slotDeleteSessionCookies(qlonglong)));
}
KCookieServer::~KCookieServer()
{
slotSave();
delete mCookieJar;
delete mTimer;
delete mPendingCookies;
delete mConfig;
}
bool KCookieServer::cookiesPending( const QString &url, KHttpCookieList *cookieList )
{
QString fqdn;
QString path;
// Check whether 'url' has cookies on the pending list
if (mPendingCookies->isEmpty())
return false;
if (!KCookieJar::parseUrl(url, fqdn, path))
return false;
QStringList domains;
mCookieJar->extractDomains(fqdn, domains);
Q_FOREACH(const KHttpCookie& cookie, *mPendingCookies) {
if (cookie.match( fqdn, domains, path)) {
if (!cookieList)
return true;
cookieList->append(cookie);
}
}
if (!cookieList)
return false;
return cookieList->isEmpty();
}
void KCookieServer::addCookies( const QString &url, const QByteArray &cookieHeader,
qlonglong windowId, bool useDOMFormat )
{
KHttpCookieList cookieList;
if (useDOMFormat)
cookieList = mCookieJar->makeDOMCookies(url, cookieHeader, windowId);
else
cookieList = mCookieJar->makeCookies(url, cookieHeader, windowId);
checkCookies(&cookieList, windowId);
*mPendingCookies += cookieList;
if (!mAdvicePending)
{
mAdvicePending = true;
while (!mPendingCookies->isEmpty())
{
checkCookies(0, windowId);
}
mAdvicePending = false;
}
}
void KCookieServer::checkCookies(KHttpCookieList *cookieList)
{
checkCookies(cookieList, 0);
}
void KCookieServer::checkCookies(KHttpCookieList *cookieList, qlonglong windowId)
{
KHttpCookieList *list;
if (cookieList)
list = cookieList;
else
list = mPendingCookies;
QMutableListIterator<KHttpCookie> cookieIterator(*list);
while (cookieIterator.hasNext()) {
KHttpCookie& cookie = cookieIterator.next();
const KCookieAdvice advice = mCookieJar->cookieAdvice(cookie);
switch(advice) {
case KCookieAccept:
case KCookieAcceptForSession:
mCookieJar->addCookie(cookie);
cookieIterator.remove();
break;
case KCookieReject:
cookieIterator.remove();
break;
default:
break;
}
}
if (cookieList || list->isEmpty())
return;
// Collect all pending cookies with the same host as the first pending cookie
const KHttpCookie& currentCookie = mPendingCookies->first();
KHttpCookieList currentList;
currentList.append(currentCookie);
const QString currentHost = currentCookie.host();
QList<int> shownCookies; shownCookies << 0;
for (int i = 1 /*first already done*/; i < mPendingCookies->count(); ++i) {
const KHttpCookie& cookie = (*mPendingCookies)[i];
if (cookie.host() == currentHost) {
currentList.append(cookie);
shownCookies << i;
}
}
//kDebug() << shownCookies;
KCookieWin *kw = new KCookieWin( 0L, currentList,
mCookieJar->preferredDefaultPolicy(),
mCookieJar->showCookieDetails() );
if (windowId > 0) {
KWindowSystem::setMainWindow(kw, static_cast<WId>(windowId));
}
KCookieAdvice userAdvice = kw->advice(mCookieJar, currentCookie);
delete kw;
// Save the cookie config if it has changed
mCookieJar->saveConfig( mConfig );
// Apply the user's choice to all cookies that are currently
// queued for this host (or just the first one, if the user asks for that).
QMutableListIterator<KHttpCookie> cookieIterator2(*mPendingCookies);
int pendingCookieIndex = -1;
while (cookieIterator2.hasNext()) {
++pendingCookieIndex;
KHttpCookie& cookie = cookieIterator2.next();
if (cookie.host() != currentHost)
continue;
if (mCookieJar->preferredDefaultPolicy() == KCookieJar::ApplyToShownCookiesOnly
&& !shownCookies.contains(pendingCookieIndex)) {
// User chose "only those cookies", and this one was added while the dialog was up -> skip
break;
}
switch(userAdvice) {
case KCookieAccept:
case KCookieAcceptForSession:
// Store the user's choice for the cookie.
// This is only used to check later if the cookie should expire
// at the end of the session. The choice is not saved on disk.
cookie.setUserSelectedAdvice(userAdvice);
mCookieJar->addCookie(cookie);
cookieIterator2.remove();
break;
case KCookieReject:
cookieIterator2.remove();
break;
default:
kWarning() << "userAdvice not accept or reject, this should never happen!";
break;
}
}
// Check if we can handle any request
QMutableListIterator<CookieRequest *> requestIterator(*mRequestList);
while (requestIterator.hasNext()) {
CookieRequest *request = requestIterator.next();
if (!cookiesPending(request->url)) {
const QString res = mCookieJar->findCookies(request->url, request->DOM, request->windowId);
QDBusConnection::sessionBus().send(request->reply.createReply(res));
delete request;
requestIterator.remove();
}
}
saveCookieJar();
}
void KCookieServer::slotSave()
{
if (mCookieJar->changed())
{
QString filename = KStandardDirs::locateLocal("data", "kcookiejar/cookies");
mCookieJar->saveCookies(filename);
}
}
void KCookieServer::saveCookieJar()
{
if( mTimer->isActive() )
return;
mTimer->start( 1000*60*SAVE_DELAY );
}
void KCookieServer::putCookie( QStringList& out, const KHttpCookie& cookie,
const QList<int>& fields )
{
foreach ( const int i, fields ) {
switch(i)
{
case CF_DOMAIN :
out << cookie.domain();
break;
case CF_NAME :
out << cookie.name();
break;
case CF_PATH :
out << cookie.path();
break;
case CF_HOST :
out << cookie.host();
break;
case CF_VALUE :
out << cookie.value();
break;
case CF_EXPIRE :
out << QString::number(cookie.expireDate());
break;
case CF_PROVER :
out << QString::number(cookie.protocolVersion());
break;
case CF_SECURE :
out << QString::number(cookie.isSecure() ? 1 : 0);
break;
default :
out << QString();
}
}
}
bool KCookieServer::cookieMatches(const KHttpCookie& c,
const QString &domain, const QString &fqdn,
const QString &path, const QString &name)
{
const bool hasDomain = !domain.isEmpty();
return (((hasDomain && c.domain() == domain) || fqdn == c.host()) &&
(c.path() == path) && (c.name() == name) &&
(!c.isExpired()));
}
// DBUS function
QString KCookieServer::listCookies(const QString &url)
{
return findCookies(url, 0);
}
// DBUS function
QString KCookieServer::findCookies(const QString &url, qlonglong windowId)
{
if (cookiesPending(url))
{
CookieRequest *request = new CookieRequest;
// message().setDelayedReply(true);
request->reply = message();
request->reply.setDelayedReply(true);
request->url = url;
request->DOM = false;
request->windowId = windowId;
mRequestList->append( request );
return QString(); // Talk to you later :-)
}
QString cookies = mCookieJar->findCookies(url, false, windowId);
saveCookieJar();
return cookies;
}
// DBUS function
QStringList
KCookieServer::findDomains()
{
QStringList result;
Q_FOREACH(const QString& domain, mCookieJar->getDomainList())
{
// Ignore domains that have policy set for but contain
// no cookies whatsoever...
const KHttpCookieList* list = mCookieJar->getCookieList(domain, "");
if ( list && !list->isEmpty() )
result << domain;
}
return result;
}
// DBUS function
QStringList
KCookieServer::findCookies(const QList<int> &fields,
const QString &_domain,
const QString &fqdn,
const QString &path,
const QString &name)
{
QStringList result;
const bool allCookies = name.isEmpty();
const QStringList domainList = _domain.split(QLatin1Char(' '));
if (allCookies)
{
Q_FOREACH(const QString& domain, domainList)
{
const KHttpCookieList* list = mCookieJar->getCookieList(domain, fqdn);
if (!list)
continue;
Q_FOREACH(const KHttpCookie& cookie, *list)
{
if (cookie.isExpired())
continue;
putCookie(result, cookie, fields);
}
}
}
else
{
Q_FOREACH(const QString& domain, domainList)
{
const KHttpCookieList* list = mCookieJar->getCookieList(domain, fqdn);
if (!list)
continue;
Q_FOREACH(const KHttpCookie& cookie, *list)
{
if (cookie.isExpired())
continue;
if (cookieMatches(cookie, domain, fqdn, path, name))
{
putCookie(result, cookie, fields);
break;
}
}
}
}
return result;
}
// DBUS function
QString
KCookieServer::findDOMCookies(const QString &url)
{
return findDOMCookies(url, 0);
}
// DBUS function
QString
KCookieServer::findDOMCookies(const QString &url, qlonglong windowId)
{
// We don't wait for pending cookies because it locks up konqueror
// which can cause a deadlock if it happens to have a popup-menu up.
// Instead we just return pending cookies as if they had been accepted already.
KHttpCookieList pendingCookies;
cookiesPending(url, &pendingCookies);
return mCookieJar->findCookies(url, true, windowId, &pendingCookies);
}
// DBUS function
void
KCookieServer::addCookies(const QString &arg1, const QByteArray &arg2, qlonglong arg3)
{
addCookies(arg1, arg2, arg3, false);
}
// DBUS function
void
KCookieServer::deleteCookie(const QString &domain, const QString &fqdn,
const QString &path, const QString &name)
{
KHttpCookieList* cookieList = mCookieJar->getCookieList( domain, fqdn );
if (cookieList && !cookieList->isEmpty()) {
KHttpCookieList::Iterator itEnd = cookieList->end();
for (KHttpCookieList::Iterator it = cookieList->begin(); it != itEnd; ++it)
{
if (cookieMatches(*it, domain, fqdn, path, name)) {
mCookieJar->eatCookie(it);
saveCookieJar();
break;
}
}
}
}
// DBUS function
void
KCookieServer::deleteCookiesFromDomain(const QString &domain)
{
mCookieJar->eatCookiesForDomain(domain);
saveCookieJar();
}
// Qt function
void
KCookieServer::slotDeleteSessionCookies( qlonglong windowId )
{
deleteSessionCookies(windowId);
}
// DBUS function
void
KCookieServer::deleteSessionCookies( qlonglong windowId )
{
mCookieJar->eatSessionCookies( windowId );
saveCookieJar();
}
void
KCookieServer::deleteSessionCookiesFor(const QString &fqdn, qlonglong windowId)
{
mCookieJar->eatSessionCookies( fqdn, windowId );
saveCookieJar();
}
// DBUS function
void
KCookieServer::deleteAllCookies()
{
mCookieJar->eatAllCookies();
saveCookieJar();
}
// DBUS function
void
KCookieServer::addDOMCookies(const QString &url, const QByteArray &cookieHeader, qlonglong windowId)
{
addCookies(url, cookieHeader, windowId, true);
}
// DBUS function
bool
KCookieServer::setDomainAdvice(const QString &url, const QString &advice)
{
QString fqdn;
QString dummy;
if (KCookieJar::parseUrl(url, fqdn, dummy))
{
QStringList domains;
mCookieJar->extractDomains(fqdn, domains);
mCookieJar->setDomainAdvice(domains[domains.count() > 3 ? 3 : 0],
KCookieJar::strToAdvice(advice));
// Save the cookie config if it has changed
mCookieJar->saveConfig( mConfig );
return true;
}
return false;
}
// DBUS function
QString
KCookieServer::getDomainAdvice(const QString &url)
{
KCookieAdvice advice = KCookieDunno;
QString fqdn;
QString dummy;
if (KCookieJar::parseUrl(url, fqdn, dummy))
{
QStringList domains;
mCookieJar->extractDomains(fqdn, domains);
QStringListIterator it (domains);
while ( (advice == KCookieDunno) && it.hasNext() )
{
// Always check advice in both ".domain" and "domain". Note
// that we only want to check "domain" if it matches the
// fqdn of the requested URL.
const QString& domain = it.next();
if ( domain.at(0) == '.' || domain == fqdn )
advice = mCookieJar->getDomainAdvice(domain);
}
if (advice == KCookieDunno)
advice = mCookieJar->getGlobalAdvice();
}
return KCookieJar::adviceToStr(advice);
}
// DBUS function
void
KCookieServer::reloadPolicy()
{
mCookieJar->loadConfig( mConfig, true );
}
// DBUS function
void
KCookieServer::shutdown()
{
deleteLater();
}
#include "moc_kcookieserver.cpp"

View file

@ -1,101 +0,0 @@
/*
This file is part of the KDE File Manager
Copyright (C) 1998 Waldo Bastian (bastian@kde.org)
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public License
as published by the Free Software Foundation; either
version 2, or (at your option) version 3.
This software is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this library; see the file COPYING. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
//----------------------------------------------------------------------------
//
// KDE Cookie Server
#ifndef KCOOKIESERVER_H
#define KCOOKIESERVER_H
#include <QtCore/QStringList>
#include <QtCore/QTimer>
#include <QtDBus/QDBusContext>
#include <kdedmodule.h>
class KHttpCookieList;
class KCookieJar;
class KHttpCookie;
class RequestList;
class KConfig;
class KCookieServer : public KDEDModule, protected QDBusContext
{
Q_OBJECT
Q_CLASSINFO("D-Bus Interface", "org.kde.KCookieServer")
public:
KCookieServer(QObject* parent, const QList<QVariant>&);
~KCookieServer();
public Q_SLOTS:
// KDE5 TODO: don't overload names here, it prevents calling e.g. findCookies from the command-line using qdbus.
QString listCookies(const QString &url);
QString findCookies(const QString &url, qlonglong windowId);
QStringList findDomains();
// KDE5: rename
QStringList findCookies(const QList<int> &fields,const QString &domain,const QString& fqdn,const QString &path, const QString &name);
QString findDOMCookies(const QString &url);
QString findDOMCookies(const QString &url, qlonglong windowId); // KDE5: merge with above, using default value (windowId = 0)
void addCookies(const QString &url, const QByteArray &cookieHeader, qlonglong windowId);
void deleteCookie(const QString &domain, const QString &fqdn, const QString &path, const QString &name);
void deleteCookiesFromDomain(const QString &domain);
void deleteSessionCookies(qlonglong windowId);
void deleteSessionCookiesFor(const QString &fqdn, qlonglong windowId);
void deleteAllCookies();
void addDOMCookies(const QString &url, const QByteArray &cookieHeader, qlonglong windowId);
/**
* Sets the cookie policy for the domain associated with the specified URL.
*/
bool setDomainAdvice(const QString &url, const QString &advice);
/**
* Returns the cookie policy in effect for the specified URL.
*/
QString getDomainAdvice(const QString &url);
void reloadPolicy();
void shutdown();
public:
bool cookiesPending(const QString &url, KHttpCookieList *cookieList=0);
void addCookies(const QString &url, const QByteArray &cookieHeader,
qlonglong windowId, bool useDOMFormat);
void checkCookies(KHttpCookieList *cookieList);
// TODO: KDE5 merge with above function and make all these public functions
// private since they are not used externally.
void checkCookies(KHttpCookieList *cookieList, qlonglong windowId);
private Q_SLOTS:
void slotSave();
void slotDeleteSessionCookies(qlonglong windowId);
private:
KCookieJar *mCookieJar;
KHttpCookieList *mPendingCookies;
RequestList *mRequestList;
QTimer *mTimer;
bool mAdvicePending;
KConfig *mConfig;
private:
bool cookieMatches(const KHttpCookie&, const QString&, const QString&, const QString&, const QString&);
void putCookie(QStringList&, const KHttpCookie&, const QList<int>&);
void saveCookieJar();
};
#endif

View file

@ -1,341 +0,0 @@
/*
This file is part of KDE
Copyright (C) 2000- Waldo Bastian <bastian@kde.org>
Copyright (C) 2000- Dawit Alemayehu <adawit@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, 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 File Manager -- HTTP Cookie Dialogs
// The purpose of the QT_NO_TOOLTIP and QT_NO_WHATSTHIS ifdefs is because
// this file is also used in Konqueror/Embedded. One of the aims of
// Konqueror/Embedded is to be a small as possible to fit on embedded
// devices. For this it's also useful to strip out unneeded features of
// Qt, like for example QToolTip or QWhatsThis. The availability (or the
// lack thereof) can be determined using these preprocessor defines.
// The same applies to the QT_NO_ACCEL ifdef below. I hope it doesn't make
// too much trouble... (Simon)
#include "kcookiewin.h"
#include "kcookiejar.h"
#include <QtCore/QUrl>
#include <QtGui/QLabel>
#include <QtGui/QLayout>
#include <QtGui/QGroupBox>
#include <QtGui/QPushButton>
#include <QtGui/QRadioButton>
#include <QtGui/QShortcut>
#include <kwindowsystem.h>
#include <klocale.h>
#include <kglobal.h>
#include <klineedit.h>
#include <kiconloader.h>
#include <kapplication.h>
#include <kvbox.h>
#include <kdatetime.h>
KCookieWin::KCookieWin( QWidget *parent, KHttpCookieList cookieList,
int defaultButton, bool showDetails )
:KDialog( parent )
{
setModal(true);
setObjectName("cookiealert");
setButtons(Yes|User1|No|Details);
#ifndef Q_WS_QWS //FIXME(E): Implement for Qt Embedded
setCaption( i18n("Cookie Alert") );
setWindowIcon( KIcon("preferences-web-browser-cookies") );
// all cookies in the list should have the same window at this time, so let's take the first
if( cookieList.first().windowIds().count() > 0 )
{
KWindowSystem::setMainWindow( this, cookieList.first().windowIds().first());
}
else
{
// No window associated... make sure the user notices our dialog.
#ifdef Q_WS_X11
KWindowSystem::setState( winId(), NET::KeepAbove );
#endif
kapp->updateUserTimestamp();
}
#endif
const int count = cookieList.count();
const KHttpCookie& cookie = cookieList.first();
QString host (cookie.host());
int pos = host.indexOf(':');
if ( pos > 0 ) {
QString portNum = host.left(pos);
host.remove(0, pos+1);
host += ':';
host += portNum;
}
QString txt = QLatin1String("<html><body style=\"p {line-height: 150%}; text-align: center;\">");
txt += i18ncp("%2 hostname, %3 optional cross domain suffix (translated below)",
"<p>You received a cookie from<br/>"
"<b>%2%3</b><br/>"
"Do you want to accept or reject this cookie?</p>",
"<p>You received %1 cookies from<br/>"
"<b>%2%3</b><br/>"
"Do you want to accept or reject these cookies?</p>",
count,
QUrl::fromAce(host.toLatin1()),
cookie.isCrossDomain() ? i18nc("@item:intext cross domain cookie", " [Cross Domain]") : QString());
txt += QLatin1String("</body></html>");
KVBox* vBox1 = new KVBox( this );
vBox1->setSpacing( -1 );
setMainWidget(vBox1);
// Cookie image and message to user
KHBox* hBox = new KHBox( vBox1 );
QLabel* icon = new QLabel( hBox );
icon->setPixmap(KIcon("dialog-warning").pixmap(IconSize(KIconLoader::Desktop)));
icon->setAlignment( Qt::AlignCenter );
icon->setFixedSize( 2*icon->sizeHint() );
KVBox* vBox = new KVBox(hBox);
QLabel* lbl = new QLabel(txt, vBox);
lbl->setAlignment(Qt::AlignCenter);
// Cookie Details dialog...
m_detailView = new KCookieDetail( cookieList, count, vBox1 );
setDetailsWidget(m_detailView);
// Cookie policy choice...
QGroupBox *m_btnGrp = new QGroupBox(i18n("Apply Choice To"),vBox1);
QVBoxLayout *vbox = new QVBoxLayout;
txt = (count == 1)? i18n("&Only this cookie") : i18n("&Only these cookies");
m_onlyCookies = new QRadioButton( txt, m_btnGrp );
vbox->addWidget(m_onlyCookies);
#ifndef QT_NO_WHATSTHIS
m_onlyCookies->setWhatsThis(i18n("Select this option to only accept or reject this cookie. "
"You will be prompted again if you receive another cookie."));
#endif
m_allCookiesDomain = new QRadioButton( i18n("All cookies from this do&main"), m_btnGrp );
vbox->addWidget(m_allCookiesDomain);
#ifndef QT_NO_WHATSTHIS
m_allCookiesDomain->setWhatsThis(i18n("Select this option to accept or reject all cookies from "
"this site. Choosing this option will add a new policy for "
"the site this cookie originated from. This policy will be "
"permanent until you manually change it from the System Settings."));
#endif
m_allCookies = new QRadioButton( i18n("All &cookies"), m_btnGrp);
vbox->addWidget(m_allCookies);
#ifndef QT_NO_WHATSTHIS
m_allCookies->setWhatsThis(i18n("Select this option to accept/reject all cookies from "
"anywhere. Choosing this option will change the global "
"cookie policy for all cookies until you manually change "
"it from the System Settings."));
#endif
m_btnGrp->setLayout(vbox);
switch (defaultButton) {
case KCookieJar::ApplyToShownCookiesOnly:
m_onlyCookies->setChecked(true);
break;
case KCookieJar::ApplyToCookiesFromDomain:
m_allCookiesDomain->setChecked(true);
break;
case KCookieJar::ApplyToAllCookies:
m_allCookies->setChecked(true);
break;
default:
m_onlyCookies->setChecked(true);
break;
}
setButtonText(KDialog::Yes, i18n("&Accept"));
setButtonText(KDialog::User1, i18n("Accept for this &session"));
setButtonIcon(KDialog::User1, KIcon("chronometer"));
setButtonToolTip(KDialog::User1, i18n("Accept cookie(s) until the end of the current session"));
setButtonText(KDialog::No, i18n("&Reject"));
setButtonToolTip(Details, i18n("See or modify the cookie information") );
setDefaultButton(Yes);
setDetailsWidgetVisible(showDetails);
}
KCookieWin::~KCookieWin()
{
}
KCookieAdvice KCookieWin::advice( KCookieJar *cookiejar, const KHttpCookie& cookie )
{
const int result = exec();
cookiejar->setShowCookieDetails (isDetailsWidgetVisible());
KCookieAdvice advice;
switch (result) {
case KDialog::Yes:
advice = KCookieAccept;
break;
case KDialog::User1:
advice = KCookieAcceptForSession;
break;
default:
advice = KCookieReject;
break;
}
KCookieJar::KCookieDefaultPolicy preferredPolicy = KCookieJar::ApplyToShownCookiesOnly;
if (m_allCookiesDomain->isChecked()) {
preferredPolicy = KCookieJar::ApplyToCookiesFromDomain;
cookiejar->setDomainAdvice( cookie, advice );
} else if (m_allCookies->isChecked()) {
preferredPolicy = KCookieJar::ApplyToAllCookies;
cookiejar->setGlobalAdvice( advice );
}
cookiejar->setPreferredDefaultPolicy( preferredPolicy );
return advice;
}
KCookieDetail::KCookieDetail( KHttpCookieList cookieList, int cookieCount,
QWidget* parent )
:QGroupBox( parent )
{
setTitle( i18n("Cookie Details") );
QGridLayout* grid = new QGridLayout( this );
grid->addItem( new QSpacerItem(0, fontMetrics().lineSpacing()), 0, 0 );
grid->setColumnStretch( 1, 3 );
QLabel* label = new QLabel( i18n("Name:"), this );
grid->addWidget( label, 1, 0 );
m_name = new KLineEdit( this );
m_name->setReadOnly( true );
m_name->setMaximumWidth( fontMetrics().maxWidth() * 25 );
grid->addWidget( m_name, 1 ,1 );
//Add the value
label = new QLabel( i18n("Value:"), this );
grid->addWidget( label, 2, 0 );
m_value = new KLineEdit( this );
m_value->setReadOnly( true );
m_value->setMaximumWidth( fontMetrics().maxWidth() * 25 );
grid->addWidget( m_value, 2, 1);
label = new QLabel( i18n("Expires:"), this );
grid->addWidget( label, 3, 0 );
m_expires = new KLineEdit( this );
m_expires->setReadOnly( true );
m_expires->setMaximumWidth(fontMetrics().maxWidth() * 25 );
grid->addWidget( m_expires, 3, 1);
label = new QLabel( i18n("Path:"), this );
grid->addWidget( label, 4, 0 );
m_path = new KLineEdit( this );
m_path->setReadOnly( true );
m_path->setMaximumWidth( fontMetrics().maxWidth() * 25 );
grid->addWidget( m_path, 4, 1);
label = new QLabel( i18n("Domain:"), this );
grid->addWidget( label, 5, 0 );
m_domain = new KLineEdit( this );
m_domain->setReadOnly( true );
m_domain->setMaximumWidth( fontMetrics().maxWidth() * 25 );
grid->addWidget( m_domain, 5, 1);
label = new QLabel( i18n("Exposure:"), this );
grid->addWidget( label, 6, 0 );
m_secure = new KLineEdit( this );
m_secure->setReadOnly( true );
m_secure->setMaximumWidth( fontMetrics().maxWidth() * 25 );
grid->addWidget( m_secure, 6, 1 );
if ( cookieCount > 1 )
{
QPushButton* btnNext = new QPushButton( i18nc("Next cookie","&Next >>"), this );
btnNext->setFixedSize( btnNext->sizeHint() );
grid->addWidget( btnNext, 8, 0, 1, 2 );
connect( btnNext, SIGNAL(clicked()), SLOT(slotNextCookie()) );
#ifndef QT_NO_TOOLTIP
btnNext->setToolTip(i18n("Show details of the next cookie") );
#endif
}
m_cookieList = cookieList;
m_cookieNumber = 0;
slotNextCookie();
}
KCookieDetail::~KCookieDetail()
{
}
void KCookieDetail::slotNextCookie()
{
if (m_cookieNumber == m_cookieList.count() - 1)
m_cookieNumber = 0;
else
++m_cookieNumber;
displayCookieDetails();
}
void KCookieDetail::displayCookieDetails()
{
const KHttpCookie& cookie = m_cookieList.at(m_cookieNumber);
m_name->setText(cookie.name());
m_value->setText((cookie.value()));
if (cookie.domain().isEmpty())
m_domain->setText(i18n("Not specified"));
else
m_domain->setText(cookie.domain());
m_path->setText(cookie.path());
KDateTime cookiedate;
cookiedate.setTime_t(cookie.expireDate());
if (cookie.expireDate())
m_expires->setText(KGlobal::locale()->formatDateTime(cookiedate));
else
m_expires->setText(i18n("End of Session"));
QString sec;
if (cookie.isSecure())
{
if (cookie.isHttpOnly())
sec = i18n("Secure servers only");
else
sec = i18n("Secure servers, page scripts");
}
else
{
if (cookie.isHttpOnly())
sec = i18n("Servers");
else
sec = i18n("Servers, page scripts");
}
m_secure->setText(sec);
}
void KCookieWin::slotButtonClicked(int button)
{
if (button == KDialog::User1) {
done (KDialog::User1);
return;
}
KDialog::slotButtonClicked(button);
}
#include "moc_kcookiewin.cpp"

View file

@ -1,82 +0,0 @@
/*
This file is part of the KDE File Manager
Copyright (C) 1998- Waldo Bastian (bastian@kde.org)
Copyright (C) 2000- Dawit Alemayehu (adawit@kde.org)
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This software is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this library; see the file COPYING. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
//----------------------------------------------------------------------------
//
// KDE File Manager -- HTTP Cookie Dialogs
#ifndef KCOOKIEWIN_H
#define KCOOKIEWIN_H
#include <QtGui/QGroupBox>
#include <QtGui/QRadioButton>
#include <kdialog.h>
#include "kcookiejar.h"
class KLineEdit;
#include <QPushButton>
class KCookieDetail : public QGroupBox
{
Q_OBJECT
public:
KCookieDetail(KHttpCookieList cookieList, int cookieCount, QWidget *parent=0);
~KCookieDetail();
private Q_SLOTS:
void slotNextCookie();
private:
void displayCookieDetails();
KLineEdit* m_name;
KLineEdit* m_value;
KLineEdit* m_expires;
KLineEdit* m_domain;
KLineEdit* m_path;
KLineEdit* m_secure;
KHttpCookieList m_cookieList;
int m_cookieNumber;
};
class KCookieWin : public KDialog
{
Q_OBJECT
public :
KCookieWin( QWidget *parent, KHttpCookieList cookieList, int defaultButton=0,
bool showDetails=false );
~KCookieWin();
KCookieAdvice advice( KCookieJar *cookiejar, const KHttpCookie& cookie );
protected:
virtual void slotButtonClicked(int button);
private :
QRadioButton* m_onlyCookies, *m_allCookies, *m_allCookiesDomain;
KCookieDetail* m_detailView;
};
#endif

View file

@ -1,78 +0,0 @@
/*
This file is part of KDE
Copyright (C) 1998-2000 Waldo Bastian (bastian@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, 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 <QtDBus/QtDBus>
#include <kcmdlineargs.h>
#include <klocale.h>
#include <kapplication.h>
#include "kcookieserverinterface.h"
#include "kdedinterface.h"
int main(int argc, char *argv[])
{
KLocalizedString description = ki18n("HTTP Cookie Daemon");
const char version[] = "1.0";
KCmdLineArgs::init(argc, argv, "kcookiejar", "kdelibs4", ki18n("HTTP cookie daemon"), version, description);
KCmdLineOptions options;
options.add("shutdown", ki18n("Shut down cookie jar"));
options.add("remove <domain>", ki18n("Remove cookies for domain"));
options.add("remove-all", ki18n("Remove all cookies"));
options.add("reload-config", ki18n("Reload configuration file"));
KCmdLineArgs::addCmdLineOptions( options );
KComponentData a("kio4");
KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
org::kde::KCookieServer *kcookiejar = new org::kde::KCookieServer("org.kde.kded", "/modules/kcookiejar", QDBusConnection::sessionBus());
if (args->isSet("remove-all"))
{
kcookiejar->deleteAllCookies();
}
if (args->isSet("remove"))
{
QString domain = args->getOption("remove");
kcookiejar->deleteCookiesFromDomain(domain);
}
if (args->isSet("shutdown"))
{
org::kde::kded kded("org.kde.kded", "/kded", QDBusConnection::sessionBus());
kded.unloadModule("kcookiejar");
}
else if(args->isSet("reload-config"))
{
kcookiejar->reloadPolicy();
}
else
{
org::kde::kded kded("org.kde.kded", "/kded", QDBusConnection::sessionBus());
kded.loadModule("kcookiejar");
}
delete kcookiejar;
return 0;
}

View file

@ -1,331 +0,0 @@
<HTML>
<HEAD>
<TITLE>Client Side State - HTTP Cookies</TITLE>
</HEAD>
<BODY BGCOLOR="#ffffff" LINK="#0000ff" VLINK="#ff0000" ALINK="#ff0000" TEXT="#000000" >
<CENTER>
<!-- BANNER:s3 -->
<A HREF="/maps/banners/documentation_s3.map"><IMG SRC="/images/banners/documentation_s3.gif" ALT="Documentation" BORDER=0 WIDTH=612 HEIGHT=50 ISMAP USEMAP="#banner_nav"></A>
<MAP NAME="banner_nav">
<AREA SHAPE=RECT COORDS="62,11,91,40" HREF="/">
<AREA SHAPE=RECT COORDS="153,41,221,50" HREF="/">
<AREA SHAPE=RECT COORDS="298,8,374,34" HREF="/support/index.html">
<AREA SHAPE=RECT COORDS="381,15,586,43" HREF="http://help.netscape.com/browse/index.html">
<AREA SHAPE=default NOHREF>
</MAP>
<!-- BANNER:s3 -->
<H2>
<FONT SIZE=+3>P</FONT>ERSISTENT
<FONT SIZE=+3>C</FONT>LIENT
<FONT SIZE=+3>S</FONT>TATE<BR>
<FONT SIZE=+3>HTTP C</FONT>OOKIES
</H2>
<H3>Preliminary Specification - Use with caution</H3>
</CENTER>
<HR SIZE=4>
<CENTER>
<H3>
<FONT SIZE=+2>I</FONT>NTRODUCTION
</H3>
</CENTER>
Cookies are a general mechanism which server side connections (such as
CGI scripts) can use to both store and retrieve information on the
client side of the connection. The addition of a simple, persistent,
client-side state significantly extends the capabilities of Web-based
client/server applications.<P>
<CENTER>
<H3>
<FONT SIZE=+2>O</FONT>VERVIEW
</H3>
</CENTER>
A server, when returning an HTTP object to a client, may also send a
piece of state information which the client will store. Included in that
state object is a description of the range of URLs for which that state is
valid. Any future HTTP requests made by the client which fall in that
range will include a transmittal of the current value of the state
object from the client back to the server. The state object is called
a <B>cookie</B>, for no compelling reason. <P>
This simple mechanism provides a powerful new tool which enables a host
of new types of applications to be written for web-based environments.
Shopping applications can now store information about the currently
selected items, for fee services can send back registration information
and free the client from retyping a user-id on next connection,
sites can store per-user preferences on the client, and have the client supply
those preferences every time that site is connected to.
<CENTER>
<H3>
<FONT SIZE=+2>S</FONT>PECIFICATION
</H3>
</CENTER>
A cookie is introduced to the client by including a <B>Set-Cookie</B>
header as part of an HTTP response, typically this will be generated
by a CGI script.
<H3>Syntax of the Set-Cookie HTTP Response Header</H3>
This is the format a CGI script would use to add to the HTTP headers
a new piece of data which is to be stored by the client for later retrieval.
<PRE>
Set-Cookie: <I>NAME</I>=<I>VALUE</I>; expires=<I>DATE</I>;
path=<I>PATH</I>; domain=<I>DOMAIN_NAME</I>; secure
</PRE>
<DL>
<DT> <I>NAME</I>=<I>VALUE</I><DD>
This string is a sequence of characters excluding semi-colon, comma and white
space. If there is a need to place such data in the name or value, some
encoding method such as URL style %XX encoding is recommended, though no
encoding is defined or required. <P> This is the only required attribute
on the <B>Set-Cookie</B> header. <P>
<DT><B>expires</B>=<I>DATE</I>
<DD>
The <B>expires</B> attribute specifies a date string that
defines the valid life time of that cookie. Once the expiration
date has been reached, the cookie will no longer be stored or
given out. <P>
The date string is formatted as:
<BLOCKQUOTE> <TT>Wdy, DD-Mon-YYYY HH:MM:SS GMT</TT></BLOCKQUOTE>
This is based on
<A TARGET="_top" HREF="http://ds.internic.net/rfc/rfc822.txt">RFC 822</A>,
<A TARGET="_top" HREF="http://ds.internic.net/rfc/rfc850.txt">RFC 850</A>,
<A TARGET="_top" HREF="http://www.w3.org/hypertext/WWW/Protocols/rfc1036/rfc1036.html#z6">
RFC 1036</A>, and
<A TARGET="_top" HREF="http://ds1.internic.net/rfc/rfc1123.txt">
RFC 1123</A>,
with the variations that the only legal time zone is <B>GMT</B> and
the separators between the elements of the date must be dashes.
<P>
<B>expires</B> is an optional attribute. If not specified, the cookie will
expire when the user's session ends. <P>
<B>Note:</B> There is a bug in Netscape Navigator version 1.1 and earlier.
Only cookies whose <B>path</B> attribute is set explicitly to "/" will
be properly saved between sessions if they have an <B>expires</B>
attribute.<P>
<DT> <B>domain</B>=<I>DOMAIN_NAME</I>
<DD>
When searching the cookie list for valid cookies, a comparison of the
<B>domain</B>
attributes of the cookie is made with the Internet domain name of the
host from which the URL will be fetched. If there is a tail match,
then the cookie will go through <B>path</B> matching to see if it
should be sent. "Tail matching" means that <B>domain</B> attribute
is matched against the tail of the fully qualified domain name of
the host. A <B>domain</B> attribute of "acme.com" would match
host names "anvil.acme.com" as well as "shipping.crate.acme.com". <P>
Only hosts within the specified domain
can set a cookie for a domain and domains must have at least two (2)
or three (3) periods in them to prevent domains of the form:
".com", ".edu", and "va.us". Any domain that fails within
one of the seven special top level domains listed below only require
two periods. Any other domain requires at least three. The
seven special top level domains are: "COM", "EDU", "NET", "ORG",
"GOV", "MIL", and "INT".
<P>
The default value of <B>domain</B> is the host name of the server
which generated the cookie response. <P>
<DT> <B>path</B>=<I>PATH</I>
<DD>
The <B>path</B> attribute is used to specify the subset of URLs in a
domain for
which the cookie is valid. If a cookie has already passed <B>domain</B>
matching, then the pathname component
of the URL is compared with the path attribute, and if there is
a match, the cookie is considered valid and is sent along with
the URL request. The path "/foo"
would match "/foobar" and "/foo/bar.html". The path "/" is the most
general path. <P>
If the <B>path</B> is not specified, it as assumed to be the same path
as the document being described by the header which contains the cookie.
<P>
<DT> <B>secure</B>
<DD>
If a cookie is marked <B>secure</B>, it will only be transmitted if the
communications channel with the host is a secure one. Currently
this means that secure cookies will only be sent to HTTPS (HTTP over SSL)
servers. <P>
If <B>secure</B> is not specified, a cookie is considered safe to be sent
in the clear over unsecured channels.
</DL>
<H3>Syntax of the Cookie HTTP Request Header</H3>
When requesting a URL from an HTTP server, the browser will match
the URL against all cookies and if any of them match, a line
containing the name/value pairs of all matching cookies will
be included in the HTTP request. Here is the format of that line:
<PRE>
Cookie: <I>NAME1=OPAQUE_STRING1</I>; <I>NAME2=OPAQUE_STRING2 ...</I>
</PRE>
<H3>Additional Notes</H3>
<UL>
<LI>Multiple <B>Set-Cookie</B> headers can be issued in a single server
response.
<p>
<LI>Instances of the same path and name will overwrite each other, with the
latest instance taking precedence. Instances of the same path but
different names will add additional mappings.
<p>
<LI>Setting the path to a higher-level value does not override other more
specific path mappings. If there are multiple matches for a given cookie
name, but with separate paths, all the matching cookies will be sent.
(See examples below.)
<p>
<LI>The
expires header lets the client know when it is safe to purge the mapping
but the client is not required to do so. A client may also delete a
cookie before it's expiration date arrives if the number of cookies
exceeds its internal limits.
<p>
<LI>When sending cookies to a server, all cookies with a more specific
path mapping should be sent before cookies with less specific path
mappings. For example, a cookie "name1=foo" with a path mapping
of "/" should be sent after a cookie "name1=foo2" with
a path mapping of "/bar" if they are both to be sent.
<p>
<LI>There are limitations on the number of cookies that a client
can store at any one time. This is a specification of the minimum
number of cookies that a client should be prepared to receive and
store.
<UL>
<LI>300 total cookies
<LI>4 kilobytes per cookie, where the name and the OPAQUE_STRING
combine to form the 4 kilobyte limit.
<LI>20 cookies per server or domain. (note that completely
specified hosts and domains are treated as separate entities
and have a 20 cookie limitation for each, not combined)
</UL>
Servers should not expect clients to be able to exceed these limits.
When the 300 cookie limit or the 20 cookie per server limit
is exceeded, clients should delete the least recently used cookie.
When a cookie larger than 4 kilobytes is encountered the cookie
should be trimmed to fit, but the name should remain intact
as long as it is less than 4 kilobytes.
<P>
<LI>If a CGI script wishes to delete a cookie, it can do so by
returning a cookie with the same name, and an <B>expires</B> time
which is in the past. The path and name must match exactly
in order for the expiring cookie to replace the valid cookie.
This requirement makes it difficult for anyone but the originator
of a cookie to delete a cookie.
<P><LI>When caching HTTP, as a proxy server might do, the <B>Set-cookie</B>
response header should never be cached.
<P><LI>If a proxy server receives a response which
contains a <B>Set-cookie</B> header, it should propagate the <B>Set-cookie</B>
header to the client, regardless of whether the response was 304
(Not Modified) or 200 (OK).
<P>Similarly, if a client request contains a Cookie: header, it
should be forwarded through a proxy, even if the conditional
If-modified-since request is being made.
</UL>
<CENTER>
<H3>
<FONT SIZE=+2>E</FONT>XAMPLES
</H3>
</CENTER>
Here are some sample exchanges which are designed to illustrate the use
of cookies.
<H3>First Example transaction sequence:</H3>
<DL>
<dt>Client requests a document, and receives in the response:<dd>
<PRE>
Set-Cookie: CUSTOMER=WILE_E_COYOTE; path=/; expires=Wednesday, 09-Nov-99 23:12:40 GMT</PRE>
<dt>When client requests a URL in path "/" on this server, it sends:<DD>
<PRE>Cookie: CUSTOMER=WILE_E_COYOTE</PRE>
<dt>Client requests a document, and receives in the response:<dd>
<PRE>Set-Cookie: PART_NUMBER=ROCKET_LAUNCHER_0001; path=/</PRE>
<dt>When client requests a URL in path "/" on this server, it sends:<dd>
<PRE>Cookie: CUSTOMER=WILE_E_COYOTE; PART_NUMBER=ROCKET_LAUNCHER_0001</PRE>
<dt>Client receives:<dd>
<PRE>Set-Cookie: SHIPPING=FEDEX; path=/foo</PRE>
<dt>When client requests a URL in path "/" on this server, it sends:<dd>
<PRE>Cookie: CUSTOMER=WILE_E_COYOTE; PART_NUMBER=ROCKET_LAUNCHER_0001</PRE>
<dt>When client requests a URL in path "/foo" on this server, it sends:<dd>
<PRE>Cookie: CUSTOMER=WILE_E_COYOTE; PART_NUMBER=ROCKET_LAUNCHER_0001; SHIPPING=FEDEX</PRE>
</DL>
<H3>Second Example transaction sequence:</H3>
<DL>
<dt>Assume all mappings from above have been cleared.<p>
<dt>Client receives:<dd>
<PRE>Set-Cookie: PART_NUMBER=ROCKET_LAUNCHER_0001; path=/</PRE>
<dt>When client requests a URL in path "/" on this server, it sends:<dd>
<PRE>Cookie: PART_NUMBER=ROCKET_LAUNCHER_0001</PRE>
<dt>Client receives:<dd>
<PRE>Set-Cookie: PART_NUMBER=RIDING_ROCKET_0023; path=/ammo</PRE>
<dt>When client requests a URL in path "/ammo" on this server, it sends:<dd>
<PRE>Cookie: PART_NUMBER=RIDING_ROCKET_0023; PART_NUMBER=ROCKET_LAUNCHER_0001</PRE>
<dd>NOTE: There are two name/value pairs named "PART_NUMBER" due to the
inheritance
of the "/" mapping in addition to the "/ammo" mapping.
</DL>
<HR SIZE=4>
<P>
<CENTER>
<!-- footer -->
<TABLE WIDTH=600 BORDER=0 CELLPADDING=0 CELLSPACING=0>
<TR>
<TD WIDTH=600 HEIGHT=8><HR SIZE=1 NOSHADE></TD></TR>
<TR><TD ALIGN=LEFT VALIGN=TOP><FONT FACE="sans-serif, Arial, Helvetica" SIZE=-2><A HREF="http://home.netscape.com/misc/nav_redir/help.html" TARGET="_top">Help</A>&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;<A
HREF="http://home.netscape.com/misc/nav_redir/site_map.html" TARGET="_top">Site&nbsp;Map</A>&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;<A
HREF="http://home.netscape.com/misc/nav_redir/howtoget.html" TARGET="_top">How&nbsp;to&nbsp;Get&nbsp;Netscape&nbsp;Products</A>&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;<A HREF="http://home.netscape.com/misc/nav_redir/ad.html" TARGET="_top">Advertise&nbsp;With&nbsp;Us</A>&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp; <A HREF="http://home.netscape.com/misc/nav_redir/addsite.html" TARGET="_top">Add Site</A>&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;<A HREF="http://home.netscape.com/misc/nav_redir/custom_browser.html" TARGET="_top">Custom Browser Program</A></FONT></TD></TR>
<TR>
<TD WIDTH=600 HEIGHT=8 COLSPAN=0></TD>
</TR>
<TR>
<TD ALIGN=LEFT VALIGN=TOP>
<!-- Channels -->
<FONT FACE="sans-serif, Arial, Helvetica" SIZE=-2><A HREF="http://home.netscape.com/misc/nav_redir/channels/autos.html" TARGET="_top">Autos</A>&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp; <A
HREF="http://home.netscape.com/misc/nav_redir/channels/business.html" TARGET="_top">Business</A>&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp; <A HREF="http://home.netscape.com/misc/nav_redir/channels/computers_internet.html" TARGET="_top">Computing&nbsp;&amp;&nbsp;Internet</A>&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp; <A HREF="http://home.netscape.com/misc/nav_redir/channels/entertainment.html" TARGET="_top">Entertainment</A>&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp; <A
HREF="http://home.netscape.com/misc/nav_redir/channels/kids_family.html" TARGET="_top">Family</A>&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp; <A
HREF="http://home.netscape.com/misc/nav_redir/channels/games.html" TARGET="_top">Games</A>&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp; <A HREF="http://home.netscape.com/misc/nav_redir/channels/health.html" TARGET="_top">Health</A>&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp; <A HREF="http://home.netscape.com/misc/nav_redir/channels/lifestyles.html" TARGET="_top">Lifestyles</A>&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp; <A
HREF="http://home.netscape.com/misc/nav_redir/channels/local.html" TARGET="_top">Local</A>&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp; <A HREF="http://home.netscape.com/misc/nav_redir/channels/netscape.html" TARGET="_top">Netscape</A>&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp; <A HREF="http://home.netscape.com/misc/nav_redir/channels/open_directory.html">Netscape&nbsp;Open&nbsp;Directory</A>&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp; <A
HREF="http://home.netscape.com/misc/nav_redir/channels/news.html" TARGET="_top">News</A>&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp; <A HREF="http://home.netscape.com/misc/nav_redir/channels/personalize_finance.html" TARGET="_top">Personal&nbsp;Finance</A>&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp; <A
HREF="http://home.netscape.com/misc/nav_redir/channels/real_estate.html" TARGET="_top">Real Estate</A>&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp; <A HREF="http://home.netscape.com/misc/nav_redir/channels/education.html" TARGET="_top">Research&nbsp;&amp;&nbsp;Learn</A>&nbsp;&nbsp;&nbsp;|&nbsp; <A HREF="http://home.netscape.com/misc/nav_redir/channels/shopping.html" TARGET="_top">Shopping</A>&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp; <A HREF="http://home.netscape.com/misc/nav_redir/channels/smallbiz.html" TARGET="_top">Small Business</A>&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp; <A
HREF="http://home.netscape.com/misc/nav_redir/channels/sports.html" TARGET="_top">Sports</A>&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp; <A HREF="http://home.netscape.com/misc/nav_redir/channels/travel.html" TARGET="_top">Travel</A></FONT></TD></TR>
</TABLE>
<TABLE WIDTH=600 BORDER=0 CELLPADDING=0 CELLSPACING=0>
<TR><TD WIDTH=600 HEIGHT=8 COLSPAN=0></TD></TR>
<TR>
<TD WIDTH=600 COLSPAN=5 VALIGN=TOP ALIGN=LEFT>
<FONT FACE="sans-serif, Arial, Helvetica" SIZE=-2>
&copy; 1999 Netscape, All Rights Reserved. <A HREF="http://home.netscape.com/legal_notices/index.html">Legal & Privacy Notices</A><BR>This site powered by <A HREF="http://home.netscape.com/comprod/server_central/index.html" TARGET="_top">Netscape SuiteSpot servers</A>.</FONT></TD>
</TR>
</TABLE>
<!-- end footer -->
</CENTER>
<P>
</BODY>
</HTML>

View file

@ -1,3 +0,0 @@
HTTP State Management Mechanism
http://www.ietf.org/rfc/rfc2965.txt
http://www.ietf.org/rfc/rfc2109.txt

View file

@ -1,7 +0,0 @@
########### next target ###############
set(kcookiejartest_SRCS kcookiejartest.cpp)
kde4_add_test(kcookiejar-kcookiejartest ${kcookiejartest_SRCS})
target_link_libraries(kcookiejar-kcookiejartest ${KDE4_KIO_LIBS} ${QT_QTTEST_LIBRARY})

View file

@ -1,188 +0,0 @@
## Check setting of cookies
COOKIE ASK http://w.y.z/ Set-Cookie: some_value=value1; Path="/"; expires=%NEXTYEAR%
CHECK http://w.y.z/ Cookie: some_value=value1
COOKIE ASK http://a.b.c/ Set-Cookie: some_value=value2; Path="/"
CHECK http://a.b.c/ Cookie: some_value=value2
## Check if clearing cookie jar works
CLEAR COOKIES
CHECK http://w.y.z/
CHECK http://a.b.c/
## Check cookie syntax
COOKIE ASK http://w.y.z/ Set-Cookie: some_value=value with spaces
CHECK http://w.y.z/ Cookie: some_value=value with spaces
COOKIE ASK http://a.b.c/ Set-Cookie: some_value="quoted value"
CHECK http://a.b.c/ Cookie: some_value="quoted value"
# Without a = sign, the cookie gets interpreted as the value for a cookie with no name
# This is what IE and Netscape does
COOKIE ASK http://a.b.c/ Set-Cookie: some_value
# Note: order in the expected list does not matter. Changed between kde3 and kde4.
CHECK http://a.b.c/ Cookie: some_value="quoted value"; some_value
COOKIE ASK http://a.b.c/ Set-Cookie: some_other_value
CHECK http://a.b.c/ Cookie: some_value="quoted value"; some_other_value
CLEAR COOKIES
# This doesn't work with old-style netscape cookies, it should work with RFC2965 cookies
COOKIE ASK http://a.b.c/ Set-Cookie: some_value="quoted value; and such"
# IE & Netscape does this:
CHECK http://a.b.c/ Cookie: some_value="quoted value
# Mozilla does:
# CHECK http://a.b.c/ Cookie: some_value="quoted value; and such"
# COOKIE ASK http://a.b.c/ Set-Cookie: some_value="quoted value;
# CHECK http://a.b.c/ Cookie: some_value=
# Note that we parse RFC2965 cookies like Mozilla does
CLEAR COOKIES
## Check if deleting cookies works
COOKIE ASK http://w.y.z/ Set-Cookie: some_value=value1; Path="/"; expires=%NEXTYEAR%
CHECK http://w.y.z/ Cookie: some_value=value1
COOKIE ASK http://w.y.z/ Set-Cookie: some_value=value1; Path="/"; expires=%LASTYEAR%
CHECK http://w.y.z/
## Check if updating cookies works
COOKIE ASK http://w.y.z/ Set-Cookie: some_value=value2; Path="/"; expires=%NEXTYEAR%
COOKIE ASK http://w.y.z/ Set-Cookie: some_value=value3; Path="/"; expires=%NEXTYEAR%
CHECK http://w.y.z/ Cookie: some_value=value3
## Check if multiple cookies work
COOKIE ASK http://w.y.z/ Set-Cookie: some_value2=foobar; Path="/"; expires=%NEXTYEAR%
CHECK http://w.y.z/ Cookie: some_value=value3; some_value2=foobar
COOKIE ASK http://w.y.z/ Set-Cookie: some_value=; Path="/"; expires=%LASTYEAR%
CHECK http://w.y.z/ Cookie: some_value2=foobar
CLEAR COOKIES
## Check if path restrictions work
COOKIE ASK http://w.y.z/ Set-Cookie: some_value=value1; Path="/Foo"; expires=%NEXTYEAR%
CHECK http://w.y.z/
CHECK http://w.y.z/Foo Cookie: some_value=value1
CHECK http://w.y.z/Foo/ Cookie: some_value=value1
CHECK http://w.y.z/Foo/bar Cookie: some_value=value1
CLEAR COOKIES
## Check if default path works
# RFC2965 says that we should default to the URL path, but netscape cookies default to /
COOKIE ASK http://w.y.z/Foo/ Set-Cookie: some_value=value1; expires=%NEXTYEAR%
CHECK http://w.y.z/
CHECK http://w.y.z/Foo Cookie: some_value=value1
CHECK http://w.y.z/FooBar
CHECK http://w.y.z/Foo/ Cookie: some_value=value1
CHECK http://w.y.z/Foo/bar Cookie: some_value=value1
CLEAR COOKIES
## Check if cookies are correctly ordered based on path
COOKIE ASK http://w.y.z/ Set-Cookie: some_value=value1; Path="/Foo"; expires=%NEXTYEAR%
COOKIE ASK http://w.y.z/ Set-Cookie: some_value2=value2; Path="/Foo/Bar"; expires=%NEXTYEAR%
CHECK http://w.y.z/Foo/Bar Cookie: some_value2=value2; some_value=value1
COOKIE ASK http://w.y.z/ Set-Cookie: some_value3=value3; Path="/"; expires=%NEXTYEAR%
CHECK http://w.y.z/Foo/Bar Cookie: some_value2=value2; some_value=value1; some_value3=value3
CLEAR COOKIES
## Check cookies with same name but different paths
COOKIE ASK http://w.y.z/Foo/ Set-Cookie: some_value=value1; expires=%NEXTYEAR%
COOKIE ASK http://w.y.z/Bar/ Set-Cookie: some_value=value2; expires=%NEXTYEAR%
CHECK http://w.y.z/Foo/Bar Cookie: some_value=value1
CHECK http://w.y.z/Bar/Foo Cookie: some_value=value2
COOKIE ASK http://w.y.z/ Set-Cookie: some_value=value3; expires=%NEXTYEAR%
CHECK http://w.y.z/Foo/Bar Cookie: some_value=value1; some_value=value3
## Check secure cookie handling
COOKIE ASK https://secure.y.z/ Set-Cookie: some_value2=value2; Path="/"; expires=%NEXTYEAR%; secure
CHECK https://secure.y.z/Foo/bar Cookie: some_value2=value2
CHECK http://secure.y.z/Foo/bar
CLEAR COOKIES
COOKIE ASK http://secure.y.z/ Set-Cookie: some_value3=value3; Path="/"; expires=%NEXTYEAR%; secure
CHECK https://secure.y.z/Foo/bar Cookie: some_value3=value3
CHECK http://secure.y.z/Foo/bar
CLEAR COOKIES
## Check domain restrictions #1
COOKIE ASK http://www.acme.com/ Set-Cookie: some_value=value1; Domain=".acme.com"; expires=%NEXTYEAR%
CHECK http://www.acme.com/ Cookie: some_value=value1
CHECK http://www.abc.com/
CHECK http://frop.acme.com/ Cookie: some_value=value1
CLEAR COOKIES
## Check domain restrictions #2
COOKIE ASK http://novell.com/ Set-Cookie: some_value=value1; Domain=".novell.com"; expires=%NEXTYEAR%
CHECK http://novell.com/ Cookie: some_value=value1
CHECK http://www.novell.com/ Cookie: some_value=value1
CLEAR COOKIES
COOKIE ASK http://novell.com/ Set-Cookie: some_value=value1; Domain="novell.com"; expires=%NEXTYEAR%
CHECK http://novell.com/ Cookie: some_value=value1
CHECK http://www.novell.com/ Cookie: some_value=value1
CLEAR COOKIES
## Check domain restrictions #3
COOKIE ASK http://novell.com/ Set-Cookie: some_value=value1; expires=%NEXTYEAR%
CHECK http://novell.com/ Cookie: some_value=value1
# FIXME: Allegedly IE sends cookies to sub-domains as well!
# See e.g. https://bugzilla.mozilla.org/show_bug.cgi?id=223027
CHECK http://www.novell.com/
CLEAR COOKIES
## Check domain restrictions #4
COOKIE ASK http://novell.com/ Set-Cookie: some_value=value1; Domain=".com"; expires=%NEXTYEAR%
CHECK http://novell.com/ Cookie: some_value=value1
# If the specified domain is too broad, we default to host only
CHECK http://www.novell.com/
CHECK http://com/
CHECK http://sun.com/
## Check domain restrictions #5
CLEAR COOKIES
COOKIE ASK http://novell.co.uk/ Set-Cookie: some_value=value1; Domain=".co.uk"; expires=%NEXTYEAR%
CHECK http://novell.co.uk/ Cookie: some_value=value1
# If the specified domain is too broad, we default to host only
CHECK http://www.novell.co.uk/
CHECK http://co.uk/
CHECK http://sun.co.uk/
COOKIE ASK http://x.y.z.foobar.com/ Set-Cookie: set_by=x.y.z.foobar.com; Domain=".foobar.com"; expires=%NEXTYEAR%
CHECK http://x.y.z.foobar.com/ Cookie: set_by=x.y.z.foobar.com
CHECK http://y.z.foobar.com/ Cookie: set_by=x.y.z.foobar.com
CHECK http://z.foobar.com/ Cookie: set_by=x.y.z.foobar.com
CHECK http://www.foobar.com/ Cookie: set_by=x.y.z.foobar.com
CHECK http://foobar.com/ Cookie: set_by=x.y.z.foobar.com
CLEAR COOKIES
## Check domain restrictions #6
COOKIE ASK http://x.y.z.frop.com/ Set-Cookie: set_by=x.y.z.frop.com; Domain=".foobar.com"; expires=%NEXTYEAR%
COOKIE ASK http://x.y.z.frop.com/ Set-Cookie: set_by2=x.y.z.frop.com; Domain=".com"; expires=%NEXTYEAR%
CHECK http://x.y.z.foobar.com/
CHECK http://y.z.foobar.com/
CHECK http://z.foobar.com/
CHECK http://www.foobar.com/
CHECK http://foobar.com/
CLEAR COOKIES
## Check domain restrictions #7
COOKIE ASK http://frop.com/ Set-Cookie: set_by=x.y.z.frop.com; Domain=".foobar.com"; expires=%NEXTYEAR%
COOKIE ASK http://frop.com/ Set-Cookie: set_by2=x.y.z.frop.com; Domain=".com"; expires=%NEXTYEAR%
CHECK http://x.y.z.foobar.com/
CHECK http://y.z.foobar.com/
CHECK http://z.foobar.com/
CHECK http://www.foobar.com/
CHECK http://foobar.com/
CLEAR COOKIES
## Check domain restrictions #8
CONFIG AcceptSessionCookies true
COOKIE ACCEPT http://www.foobar.com Set-Cookie: from=foobar.com; domain=bar.com; Path="/"
CHECK http://bar.com
CLEAR CONFIG
CLEAR COOKIES
## Check cookies with IP address hostnames
COOKIE ASK http://192.168.0.1 Set-Cookie: name1=value1; Path="/"; expires=%NEXTYEAR%
COOKIE ASK http://192.168.0.1 Set-Cookie: name11=value11; domain="test.local"; Path="/"; expires=%NEXTYEAR%
COOKIE ASK http://192.168.0.1:8080 Set-Cookie: name2=value2; Path="/"; expires=%NEXTYEAR%
COOKIE ASK https://192.168.0.1 Set-Cookie: name3=value3; Path="/"; expires=%NEXTYEAR%; secure
CHECK http://192.168.0.1 Cookie: name1=value1; name11=value11; name2=value2
CHECK http://192.168.0.1:8080 Cookie: name1=value1; name11=value11; name2=value2
CHECK https://192.168.0.1 Cookie: name1=value1; name11=value11; name2=value2; name3=value3
CHECK http://192.168.0.10
CHECK http://192.168.0
CLEAR COOKIES
## Check expiration dates for the Y2K38 problem
COOKIE ASK http://foo.bar Set-Cookie: name=value;expires=Tue, 06-Dec-2039 00:30:42 GMT;path="/"
CHECK http://foo.bar Cookie: name=value
CLEAR COOKIES
## Check non-standard expiration dates (BR# 145244)
COOKIE ASK http://foo.bar Set-Cookie: name=value; expires=Sat Sep 12 07:00:00 2020 GMT; path="/"
COOKIE ASK http://foo.bar Set-Cookie: name1=value1; expires=Thu, 01 Jan 1970 00:00:00 GMT; path="/"
COOKIE ASK http://foo.bar Set-Cookie: name2=value2; expires=Sat Sep 12 2020 07:00:00 GMT; path="/"
CHECK http://foo.bar Cookie: name=value; name2=value2
CLEAR COOKIES
## Check path restrictions
COOKIE ASK http://a.b.c/app1 Set-Cookie: name=value; Path="/app1"; expires=%NEXTYEAR%
COOKIE ASK http://a.b.c/app2 Set-Cookie: name1=value1; Path="/app2"; expires=%NEXTYEAR%
CHECK http://a.b.c/app1 Cookie: name=value
## Check invalid weekday value in expire headers (BR# 298660)
COOKIE ASK http://foo.bar Set-Cookie: name=value; expires=Thu, 01 Jan 1970 00:00:00 GMT; path="/"
COOKIE ASK http://foo.bar Set-Cookie: name1=value1; expires=Thu, 30 Dec 2037 00:00:00 GMT; path="/"
CLEAR SESSIONCOOKIES
CHECK http://foo.bar Cookie: name1=value1
CLEAR COOKIES
## Check JSON formatted cookie values (QTBUG-26002)
COOKIE ASK http://www.foo.bar Set-Cookie: name={"value":"null","value2":"null","value3":"null"}; domain=.foo.bar; path=/
CHECK http://www.foo.bar Cookie: name={"value":"null","value2":"null","value3":"null"}

View file

@ -1,172 +0,0 @@
## Check setting of cookies
COOKIE ASK http://w.y.z/ Set-Cookie2: some_value="value1"; Version=1; Path="/"; Max-Age=3600
# Although the examples in RFC2965 uses $Version="1" the syntax description suggests that
# such quotes are not allowed, KDE BR59990 reports that the Sun Java server fails to handle
# cookies that use $Version="1"
CHECK http://w.y.z/ Cookie: $Version=1; some_value="value1"; $Path="/"
COOKIE ASK http://a.b.c/ Set-Cookie2: some_value="value2"; Version=1; Path="/"
CHECK http://a.b.c/ Cookie: $Version=1; some_value="value2"; $Path="/"
## Check if clearing cookie jar works
CLEAR COOKIES
CHECK http://w.y.z/
CHECK http://a.b.c/
## Check cookie syntax
COOKIE ASK http://w.y.z/ Set-Cookie2: some_value="value with spaces"; Version=1
CHECK http://w.y.z/ Cookie: $Version=1; some_value="value with spaces"
COOKIE ASK http://w.y.z/ Set-Cookie2: some_value ="extra space 1"; Version=1
CHECK http://w.y.z/ Cookie: $Version=1; some_value="extra space 1"
COOKIE ASK http://w.y.z/ Set-Cookie2: some_value= "extra space 2"; Version=1
CHECK http://w.y.z/ Cookie: $Version=1; some_value="extra space 2"
COOKIE ASK http://a.b.c/ Set-Cookie2: some_value=unquoted; Version=1
CHECK http://a.b.c/ Cookie: $Version=1; some_value=unquoted
# Note that we parse this different for Netscape-style cookies!
COOKIE ASK http://a.b.c/ Set-Cookie2: some_value="quoted value; and such"; Version=1;
CHECK http://a.b.c/ Cookie: $Version=1; some_value="quoted value; and such"
CLEAR COOKIES
## Check if deleting cookies works #1
COOKIE ASK http://w.y.z/ Set-Cookie2: some_value="value1"; Version=1; Path="/"; Max-Age=3600
CHECK http://w.y.z/ Cookie: $Version=1; some_value="value1"; $Path="/"
COOKIE ASK http://w.y.z/ Set-Cookie2: some_value=value1; Version=1; Path="/"; Max-Age=0
CHECK http://w.y.z/
## Check if updating cookies works
COOKIE ASK http://w.y.z/ Set-Cookie2: some_value=value2; Version=1; Path="/"; Max-Age=3600
COOKIE ASK http://w.y.z/ Set-Cookie2: some_value=value3; Version=1; Path="/"; Max-Age=3600
CHECK http://w.y.z/ Cookie: $Version=1; some_value=value3; $Path="/"
## Check if multiple cookies work
COOKIE ASK http://w.y.z/ Set-Cookie2: some_value2=foobar; Version=1; Path="/"; Max-Age=3600
CHECK http://w.y.z/ Cookie: $Version=1; some_value=value3; $Path="/"; some_value2=foobar; $Path="/"
COOKIE ASK http://w.y.z/ Set-Cookie2: some_value=; Version=1; Path="/"; Max-Age=0
CHECK http://w.y.z/ Cookie: $Version=1; some_value2=foobar; $Path="/"
CLEAR COOKIES
## Check if we prepend domain with a dot
COOKIE ASK http://w.y.z/ Set-Cookie2: some_value=value2; Version=1; Path="/"; Domain=.y.z; Max-Age=3600
COOKIE ASK http://w.y.z/ Set-Cookie2: some_value=value3; Version=1; Path="/"; Domain=y.z.; Max-Age=3600
CHECK http://w.y.z/ Cookie: $Version=1; some_value=value3; $Path="/"; $Domain=".y.z"
CLEAR COOKIES
## Check if multiple cookies on a single line work
## FIXME
#COOKIE ASK http://w.y.z/ Set-Cookie2: some_value=value3; Version=1; Path="/"; Max-Age=3600, some_value2=foobar; Version=1; Path="/"; Max-Age=3600
# CHECK http://w.y.z/ Cookie: $Version=1; some_value2=foobar; $Path="/"; some_value=value3; $Path="/"
# COOKIE ASK http://w.y.z/ Set-Cookie2: some_value=; Version=1; Path="/"; Max-Age=0
# CHECK http://w.y.z/ Cookie: $Version=1; some_value2=foobar; $Path="/"
CLEAR COOKIES
## Check if path restrictions work
COOKIE ASK http://w.y.z/ Set-Cookie2: some_value=value1; Version=1; Path="/Foo"; Max-Age=3600
CHECK http://w.y.z/
CHECK http://w.y.z/Foo Cookie: $Version=1; some_value=value1; $Path="/Foo"
CHECK http://w.y.z/Foo/ Cookie: $Version=1; some_value=value1; $Path="/Foo"
CHECK http://w.y.z/Foo/bar Cookie: $Version=1; some_value=value1; $Path="/Foo"
CLEAR COOKIES
## Check if default path works
# RFC2965 says that we should default to the URL path
COOKIE ASK http://w.y.z/Foo/ Set-Cookie2: some_value=value1; Version=1; Max-Age=3600
CHECK http://w.y.z/
CHECK http://w.y.z/Foo Cookie: $Version=1; some_value=value1
CHECK http://w.y.z/FooBar
CHECK http://w.y.z/Foo/ Cookie: $Version=1; some_value=value1
CHECK http://w.y.z/Foo/bar Cookie: $Version=1; some_value=value1
CLEAR COOKIES
## Check if cookies are correctly ordered based on path
COOKIE ASK http://w.y.z/ Set-Cookie2: some_value=value1; Version=1; Path="/Foo"; Max-Age=3600
COOKIE ASK http://w.y.z/ Set-Cookie2: some_value2=value2; Version=1; Path="/Foo/Bar"; Max-Age=3600
CHECK http://w.y.z/Foo/Bar Cookie: $Version=1; some_value2=value2; $Path="/Foo/Bar"; some_value=value1; $Path="/Foo"
COOKIE ASK http://w.y.z/ Set-Cookie2: some_value3=value3; Version=1; Path="/"; Max-Age=3600
CHECK http://w.y.z/Foo/Bar Cookie: $Version=1; some_value2=value2; $Path="/Foo/Bar"; some_value=value1; $Path="/Foo"; some_value3=value3; $Path="/"
CLEAR COOKIES
## Check cookies with same name but different paths
COOKIE ASK http://w.y.z/Foo/ Set-Cookie2: some_value=value1; Version=1; Max-Age=3600
COOKIE ASK http://w.y.z/Bar/ Set-Cookie2: some_value=value2; Version=1; Max-Age=3600
CHECK http://w.y.z/Foo/Bar Cookie: $Version=1; some_value=value1
CHECK http://w.y.z/Bar/Foo Cookie: $Version=1; some_value=value2
COOKIE ASK http://w.y.z/ Set-Cookie2: some_value=value3; Version=1; Max-Age=3600
CHECK http://w.y.z/Foo/Bar Cookie: $Version=1; some_value=value1; some_value=value3
CLEAR COOKIES
## Check port selection handling (rfc 2965 3.3.4)
COOKIE ASK http://w.y.z/ Set-Cookie2: some_value=value1; Version=1; Domain=.y.z; Port
CHECK http://foo.y.z/ Cookie: $Version=1; some_value=value1; $Domain=".y.z"; $Port
CHECK http://foo.y.z:8080
COOKIE ASK http://w.y.z/ Set-Cookie2: some_value=value1; Version=1; Domain=.y.z; Port="80 8080 443"
CHECK http://foo.y.z/ Cookie: $Version=1; some_value=value1; $Domain=".y.z"; $Port="80 8080 443"
CHECK http://foo.y.z:8080 Cookie: $Version=1; some_value=value1; $Domain=".y.z"; $Port="80 8080 443"
CHECK http://foo.y.z:443 Cookie: $Version=1; some_value=value1; $Domain=".y.z"; $Port="80 8080 443"
CHECK http://w.y.z:3129
COOKIE ASK http://w.y.z/ Set-Cookie2: some_value=value1; Version=1; Domain=.y.z
CHECK http://w.y.z:80 Cookie: $Version=1; some_value=value1; $Domain=".y.z"
CHECK http://w.y.z:443 Cookie: $Version=1; some_value=value1; $Domain=".y.z"
CHECK http://w.y.z:3129 Cookie: $Version=1; some_value=value1; $Domain=".y.z"
CHECK http://w.y.z:8080 Cookie: $Version=1; some_value=value1; $Domain=".y.z"
CLEAR COOKIES
## Check secure cookie handling
COOKIE ASK https://secure.y.z/ Set-Cookie2: some_value2=value2; Version=1; Path="/"; Max-Age=3600; Secure
CHECK https://secure.y.z/Foo/bar Cookie: $Version=1; some_value2=value2; $Path="/"
CHECK http://secure.y.z/Foo/bar
CLEAR COOKIES
COOKIE ASK http://secure.y.z/ Set-Cookie2: some_value3=value3; Version=1; Path="/"; Max-Age=3600; Secure
CHECK https://secure.y.z/Foo/bar Cookie: $Version=1; some_value3=value3; $Path="/"
CHECK http://secure.y.z/Foo/bar
CLEAR COOKIES
COOKIE ASK https://secure.y.z/ Set-Cookie: some_value=value; Path="/"; Max-Age=3600;
CHECK https://secure.y.z/Foo/bar Cookie: some_value=value
CHECK http://secure.y.z/Foo/bar Cookie: some_value=value
CLEAR COOKIES
COOKIE ASK http://secure.y.z/ Set-Cookie: some_value=value; Path="/"; Max-Age=3600;
CHECK https://secure.y.z/Foo/bar Cookie: some_value=value
CHECK http://secure.y.z/Foo/bar Cookie: some_value=value
CLEAR COOKIES
## Check domain restrictions #1
COOKIE ASK http://www.acme.com/ Set-Cookie2: some_value=value1; Version=1; Domain=".acme.com"; Max-Age=3600
CHECK http://www.acme.com/ Cookie: $Version=1; some_value=value1; $Domain=".acme.com"
CHECK http://www.abc.com/
CHECK http://frop.acme.com/ Cookie: $Version=1; some_value=value1; $Domain=".acme.com"
CLEAR COOKIES
## Check domain restrictions #2
COOKIE ASK http://novell.com/ Set-Cookie2: some_value=value1; Version=1; Domain=".novell.com"; Max-Age=3600
CHECK http://novell.com/ Cookie: $Version=1; some_value=value1; $Domain=".novell.com"
CHECK http://www.novell.com/ Cookie: $Version=1; some_value=value1; $Domain=".novell.com"
CLEAR COOKIES
## Check domain restrictions #3
COOKIE ASK http://novell.com/ Set-Cookie2: some_value=value1; Version=1; Max-Age=3600
CHECK http://novell.com/ Cookie: $Version=1; some_value=value1
CHECK http://www.novell.com/
CLEAR COOKIES
## Check domain restrictions #4
COOKIE ASK http://novell.com/ Set-Cookie2: some_value=value1; Version=1; Domain=".com"; Max-Age=3600
# If the specified domain is too broad, we ignore the Domain
# FIXME: RFC2965 says we should ignore the cookie completely
CHECK http://novell.com/ Cookie: $Version=1; some_value=value1
CHECK http://www.novell.com/
CHECK http://com/
CHECK http://sun.com/
## Check domain restrictions #5
CLEAR COOKIES
COOKIE ASK http://novell.co.uk/ Set-Cookie2: some_value=value1; Version=1; Domain=".co.uk"; Max-Age=3600
# If the specified domain is too broad, we default to host only
# FIXME: RFC2965 says we should ignore the cookie completely
CHECK http://novell.co.uk/ Cookie: $Version=1; some_value=value1
CHECK http://www.novell.co.uk/
CHECK http://co.uk/
CHECK http://sun.co.uk/
COOKIE ASK http://x.y.z.foobar.com/ Set-Cookie2: set_by=x.y.z.foobar.com; Version=1; Domain=".foobar.com"; Max-Age=3600
CHECK http://x.y.z.foobar.com/ Cookie: $Version=1; set_by=x.y.z.foobar.com; $Domain=".foobar.com"
CHECK http://y.z.foobar.com/ Cookie: $Version=1; set_by=x.y.z.foobar.com; $Domain=".foobar.com"
CHECK http://z.foobar.com/ Cookie: $Version=1; set_by=x.y.z.foobar.com; $Domain=".foobar.com"
CHECK http://www.foobar.com/ Cookie: $Version=1; set_by=x.y.z.foobar.com; $Domain=".foobar.com"
CHECK http://foobar.com/ Cookie: $Version=1; set_by=x.y.z.foobar.com; $Domain=".foobar.com"
CLEAR COOKIES
## Check domain restrictions #6
COOKIE ASK http://x.y.z.frop.com/ Set-Cookie2: set_by=x.y.z.frop.com; Version=1; Domain=".foobar.com"; Max-Age=3600
COOKIE ASK http://x.y.z.frop.com/ Set-Cookie2: set_by2=x.y.z.frop.com; Version=1; Domain=".com"; Max-Age=3600
CHECK http://x.y.z.foobar.com/
CHECK http://y.z.foobar.com/
CHECK http://z.foobar.com/
CHECK http://www.foobar.com/
CHECK http://foobar.com/
CLEAR COOKIES
## Check domain restrictions #7
COOKIE ASK http://frop.com/ Set-Cookie2: set_by=x.y.z.frop.com; Version=1; Domain=".foobar.com"; Max-Age=3600
COOKIE ASK http://frop.com/ Set-Cookie2: set_by2=x.y.z.frop.com; Version=1; Domain=".com"; Max-Age=3600
CHECK http://x.y.z.foobar.com/
CHECK http://y.z.foobar.com/
CHECK http://z.foobar.com/
CHECK http://www.foobar.com/
CHECK http://foobar.com/

View file

@ -1,434 +0,0 @@
## Check setting of cookies
COOKIE ASK http://w.y.z/ Set-Cookie: some_value=value1; Path="/"; expires=%NEXTYEAR%
COOKIE ASK http://a.b.c/ Set-Cookie: some_value=value2; Path="/"
## Check if clearing cookie jar works
CLEAR COOKIES
## Check cookie syntax
COOKIE ASK http://w.y1.z/ Set-Cookie: some_value=value with spaces; expires=%NEXTYEAR%
COOKIE ASK http://a.b1.c/ Set-Cookie: some_value="quoted value"; expires=%NEXTYEAR%
# Without a = sign, the cookie gets interpreted as the value for a cookie with no name
# This is what IE and Netscape does
COOKIE ASK http://a.b1.c/ Set-Cookie: some_value
COOKIE ASK http://a.b1.c/ Set-Cookie: some_other_value; expires=%NEXTYEAR%
# This doesn't work with old-style netscape cookies, it should work with RFC2965 cookies
COOKIE ASK http://a.b2.c/ Set-Cookie: some_value="quoted value; and such"; expires=%NEXTYEAR%
# IE & Netscape does this:
## Check if deleting cookies works
COOKIE ASK http://w.y3.z/ Set-Cookie: some_value=value1; Path="/"; expires=%NEXTYEAR%
COOKIE ASK http://w.y3.z/ Set-Cookie: some_value=value1; Path="/"; expires=%LASTYEAR%
## Check if updating cookies works
COOKIE ASK http://w.y3.z/ Set-Cookie: some_value=value2; Path="/"; expires=%NEXTYEAR%
COOKIE ASK http://w.y3.z/ Set-Cookie: some_value=value3; Path="/"; expires=%NEXTYEAR%
## Check if multiple cookies work
COOKIE ASK http://w.y3.z/ Set-Cookie: some_value2=foobar; Path="/"; expires=%NEXTYEAR%
COOKIE ASK http://w.y3.z/ Set-Cookie: some_value=; Path="/"; expires=%LASTYEAR%
## Check if path restrictions work
COOKIE ASK http://w.y4.z/ Set-Cookie: some_value=value1; Path="/Foo"; expires=%NEXTYEAR%
## Check if default path works
COOKIE ASK http://w.y5.z/Foo/ Set-Cookie: some_value=value1; expires=%NEXTYEAR%
## Check if cookies are correctly ordered based on path
COOKIE ASK http://w.y6.z/ Set-Cookie: some_value=value1; Path="/Foo"; expires=%NEXTYEAR%
COOKIE ASK http://w.y6.z/ Set-Cookie: some_value2=value2; Path="/Foo/Bar"; expires=%NEXTYEAR%
COOKIE ASK http://w.y6.z/ Set-Cookie: some_value3=value3; Path="/"; expires=%NEXTYEAR%
## Check cookies with same name but different paths
COOKIE ASK http://w.y7.z/Foo/ Set-Cookie: some_value=value1; expires=%NEXTYEAR%
COOKIE ASK http://w.y7.z/Bar/ Set-Cookie: some_value=value2; expires=%NEXTYEAR%
COOKIE ASK http://w.y7.z/ Set-Cookie: some_value=value3; expires=%NEXTYEAR%
## Check secure cookie handling
COOKIE ASK https://secure.y7.z/ Set-Cookie: some_value2=value2; Path="/"; expires=%NEXTYEAR%; secure
COOKIE ASK http://secure.y8.z/ Set-Cookie: some_value3=value3; Path="/"; expires=%NEXTYEAR%; secure
## Check domain restrictions #1
COOKIE ASK http://www.acme9.com/ Set-Cookie: some_value=value1; Domain=".acme9.com"; expires=%NEXTYEAR%
## Check domain restrictions #2
COOKIE ASK http://novell10.com/ Set-Cookie: some_value=value1; Domain=".novell10.com"; expires=%NEXTYEAR%
COOKIE ASK http://novell11.com/ Set-Cookie: some_value=value1; Domain="novell11.com"; expires=%NEXTYEAR%
## Check domain restrictions #3
COOKIE ASK http://novell12.com/ Set-Cookie: some_value=value1; expires=%NEXTYEAR%
## Check domain restrictions #4
COOKIE ASK http://novell13.com/ Set-Cookie: some_value=value1; Domain=".com"; expires=%NEXTYEAR%
# If the specified domain is too broad, we default to host only
## Check domain restrictions #5
COOKIE ASK http://novell14.co.uk/ Set-Cookie: some_value=value1; Domain=".co.uk"; expires=%NEXTYEAR%
COOKIE ASK http://x.y.z.foobar14.com/ Set-Cookie: set_by=x.y.z.foobar14.com; Domain=".foobar14.com"; expires=%NEXTYEAR%
## Check domain restrictions #6
COOKIE ASK http://x.y.z.frop15.com/ Set-Cookie: set_by=x.y.z.frop15.com; Domain=".foobar15.com"; expires=%NEXTYEAR%
COOKIE ASK http://x.y.z.frop15.com/ Set-Cookie: set_by2=x.y.z.frop15.com; Domain=".com"; expires=%NEXTYEAR%
## Check domain restrictions #7
COOKIE ASK http://frop16.com/ Set-Cookie: set_by=x.y.z.frop16.com; Domain=".foobar16.com"; expires=%NEXTYEAR%
COOKIE ASK http://frop16.com/ Set-Cookie: set_by2=x.y.z.frop16.com; Domain=".com"; expires=%NEXTYEAR%
## RFC Cookies
## Check setting of cookies
COOKIE ASK http://w.y20.z/ Set-Cookie2: some_value="value1"; Version=1; Path="/"; Max-Age=3600
# Although the examples in RFC2965 uses $Version="1" the syntax description suggests that
# such quotes are not allowed, KDE BR59990 reports that the Sun Java server fails to handle
# cookies that use $Version="1"
COOKIE ASK http://a.b20.c/ Set-Cookie2: some_value="value2"; Version=1; Path="/"; Max-Age=3600
## Check cookie syntax
COOKIE ASK http://w.y21.z/ Set-Cookie2: some_value="value with spaces"; Version=1; Max-Age=3600
COOKIE ASK http://w.y21.z/ Set-Cookie2: some_value ="extra space 1"; Version=1; Max-Age=3600
COOKIE ASK http://w.y21.z/ Set-Cookie2: some_value= "extra space 2"; Version=1; Max-Age=3600
COOKIE ASK http://a.b21.c/ Set-Cookie2: some_value=unquoted; Version=1; Max-Age=3600
# Note that we parse this different for Netscape-style cookies!
COOKIE ASK http://a.b21.c/ Set-Cookie2: some_value="quoted value; and such"; Version=1; Max-Age=3600
## Check if deleting cookies works #1
COOKIE ASK http://w.y22.z/ Set-Cookie2: some_value="value1"; Version=1; Path="/"; Max-Age=3600
COOKIE ASK http://w.y22.z/ Set-Cookie2: some_value=value1; Version=1; Path="/"; Max-Age=0
## Check if updating cookies works
COOKIE ASK http://w.y22.z/ Set-Cookie2: some_value=value2; Version=1; Path="/"; Max-Age=3600
COOKIE ASK http://w.y22.z/ Set-Cookie2: some_value=value3; Version=1; Path="/"; Max-Age=3600
## Check if multiple cookies work
COOKIE ASK http://w.y22.z/ Set-Cookie2: some_value2=foobar; Version=1; Path="/"; Max-Age=3600
COOKIE ASK http://w.y22.z/ Set-Cookie2: some_value=; Version=1; Path="/"; Max-Age=0
## Check if path restrictions work
COOKIE ASK http://w.y23.z/ Set-Cookie2: some_value=value1; Version=1; Path="/Foo"; Max-Age=3600
## Check if default path works
# RFC2965 says that we should default to the URL path
COOKIE ASK http://w.y24.z/Foo/ Set-Cookie2: some_value=value1; Version=1; Max-Age=3600
## Check if cookies are correctly ordered based on path
COOKIE ASK http://w.y25.z/ Set-Cookie2: some_value=value1; Version=1; Path="/Foo"; Max-Age=3600
COOKIE ASK http://w.y25.z/ Set-Cookie2: some_value2=value2; Version=1; Path="/Foo/Bar"; Max-Age=3600
COOKIE ASK http://w.y25.z/ Set-Cookie2: some_value3=value3; Version=1; Path="/"; Max-Age=3600
## Check cookies with same name but different paths
COOKIE ASK http://w.y26.z/Foo/ Set-Cookie2: some_value=value1; Version=1; Max-Age=3600
COOKIE ASK http://w.y26.z/Bar/ Set-Cookie2: some_value=value2; Version=1; Max-Age=3600
COOKIE ASK http://w.y26.z/ Set-Cookie2: some_value=value3; Version=1; Max-Age=3600
## Check secure cookie handling
COOKIE ASK https://secure.y26.z/ Set-Cookie2: some_value2=value2; Version=1; Path="/"; Max-Age=3600; Secure
COOKIE ASK http://secure.y27.z/ Set-Cookie2: some_value3=value3; Version=1; Path="/"; Max-Age=3600; Secure
## Check domain restrictions #1
COOKIE ASK http://www.acme28.com/ Set-Cookie2: some_value=value1; Version=1; Domain=".acme28.com"; Max-Age=3600
## Check domain restrictions #2
COOKIE ASK http://novell29.com/ Set-Cookie2: some_value=value1; Version=1; Domain=".novell29.com"; Max-Age=3600
## Check domain restrictions #3
COOKIE ASK http://novell30.com/ Set-Cookie2: some_value=value1; Version=1; Max-Age=3600
## Check domain restrictions #4
COOKIE ASK http://novell31.com/ Set-Cookie2: some_value=value1; Version=1; Domain=".com"; Max-Age=3600
# If the specified domain is too broad, we ignore the Domain
# FIXME: RFC2965 says we should ignore the cookie completely
## Check domain restrictions #5
COOKIE ASK http://novell32.co.uk/ Set-Cookie2: some_value=value1; Version=1; Domain=".co.uk"; Max-Age=3600
# If the specified domain is too broad, we default to host only
# FIXME: RFC2965 says we should ignore the cookie completely
COOKIE ASK http://x.y.z.foobar33.com/ Set-Cookie2: set_by=x.y.z.foobar.com; Version=1; Domain=".foobar33.com"; Max-Age=3600
## Check domain restrictions #6
COOKIE ASK http://x.y.z.frop34.com/ Set-Cookie2: set_by=x.y.z.frop.com; Version=1; Domain=".foobar.com"; Max-Age=3600
COOKIE ASK http://x.y.z.frop34.com/ Set-Cookie2: set_by2=x.y.z.frop.com; Version=1; Domain=".com"; Max-Age=3600
## Check domain restrictions #7
COOKIE ASK http://frop35.com/ Set-Cookie2: set_by=x.y.z.frop.com; Version=1; Domain=".foobar.com"; Max-Age=3600
COOKIE ASK http://frop35.com/ Set-Cookie2: set_by2=x.y.z.frop.com; Version=1; Domain=".com"; Max-Age=3600
## Check port restrictions (RFC2965 3.3.4)
COOKIE ASK http://ports.foo.bar.com Set-Cookie2: name=value1; Version=1; Port="80 8080 443"; Max-Age=3600
## Check results
CHECK http://w.y.z/
CHECK http://a.b.c/
CHECK http://w.y1.z/ Cookie: some_value=value with spaces
CHECK http://a.b1.c/ Cookie: some_value="quoted value"; some_other_value
CHECK http://a.b2.c/ Cookie: some_value="quoted value
CHECK http://w.y3.z/ Cookie: some_value2=foobar
CHECK http://w.y4.z/
CHECK http://w.y4.z/Foo Cookie: some_value=value1
CHECK http://w.y4.z/Foo/ Cookie: some_value=value1
CHECK http://w.y4.z/Foo/bar Cookie: some_value=value1
CHECK http://w.y5.z/
CHECK http://w.y5.z/Foo Cookie: some_value=value1
CHECK http://w.y5.z/FooBar
CHECK http://w.y5.z/Foo/ Cookie: some_value=value1
CHECK http://w.y5.z/Foo/bar Cookie: some_value=value1
CHECK http://w.y6.z/Foo/Bar Cookie: some_value2=value2; some_value=value1; some_value3=value3
CHECK http://w.y7.z/Bar/Foo Cookie: some_value=value2; some_value=value3
CHECK http://w.y7.z/Foo/Bar Cookie: some_value=value1; some_value=value3
CHECK https://secure.y7.z/Foo/bar Cookie: some_value2=value2
CHECK http://secure.y7.z/Foo/bar
CHECK https://secure.y8.z/Foo/bar Cookie: some_value3=value3
CHECK http://secure.y8.z/Foo/bar
CHECK http://www.acme9.com/ Cookie: some_value=value1
CHECK http://www.abc9.com/
CHECK http://frop.acme9.com/ Cookie: some_value=value1
CHECK http://novell10.com/ Cookie: some_value=value1
CHECK http://www.novell10.com/ Cookie: some_value=value1
CHECK http://novell11.com/ Cookie: some_value=value1
CHECK http://www.novell11.com/ Cookie: some_value=value1
CHECK http://novell12.com/ Cookie: some_value=value1
CHECK http://www.novell12.com/
CHECK http://novell13.com/ Cookie: some_value=value1
CHECK http://www.novell13.com/
CHECK http://com/
CHECK http://sun13.com/
CHECK http://novell14.co.uk/ Cookie: some_value=value1
CHECK http://www.novell14.co.uk/
CHECK http://co.uk/
CHECK http://sun14.co.uk/
CHECK http://x.y.z.foobar14.com/ Cookie: set_by=x.y.z.foobar14.com
CHECK http://y.z.foobar14.com/ Cookie: set_by=x.y.z.foobar14.com
CHECK http://z.foobar14.com/ Cookie: set_by=x.y.z.foobar14.com
CHECK http://www.foobar14.com/ Cookie: set_by=x.y.z.foobar14.com
CHECK http://foobar14.com/ Cookie: set_by=x.y.z.foobar14.com
CHECK http://x.y.z.foobar15.com/
CHECK http://y.z.foobar15.com/
CHECK http://z.foobar15.com/
CHECK http://www.foobar15.com/
CHECK http://foobar15.com/
CHECK http://x.y.z.foobar16.com/
CHECK http://y.z.foobar16.com/
CHECK http://z.foobar16.com/
CHECK http://www.foobar16.com/
CHECK http://foobar16.com/
## Check results for RFC cookies
CHECK http://w.y20.z/ Cookie: $Version=1; some_value="value1"; $Path="/"
CHECK http://a.b20.c/ Cookie: $Version=1; some_value="value2"; $Path="/"
CHECK http://w.y21.z/ Cookie: $Version=1; some_value="extra space 2"
CHECK http://a.b21.c/ Cookie: $Version=1; some_value="quoted value; and such"
CHECK http://w.y22.z/ Cookie: $Version=1; some_value2=foobar; $Path="/"
CHECK http://w.y23.z/
CHECK http://w.y23.z/Foo Cookie: $Version=1; some_value=value1; $Path="/Foo"
CHECK http://w.y23.z/Foo/ Cookie: $Version=1; some_value=value1; $Path="/Foo"
CHECK http://w.y23.z/Foo/bar Cookie: $Version=1; some_value=value1; $Path="/Foo"
CHECK http://w.y24.z/
CHECK http://w.y24.z/Foo Cookie: $Version=1; some_value=value1
CHECK http://w.y24.z/FooBar
CHECK http://w.y24.z/Foo/ Cookie: $Version=1; some_value=value1
CHECK http://w.y24.z/Foo/bar Cookie: $Version=1; some_value=value1
CHECK http://w.y25.z/Foo/Bar Cookie: $Version=1; some_value2=value2; $Path="/Foo/Bar"; some_value=value1; $Path="/Foo"; some_value3=value3; $Path="/"
CHECK http://w.y26.z/Bar/Foo Cookie: $Version=1; some_value=value2; some_value=value3
CHECK http://w.y26.z/Foo/Bar Cookie: $Version=1; some_value=value1; some_value=value3
CHECK https://secure.y26.z/Foo/bar Cookie: $Version=1; some_value2=value2; $Path="/"
CHECK http://secure.y26.z/Foo/bar
CHECK https://secure.y27.z/Foo/bar Cookie: $Version=1; some_value3=value3; $Path="/"
CHECK http://secure.y27.z/Foo/bar
CHECK http://www.acme28.com/ Cookie: $Version=1; some_value=value1; $Domain=".acme28.com"
CHECK http://www.abc28.com/
CHECK http://frop.acme28.com/ Cookie: $Version=1; some_value=value1; $Domain=".acme28.com"
CHECK http://novell29.com/ Cookie: $Version=1; some_value=value1; $Domain=".novell29.com"
CHECK http://www.novell29.com/ Cookie: $Version=1; some_value=value1; $Domain=".novell29.com"
CHECK http://novell30.com/ Cookie: $Version=1; some_value=value1
CHECK http://www.novell30.com/
CHECK http://novell31.com/ Cookie: $Version=1; some_value=value1
CHECK http://www.novell31.com/
CHECK http://com/
CHECK http://sun31.com/
CHECK http://novell32.co.uk/ Cookie: $Version=1; some_value=value1
CHECK http://www.novell32.co.uk/
CHECK http://co.uk/
CHECK http://sun32.co.uk/
CHECK http://x.y.z.foobar33.com/ Cookie: $Version=1; set_by=x.y.z.foobar.com; $Domain=".foobar33.com"
CHECK http://y.z.foobar33.com/ Cookie: $Version=1; set_by=x.y.z.foobar.com; $Domain=".foobar33.com"
CHECK http://z.foobar33.com/ Cookie: $Version=1; set_by=x.y.z.foobar.com; $Domain=".foobar33.com"
CHECK http://www.foobar33.com/ Cookie: $Version=1; set_by=x.y.z.foobar.com; $Domain=".foobar33.com"
CHECK http://foobar33.com/ Cookie: $Version=1; set_by=x.y.z.foobar.com; $Domain=".foobar33.com"
CHECK http://x.y.z.foobar.com/
CHECK http://y.z.foobar.com/
CHECK http://z.foobar.com/
CHECK http://www.foobar.com/
CHECK http://foobar.com/
CHECK http://ports.foo.bar.com Cookie: $Version=1; name=value1; $Port="80 8080 443"
SAVE
## Check result after saving
CHECK http://w.y.z/
CHECK http://a.b.c/
CHECK http://w.y1.z/ Cookie: some_value=value with spaces
CHECK http://a.b1.c/ Cookie: some_value="quoted value"; some_other_value
CHECK http://a.b2.c/ Cookie: some_value="quoted value
CHECK http://w.y3.z/ Cookie: some_value2=foobar
CHECK http://w.y4.z/
CHECK http://w.y4.z/Foo Cookie: some_value=value1
CHECK http://w.y4.z/Foo/ Cookie: some_value=value1
CHECK http://w.y4.z/Foo/bar Cookie: some_value=value1
CHECK http://w.y5.z/
CHECK http://w.y5.z/Foo Cookie: some_value=value1
CHECK http://w.y5.z/FooBar
CHECK http://w.y5.z/Foo/ Cookie: some_value=value1
CHECK http://w.y5.z/Foo/bar Cookie: some_value=value1
CHECK http://w.y6.z/Foo/Bar Cookie: some_value2=value2; some_value=value1; some_value3=value3
CHECK http://w.y7.z/Bar/Foo Cookie: some_value=value2; some_value=value3
CHECK http://w.y7.z/Foo/Bar Cookie: some_value=value1; some_value=value3
CHECK https://secure.y7.z/Foo/bar Cookie: some_value2=value2
CHECK http://secure.y7.z/Foo/bar
CHECK https://secure.y8.z/Foo/bar Cookie: some_value3=value3
CHECK http://secure.y8.z/Foo/bar
CHECK http://www.acme9.com/ Cookie: some_value=value1
CHECK http://www.abc9.com/
CHECK http://frop.acme9.com/ Cookie: some_value=value1
CHECK http://novell10.com/ Cookie: some_value=value1
CHECK http://www.novell10.com/ Cookie: some_value=value1
CHECK http://novell11.com/ Cookie: some_value=value1
CHECK http://www.novell11.com/ Cookie: some_value=value1
CHECK http://novell12.com/ Cookie: some_value=value1
CHECK http://www.novell12.com/
CHECK http://novell13.com/ Cookie: some_value=value1
CHECK http://www.novell13.com/
CHECK http://com/
CHECK http://sun13.com/
CHECK http://novell14.co.uk/ Cookie: some_value=value1
CHECK http://www.novell14.co.uk/
CHECK http://co.uk/
CHECK http://sun14.co.uk/
CHECK http://x.y.z.foobar14.com/ Cookie: set_by=x.y.z.foobar14.com
CHECK http://y.z.foobar14.com/ Cookie: set_by=x.y.z.foobar14.com
CHECK http://z.foobar14.com/ Cookie: set_by=x.y.z.foobar14.com
CHECK http://www.foobar14.com/ Cookie: set_by=x.y.z.foobar14.com
CHECK http://foobar14.com/ Cookie: set_by=x.y.z.foobar14.com
CHECK http://x.y.z.foobar15.com/
CHECK http://y.z.foobar15.com/
CHECK http://z.foobar15.com/
CHECK http://www.foobar15.com/
CHECK http://foobar15.com/
CHECK http://x.y.z.foobar16.com/
CHECK http://y.z.foobar16.com/
CHECK http://z.foobar16.com/
CHECK http://www.foobar16.com/
CHECK http://foobar16.com/
## Check result for RFC cookies after saving
CHECK http://w.y20.z/ Cookie: $Version=1; some_value="value1"; $Path="/"
CHECK http://a.b20.c/ Cookie: $Version=1; some_value="value2"; $Path="/"
CHECK http://w.y21.z/ Cookie: $Version=1; some_value="extra space 2"
CHECK http://a.b21.c/ Cookie: $Version=1; some_value="quoted value; and such"
CHECK http://w.y22.z/ Cookie: $Version=1; some_value2=foobar; $Path="/"
CHECK http://w.y23.z/
CHECK http://w.y23.z/Foo Cookie: $Version=1; some_value=value1; $Path="/Foo"
CHECK http://w.y23.z/Foo/ Cookie: $Version=1; some_value=value1; $Path="/Foo"
CHECK http://w.y23.z/Foo/bar Cookie: $Version=1; some_value=value1; $Path="/Foo"
CHECK http://w.y24.z/
CHECK http://w.y24.z/Foo Cookie: $Version=1; some_value=value1
CHECK http://w.y24.z/FooBar
CHECK http://w.y24.z/Foo/ Cookie: $Version=1; some_value=value1
CHECK http://w.y24.z/Foo/bar Cookie: $Version=1; some_value=value1
CHECK http://w.y25.z/Foo/Bar Cookie: $Version=1; some_value2=value2; $Path="/Foo/Bar"; some_value=value1; $Path="/Foo"; some_value3=value3; $Path="/"
CHECK http://w.y26.z/Bar/Foo Cookie: $Version=1; some_value=value2; some_value=value3
CHECK http://w.y26.z/Foo/Bar Cookie: $Version=1; some_value=value1; some_value=value3
CHECK https://secure.y26.z/Foo/bar Cookie: $Version=1; some_value2=value2; $Path="/"
CHECK http://secure.y26.z/Foo/bar
CHECK https://secure.y27.z/Foo/bar Cookie: $Version=1; some_value3=value3; $Path="/"
CHECK http://secure.y27.z/Foo/bar
CHECK http://www.acme28.com/ Cookie: $Version=1; some_value=value1; $Domain=".acme28.com"
CHECK http://www.abc28.com/
CHECK http://frop.acme28.com/ Cookie: $Version=1; some_value=value1; $Domain=".acme28.com"
CHECK http://novell29.com/ Cookie: $Version=1; some_value=value1; $Domain=".novell29.com"
CHECK http://www.novell29.com/ Cookie: $Version=1; some_value=value1; $Domain=".novell29.com"
CHECK http://novell30.com/ Cookie: $Version=1; some_value=value1
CHECK http://www.novell30.com/
CHECK http://novell31.com/ Cookie: $Version=1; some_value=value1
CHECK http://www.novell31.com/
CHECK http://com/
CHECK http://sun31.com/
CHECK http://novell32.co.uk/ Cookie: $Version=1; some_value=value1
CHECK http://www.novell32.co.uk/
CHECK http://co.uk/
CHECK http://sun32.co.uk/
CHECK http://x.y.z.foobar33.com/ Cookie: $Version=1; set_by=x.y.z.foobar.com; $Domain=".foobar33.com"
CHECK http://y.z.foobar33.com/ Cookie: $Version=1; set_by=x.y.z.foobar.com; $Domain=".foobar33.com"
CHECK http://z.foobar33.com/ Cookie: $Version=1; set_by=x.y.z.foobar.com; $Domain=".foobar33.com"
CHECK http://www.foobar33.com/ Cookie: $Version=1; set_by=x.y.z.foobar.com; $Domain=".foobar33.com"
CHECK http://foobar33.com/ Cookie: $Version=1; set_by=x.y.z.foobar.com; $Domain=".foobar33.com"
CHECK http://x.y.z.foobar.com/
CHECK http://y.z.foobar.com/
CHECK http://z.foobar.com/
CHECK http://www.foobar.com/
CHECK http://foobar.com/
CHECK http://ports.foo.bar.com Cookie: $Version=1; name=value1; $Port="80 8080 443"
SAVE
## Check result after saving a second time
CHECK http://w.y.z/
CHECK http://a.b.c/
CHECK http://w.y1.z/ Cookie: some_value=value with spaces
CHECK http://a.b1.c/ Cookie: some_value="quoted value"; some_other_value
CHECK http://a.b2.c/ Cookie: some_value="quoted value
CHECK http://w.y3.z/ Cookie: some_value2=foobar
CHECK http://w.y4.z/
CHECK http://w.y4.z/Foo Cookie: some_value=value1
CHECK http://w.y4.z/Foo/ Cookie: some_value=value1
CHECK http://w.y4.z/Foo/bar Cookie: some_value=value1
CHECK http://w.y5.z/
CHECK http://w.y5.z/Foo Cookie: some_value=value1
CHECK http://w.y5.z/FooBar
CHECK http://w.y5.z/Foo/ Cookie: some_value=value1
CHECK http://w.y5.z/Foo/bar Cookie: some_value=value1
CHECK http://w.y6.z/Foo/Bar Cookie: some_value2=value2; some_value=value1; some_value3=value3
CHECK http://w.y7.z/Bar/Foo Cookie: some_value=value2; some_value=value3
CHECK http://w.y7.z/Foo/Bar Cookie: some_value=value1; some_value=value3
CHECK https://secure.y7.z/Foo/bar Cookie: some_value2=value2
CHECK http://secure.y7.z/Foo/bar
CHECK https://secure.y8.z/Foo/bar Cookie: some_value3=value3
CHECK http://secure.y8.z/Foo/bar
CHECK http://www.acme9.com/ Cookie: some_value=value1
CHECK http://www.abc9.com/
CHECK http://frop.acme9.com/ Cookie: some_value=value1
CHECK http://novell10.com/ Cookie: some_value=value1
CHECK http://www.novell10.com/ Cookie: some_value=value1
CHECK http://novell11.com/ Cookie: some_value=value1
CHECK http://www.novell11.com/ Cookie: some_value=value1
CHECK http://novell12.com/ Cookie: some_value=value1
CHECK http://www.novell12.com/
CHECK http://novell13.com/ Cookie: some_value=value1
CHECK http://www.novell13.com/
CHECK http://com/
CHECK http://sun13.com/
CHECK http://novell14.co.uk/ Cookie: some_value=value1
CHECK http://www.novell14.co.uk/
CHECK http://co.uk/
CHECK http://sun14.co.uk/
CHECK http://x.y.z.foobar14.com/ Cookie: set_by=x.y.z.foobar14.com
CHECK http://y.z.foobar14.com/ Cookie: set_by=x.y.z.foobar14.com
CHECK http://z.foobar14.com/ Cookie: set_by=x.y.z.foobar14.com
CHECK http://www.foobar14.com/ Cookie: set_by=x.y.z.foobar14.com
CHECK http://foobar14.com/ Cookie: set_by=x.y.z.foobar14.com
CHECK http://x.y.z.foobar15.com/
CHECK http://y.z.foobar15.com/
CHECK http://z.foobar15.com/
CHECK http://www.foobar15.com/
CHECK http://foobar15.com/
CHECK http://x.y.z.foobar16.com/
CHECK http://y.z.foobar16.com/
CHECK http://z.foobar16.com/
CHECK http://www.foobar16.com/
CHECK http://foobar16.com/
## Check result for rfc cookies after saving a second time
CHECK http://w.y20.z/ Cookie: $Version=1; some_value="value1"; $Path="/"
CHECK http://a.b20.c/ Cookie: $Version=1; some_value="value2"; $Path="/"
CHECK http://w.y21.z/ Cookie: $Version=1; some_value="extra space 2"
CHECK http://a.b21.c/ Cookie: $Version=1; some_value="quoted value; and such"
CHECK http://w.y22.z/ Cookie: $Version=1; some_value2=foobar; $Path="/"
CHECK http://w.y23.z/
CHECK http://w.y23.z/Foo Cookie: $Version=1; some_value=value1; $Path="/Foo"
CHECK http://w.y23.z/Foo/ Cookie: $Version=1; some_value=value1; $Path="/Foo"
CHECK http://w.y23.z/Foo/bar Cookie: $Version=1; some_value=value1; $Path="/Foo"
CHECK http://w.y24.z/
CHECK http://w.y24.z/Foo Cookie: $Version=1; some_value=value1
CHECK http://w.y24.z/FooBar
CHECK http://w.y24.z/Foo/ Cookie: $Version=1; some_value=value1
CHECK http://w.y24.z/Foo/bar Cookie: $Version=1; some_value=value1
CHECK http://w.y25.z/Foo/Bar Cookie: $Version=1; some_value2=value2; $Path="/Foo/Bar"; some_value=value1; $Path="/Foo"; some_value3=value3; $Path="/"
CHECK http://w.y26.z/Bar/Foo Cookie: $Version=1; some_value=value2; some_value=value3
CHECK http://w.y26.z/Foo/Bar Cookie: $Version=1; some_value=value1; some_value=value3
CHECK https://secure.y26.z/Foo/bar Cookie: $Version=1; some_value2=value2; $Path="/"
CHECK http://secure.y26.z/Foo/bar
CHECK https://secure.y27.z/Foo/bar Cookie: $Version=1; some_value3=value3; $Path="/"
CHECK http://secure.y27.z/Foo/bar
CHECK http://www.acme28.com/ Cookie: $Version=1; some_value=value1; $Domain=".acme28.com"
CHECK http://www.abc28.com/
CHECK http://frop.acme28.com/ Cookie: $Version=1; some_value=value1; $Domain=".acme28.com"
CHECK http://novell29.com/ Cookie: $Version=1; some_value=value1; $Domain=".novell29.com"
CHECK http://www.novell29.com/ Cookie: $Version=1; some_value=value1; $Domain=".novell29.com"
CHECK http://novell30.com/ Cookie: $Version=1; some_value=value1
CHECK http://www.novell30.com/
CHECK http://novell31.com/ Cookie: $Version=1; some_value=value1
CHECK http://www.novell31.com/
CHECK http://com/
CHECK http://sun31.com/
CHECK http://novell32.co.uk/ Cookie: $Version=1; some_value=value1
CHECK http://www.novell32.co.uk/
CHECK http://co.uk/
CHECK http://sun32.co.uk/
CHECK http://x.y.z.foobar33.com/ Cookie: $Version=1; set_by=x.y.z.foobar.com; $Domain=".foobar33.com"
CHECK http://y.z.foobar33.com/ Cookie: $Version=1; set_by=x.y.z.foobar.com; $Domain=".foobar33.com"
CHECK http://z.foobar33.com/ Cookie: $Version=1; set_by=x.y.z.foobar.com; $Domain=".foobar33.com"
CHECK http://www.foobar33.com/ Cookie: $Version=1; set_by=x.y.z.foobar.com; $Domain=".foobar33.com"
CHECK http://foobar33.com/ Cookie: $Version=1; set_by=x.y.z.foobar.com; $Domain=".foobar33.com"
CHECK http://x.y.z.foobar.com/
CHECK http://y.z.foobar.com/
CHECK http://z.foobar.com/
CHECK http://www.foobar.com/
CHECK http://foobar.com/
CHECK http://ports.foo.bar.com Cookie: $Version=1; name=value1; $Port="80 8080 443"

View file

@ -1,51 +0,0 @@
## Check that persistent cookies are not deleted at the end of the session
CLEAR CONFIG
CONFIG CookieGlobalAdvice Accept
COOKIE ACCEPT http://a.example1.net/ Set-Cookie: some_value=value1; Path="/"; expires=%NEXTYEAR%
COOKIE ACCEPT http://a.example2.net/ Set-Cookie: some_value=value2; Path="/"; max-age="600"
CHECK http://a.example1.net/ Cookie: some_value=value1
CHECK http://a.example2.net/ Cookie: some_value=value2
ENDSESSION
CHECK http://a.example1.net/ Cookie: some_value=value1
CHECK http://a.example2.net/ Cookie: some_value=value2
CONFIG CookieGlobalAdvice Reject
CONFIG CookieDomainAdvice a.example3.net:Accept,.example4.net:Accept
COOKIE ACCEPT http://a.example3.net/ Set-Cookie: some_value=value3; Path="/"; expires=%NEXTYEAR%
COOKIE ACCEPT http://a.example4.net/ Set-Cookie: some_value=value4; Path="/"; expires=%NEXTYEAR%
CHECK http://a.example3.net/ Cookie: some_value=value3
CHECK http://a.example4.net/ Cookie: some_value=value4
ENDSESSION
CHECK http://a.example3.net/ Cookie: some_value=value3
CHECK http://a.example4.net/ Cookie: some_value=value4
## Check that non persistent cookies are deleted at the end of the session
CLEAR CONFIG
CONFIG CookieGlobalAdvice Accept
COOKIE ACCEPT http://x.example1.net/ Set-Cookie: some_value=value1; Path="/"
CHECK http://x.example1.net/ Cookie: some_value=value1
ENDSESSION
CHECK http://x.example1.net/
CONFIG CookieGlobalAdvice AcceptForSession
COOKIE ACCEPTFORSESSION http://x.example2.net/ Set-Cookie: some_value=value2; Path="/"
COOKIE ACCEPTFORSESSION http://x.example3.net/ Set-Cookie: some_value=value3; Path="/"; expires=%NEXTYEAR%
CHECK http://x.example2.net/ Cookie: some_value=value2
CHECK http://x.example3.net/ Cookie: some_value=value3
ENDSESSION
CHECK http://x.example2.net/
CHECK http://x.example3.net/
CONFIG CookieGlobalAdvice Reject
CONFIG CookieDomainAdvice x.example4.net:AcceptForSession,.example5.net:AcceptForSession,x.y.example6.net:AcceptForSession,.y.example6.net:Accept
COOKIE ACCEPTFORSESSION http://x.example4.net/ Set-Cookie: some_value=value4; Path="/"; expires=%NEXTYEAR%
COOKIE ACCEPTFORSESSION http://x.example5.net/ Set-Cookie: some_value=value5; Path="/"; expires=%NEXTYEAR%
COOKIE ACCEPTFORSESSION http://x.y.example6.net/ Set-Cookie: some_value=value6; Path="/"; expires=%NEXTYEAR%
CHECK http://x.example4.net/ Cookie: some_value=value4
CHECK http://x.example5.net/ Cookie: some_value=value5
CHECK http://x.y.example6.net/ Cookie: some_value=value6
ENDSESSION
CHECK http://x.example4.net/
CHECK http://x.example5.net/
CHECK http://x.y.example6.net/
CONFIG AcceptSessionCookies true
COOKIE ACCEPT http://x.example7.net/ Set-Cookie: some_value=value7; Path="/"
CHECK http://x.example7.net/ Cookie: some_value=value7
ENDSESSION
CHECK http://x.example7.net/

View file

@ -1,112 +0,0 @@
## Check CookieGlobalAdvice setting
COOKIE ASK http://a.b.c/ Set-Cookie: some_value=value1; Path="/"; expires=%NEXTYEAR%
COOKIE ASK http://a.b.c/ Set-Cookie: some_value=value2; Path="/"
CONFIG CookieGlobalAdvice Reject
COOKIE REJECT http://a.b.c/ Set-Cookie: some_value=value3; Path="/"; expires=%NEXTYEAR%
COOKIE REJECT http://a.b.c/ Set-Cookie: some_value=value4; Path="/"
CONFIG CookieGlobalAdvice Accept
COOKIE ACCEPT http://a.b.c/ Set-Cookie: some_value=value5; Path="/"; expires=%NEXTYEAR%
COOKIE ACCEPT http://a.b.c/ Set-Cookie: some_value=value6; Path="/"
CONFIG CookieGlobalAdvice AcceptForSession
COOKIE ACCEPTFORSESSION http://a.b.c/ Set-Cookie: some_value=value5; Path="/"; expires=%NEXTYEAR%
COOKIE ACCEPTFORSESSION http://a.b.c/ Set-Cookie: some_value=value6; Path="/"
CONFIG CookieGlobalAdvice Ask
COOKIE ASK http://a.b.c/ Set-Cookie: some_value=value7; Path="/"; expires=%NEXTYEAR%
COOKIE ASK http://a.b.c/ Set-Cookie: some_value=value8; Path="/"
CONFIG AcceptSessionCookies true
COOKIE ASK http://a.b.c/ Set-Cookie: some_value=value9; Path="/"; expires=%NEXTYEAR%
COOKIE ASK http://a.b.c/ Set-Cookie2: some_value=value10; Version=1; Path="/"; max-age="600"
COOKIE ACCEPT http://a.b.c/ Set-Cookie: some_value=value11; Path="/"
COOKIE ACCEPT http://a.b.c/ Set-Cookie2: some_value=value12; Version=1; Path="/"
# FIXME: Shouldn't this be considered a session cookie?
# COOKIE ACCEPT http://a.b.c/ Set-Cookie2: some_value=value10; Version=1; Path="/"; max-age="0"
# COOKIE ACCEPT http://a.b.c/ Set-Cookie: some_value=value9; Path="/"; expires=%LASTYEAR%
# FIXME: The 'Discard' attribute makes the cookie a session cookie
# COOKIE ACCEPT http://a.b.c/ Set-Cookie2: some_value=value10; Version=1; Path="/"; max-age="600"
## Check host-based domain policies
CONFIG AcceptSessionCookies false
CONFIG CookieDomainAdvice a.b.c:Reject
COOKIE REJECT http://a.b.c/ Set-Cookie: some_value=value9; Path="/"; expires=%NEXTYEAR%
COOKIE REJECT http://a.b.c/ Set-Cookie2: some_value=value10; Version=1; Path="/"; max-age="600"
COOKIE REJECT http://a.b.c/ Set-Cookie: some_value=value11; Path="/"
COOKIE REJECT http://a.b.c/ Set-Cookie2: some_value=value12; Version=1; Path="/"
COOKIE ASK http://d.b.c/ Set-Cookie: some_value=value9; Path="/"; expires=%NEXTYEAR%
COOKIE ASK http://d.b.c/ Set-Cookie2: some_value=value10; Version=1; Path="/"; max-age="600"
COOKIE ASK http://d.b.c/ Set-Cookie: some_value=value11; Path="/"
COOKIE ASK http://d.b.c/ Set-Cookie2: some_value=value12; Version=1; Path="/"
## Check resetting of domain policies
CONFIG CookieDomainAdvice
COOKIE ASK http://a.b.c/ Set-Cookie: some_value=value9; Path="/"; expires=%NEXTYEAR%
COOKIE ASK http://a.b.c/ Set-Cookie2: some_value=value10; Version=1; Path="/"; max-age="600"
COOKIE ASK http://a.b.c/ Set-Cookie: some_value=value11; Path="/"
COOKIE ASK http://a.b.c/ Set-Cookie2: some_value=value12; Version=1; Path="/"
COOKIE ASK http://d.b.c/ Set-Cookie: some_value=value9; Path="/"; expires=%NEXTYEAR%
COOKIE ASK http://d.b.c/ Set-Cookie2: some_value=value10; Version=1; Path="/"; max-age="600"
COOKIE ASK http://d.b.c/ Set-Cookie: some_value=value11; Path="/"
COOKIE ASK http://d.b.c/ Set-Cookie2: some_value=value12; Version=1; Path="/"
## Check domain policies
CONFIG CookieDomainAdvice .b.c:Reject
COOKIE REJECT http://a.b.c/ Set-Cookie: some_value=value9; Path="/"; expires=%NEXTYEAR%
COOKIE REJECT http://a.b.c/ Set-Cookie2: some_value=value10; Version=1; Path="/"; max-age="600"
COOKIE REJECT http://a.b.c/ Set-Cookie: some_value=value11; Path="/"
COOKIE REJECT http://a.b.c/ Set-Cookie2: some_value=value12; Version=1; Path="/"
COOKIE REJECT http://d.b.c/ Set-Cookie: some_value=value9; Path="/"; expires=%NEXTYEAR%
COOKIE REJECT http://d.b.c/ Set-Cookie2: some_value=value10; Version=1; Path="/"; max-age="600"
COOKIE REJECT http://d.b.c/ Set-Cookie: some_value=value11; Path="/"
COOKIE REJECT http://d.b.c/ Set-Cookie2: some_value=value12; Version=1; Path="/"
## Check overriding of domain policies #1
CONFIG CookieDomainAdvice .b.c:Reject,a.b.c:Accept
COOKIE ACCEPT http://a.b.c/ Set-Cookie: some_value=value9; Path="/"; expires=%NEXTYEAR%
COOKIE ACCEPT http://a.b.c/ Set-Cookie2: some_value=value10; Version=1; Path="/"; max-age="600"
COOKIE ACCEPT http://a.b.c/ Set-Cookie: some_value=value11; Path="/"
COOKIE ACCEPT http://a.b.c/ Set-Cookie2: some_value=value12; Version=1; Path="/"
COOKIE REJECT http://d.b.c/ Set-Cookie: some_value=value9; Path="/"; expires=%NEXTYEAR%
COOKIE REJECT http://d.b.c/ Set-Cookie2: some_value=value10; Version=1; Path="/"; max-age="600"
COOKIE REJECT http://d.b.c/ Set-Cookie: some_value=value11; Path="/"
COOKIE REJECT http://d.b.c/ Set-Cookie2: some_value=value12; Version=1; Path="/"
## Check overriding of domain policies #2
CONFIG CookieDomainAdvice a.b.c:Reject,.b.c:Accept
COOKIE REJECT http://a.b.c/ Set-Cookie: some_value=value9; Path="/"; expires=%NEXTYEAR%
COOKIE REJECT http://a.b.c/ Set-Cookie2: some_value=value10; Version=1; Path="/"; max-age="600"
COOKIE REJECT http://a.b.c/ Set-Cookie: some_value=value11; Path="/"
COOKIE REJECT http://a.b.c/ Set-Cookie2: some_value=value12; Version=1; Path="/"
COOKIE ACCEPT http://d.b.c/ Set-Cookie: some_value=value9; Path="/"; expires=%NEXTYEAR%
COOKIE ACCEPT http://d.b.c/ Set-Cookie2: some_value=value10; Version=1; Path="/"; max-age="600"
COOKIE ACCEPT http://d.b.c/ Set-Cookie: some_value=value11; Path="/"
COOKIE ACCEPT http://d.b.c/ Set-Cookie2: some_value=value12; Version=1; Path="/"
## Check resetting of domain policies
CONFIG CookieDomainAdvice
COOKIE ASK http://a.b.c/ Set-Cookie: some_value=value9; Path="/"; expires=%NEXTYEAR%
COOKIE ASK http://a.b.c/ Set-Cookie2: some_value=value10; Version=1; Path="/"; max-age="600"
COOKIE ASK http://a.b.c/ Set-Cookie: some_value=value11; Path="/"
COOKIE ASK http://a.b.c/ Set-Cookie2: some_value=value12; Version=1; Path="/"
COOKIE ASK http://d.b.c/ Set-Cookie: some_value=value9; Path="/"; expires=%NEXTYEAR%
COOKIE ASK http://d.b.c/ Set-Cookie2: some_value=value10; Version=1; Path="/"; max-age="600"
COOKIE ASK http://d.b.c/ Set-Cookie: some_value=value11; Path="/"
COOKIE ASK http://d.b.c/ Set-Cookie2: some_value=value12; Version=1; Path="/"
## Check overriding of domain policies #3
CONFIG CookieDomainAdvice b.c:Reject,.b.c:Accept
COOKIE REJECT http://b.c/ Set-Cookie: some_value=value9; Path="/"; expires=%NEXTYEAR%
COOKIE REJECT http://b.c/ Set-Cookie2: some_value=value10; Version=1; Path="/"; max-age="600"
COOKIE REJECT http://b.c/ Set-Cookie: some_value=value11; Path="/"
COOKIE REJECT http://b.c/ Set-Cookie2: some_value=value12; Version=1; Path="/"
COOKIE ACCEPT http://a.b.c/ Set-Cookie: some_value=value9; Path="/"; expires=%NEXTYEAR%
COOKIE ACCEPT http://a.b.c/ Set-Cookie2: some_value=value10; Version=1; Path="/"; max-age="600"
COOKIE ACCEPT http://a.b.c/ Set-Cookie: some_value=value11; Path="/"
COOKIE ACCEPT http://a.b.c/ Set-Cookie2: some_value=value12; Version=1; Path="/"
## Check overriding of domain policies #4
CONFIG CookieDomainAdvice .a.b.c.d:Reject,.b.c.d:Accept,.c.d:Ask
COOKIE REJECT http://www.a.b.c.d/ Set-Cookie: some_value=value9; Path="/"; expires=%NEXTYEAR%
COOKIE ACCEPT http://www.b.c.d/ Set-Cookie: some_value=value9; Path="/"; expires=%NEXTYEAR%
COOKIE ASK http://www.c.d/ Set-Cookie: some_value=value9; Path="/"; expires=%NEXTYEAR%
## Check interaction with session policy
CONFIG AcceptSessionCookies true
CONFIG CookieDomainAdvice .b.c:Reject
COOKIE REJECT http://a.b.c/ Set-Cookie: some_value=value9; Path="/"; expires=%NEXTYEAR%
COOKIE REJECT http://a.b.c/ Set-Cookie2: some_value=value10; Version=1; Path="/"; max-age="600"
COOKIE ACCEPT http://a.b.c/ Set-Cookie: some_value=value11; Path="/"
COOKIE ACCEPT http://a.b.c/ Set-Cookie2: some_value=value12; Version=1; Path="/"
COOKIE REJECT http://d.b.c/ Set-Cookie: some_value=value9; Path="/"; expires=%NEXTYEAR%
COOKIE REJECT http://d.b.c/ Set-Cookie2: some_value=value10; Version=1; Path="/"; max-age="600"
COOKIE ACCEPT http://d.b.c/ Set-Cookie: some_value=value11; Path="/"
COOKIE ACCEPT http://d.b.c/ Set-Cookie2: some_value=value12; Version=1; Path="/"

View file

@ -1,313 +0,0 @@
/*
This file is part of KDE
Copyright (C) 2004 Waldo Bastian (bastian@kde.org)
Copyright 2008 David Faure <faure@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public License
as published by the Free Software Foundation; either
version 2, or (at your option) version 3.
This software is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this library; see the file COPYING. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include <QtCore/qdatetime.h>
#include <QtCore/QString>
#include <qtest_kde.h>
#include <kstandarddirs.h>
#include "../kcookiejar.cpp"
static KCookieJar *jar;
static QString *lastYear;
static QString *nextYear;
static KConfig *config = 0;
static int windowId = 1234; // random number to be used as windowId for test cookies
static void FAIL(const QString &msg)
{
qWarning("%s", msg.toLocal8Bit().data());
exit(1);
}
static void popArg(QString &command, QString & line)
{
int i = line.indexOf(' ');
if (i != -1)
{
command = line.left(i);
line = line.mid(i+1);
}
else
{
command = line;
line.clear();
}
}
static void clearConfig()
{
delete config;
QString file = KStandardDirs::locateLocal("config", "kcookiejar-testconfig");
QFile::remove(file);
config = new KConfig(file);
KConfigGroup cg(config, "Cookie Policy");
cg.writeEntry("RejectCrossDomainCookies", false);
cg.writeEntry("AcceptSessionCookies", false);
cg.writeEntry("CookieGlobalAdvice", "Ask");
jar->loadConfig(config, false);
}
static void clearCookies(bool sessionOnly = false)
{
if (sessionOnly) {
jar->eatSessionCookies(windowId);
} else {
jar->eatAllCookies();
}
}
static void saveCookies()
{
QString file = KStandardDirs::locateLocal("config", "kcookiejar-testcookies");
QFile::remove(file);
jar->saveCookies(file);
// Add an empty domain to the cookies file, just for testing robustness
QFile f(file);
f.open(QIODevice::Append);
f.write("[]\n \"\" \"/\" 1584320400 0 h 4 x\n");
f.close();
delete jar;
jar = new KCookieJar();
clearConfig();
jar->loadCookies(file);
}
static void endSession()
{
jar->eatSessionCookies(windowId);
}
static void processCookie(QString &line)
{
QString policy;
popArg(policy, line);
KCookieAdvice expectedAdvice = KCookieJar::strToAdvice(policy);
if (expectedAdvice == KCookieDunno)
FAIL(QString("Unknown accept policy '%1'").arg(policy));
QString urlStr;
popArg(urlStr, line);
KUrl url(urlStr);
if (!url.isValid())
FAIL(QString("Invalid URL '%1'").arg(urlStr));
if (url.isEmpty())
FAIL(QString("Missing URL"));
line.replace("%LASTYEAR%", *lastYear);
line.replace("%NEXTYEAR%", *nextYear);
KHttpCookieList list = jar->makeCookies(urlStr, line.toUtf8(), windowId);
if (list.isEmpty())
FAIL(QString("Failed to make cookies from: '%1'").arg(line));
for(KHttpCookieList::iterator cookieIterator = list.begin();
cookieIterator != list.end(); ++cookieIterator) {
KHttpCookie& cookie = *cookieIterator;
const KCookieAdvice cookieAdvice = jar->cookieAdvice(cookie);
if (cookieAdvice != expectedAdvice)
FAIL(urlStr+QString("\n'%2'\nGot advice '%3' expected '%4'").arg(line)
.arg(KCookieJar::adviceToStr(cookieAdvice))
.arg(KCookieJar::adviceToStr(expectedAdvice)));
jar->addCookie(cookie);
}
}
static void processCheck(QString &line)
{
QString urlStr;
popArg(urlStr, line);
KUrl url(urlStr);
if (!url.isValid())
FAIL(QString("Invalid URL '%1'").arg(urlStr));
if (url.isEmpty())
FAIL(QString("Missing URL"));
QString expectedCookies = line;
QString cookies = jar->findCookies(urlStr, false, windowId, 0).trimmed();
QCOMPARE(cookies, expectedCookies);
}
static void processClear(QString &line)
{
if (line == "CONFIG")
clearConfig();
else if (line == "COOKIES")
clearCookies();
else if (line == "SESSIONCOOKIES")
clearCookies(true);
else
FAIL(QString("Unknown command 'CLEAR %1'").arg(line));
}
static void processConfig(QString &line)
{
QString key;
popArg(key, line);
if (key.isEmpty())
FAIL(QString("Missing Key"));
KConfigGroup cg(config, "Cookie Policy");
cg.writeEntry(key, line);
jar->loadConfig(config, false);
}
static void processLine(QString line)
{
if (line.isEmpty())
return;
if (line[0] == '#')
{
if (line[1] == '#')
qDebug("%s", line.toLatin1().constData());
return;
}
QString command;
popArg(command, line);
if (command.isEmpty())
return;
if (command == "COOKIE")
processCookie(line);
else if (command == "CHECK")
processCheck(line);
else if (command == "CLEAR")
processClear(line);
else if (command == "CONFIG")
processConfig(line);
else if (command == "SAVE")
saveCookies();
else if (command == "ENDSESSION")
endSession();
else
FAIL(QString("Unknown command '%1'").arg(command));
}
static void runRegression(const QString &filename)
{
FILE *file = fopen(QFile::encodeName( filename ), "r");
if (!file)
FAIL(QString("Can't open '%1'").arg(filename));
char buf[4096];
while (fgets(buf, sizeof(buf), file))
{
int l = strlen(buf);
if (l)
{
l--;
buf[l] = 0;
}
processLine(QString::fromUtf8(buf));
}
fclose( file );
qDebug("%s OK", filename.toLocal8Bit().data());
}
class KCookieJarTest : public QObject
{
Q_OBJECT
private Q_SLOTS:
void initTestCase()
{
jar = new KCookieJar;
KDateTime dt = KDateTime::currentDateTime(KDateTime::Spec::LocalZone());
lastYear = new QString(QString("%1 01:00:00 GMT").arg(dt.addYears(-1).toString("%:a, %e-%:b-%Y")));
nextYear = new QString(QString("%1 01:00:00 GMT").arg(dt.addYears(1).toString("%:a, %e-%:b-%Y")));
}
void testCookieFile_data()
{
QTest::addColumn<QString>("fileName");
QTest::newRow("cookie.test") << KDESRCDIR "/cookie.test";
QTest::newRow("cookie_rfc.test") << KDESRCDIR "/cookie_rfc.test";
QTest::newRow("cookie_saving.test") << KDESRCDIR "/cookie_saving.test";
QTest::newRow("cookie_settings.test") << KDESRCDIR "/cookie_settings.test";
QTest::newRow("cookie_session.test") << KDESRCDIR "/cookie_session.test";
}
void testCookieFile()
{
QFETCH(QString, fileName);
clearConfig();
runRegression(fileName);
}
void testParseUrl_data()
{
QTest::addColumn<QString>("url");
QTest::addColumn<bool>("expectedResult");
QTest::addColumn<QString>("expectedFqdn");
QTest::addColumn<QString>("expectedPath");
QTest::newRow("empty") << "" << false << "" << "";
QTest::newRow("url with no path") << "http://bugs.kde.org" << true << "bugs.kde.org" << "/";
QTest::newRow("url with path") << "http://bugs.kde.org/foo" << true << "bugs.kde.org" << "/foo";
QTest::newRow("just a host") << "bugs.kde.org" << false << "" << "";
}
void testParseUrl()
{
QFETCH(QString, url);
QFETCH(bool, expectedResult);
QFETCH(QString, expectedFqdn);
QFETCH(QString, expectedPath);
QString fqdn;
QString path;
bool result = KCookieJar::parseUrl(url, fqdn, path);
QCOMPARE(result, expectedResult);
QCOMPARE(fqdn, expectedFqdn);
QCOMPARE(path, expectedPath);
}
void testExtractDomains_data()
{
QTest::addColumn<QString>("fqdn");
QTest::addColumn<QStringList>("expectedDomains");
QTest::newRow("empty") << "" << (QStringList() << "localhost");
QTest::newRow("ipv4") << "1.2.3.4" << (QStringList() << "1.2.3.4");
QTest::newRow("ipv6") << "[fe80::213:d3ff:fef4:8c92]" << (QStringList() << "[fe80::213:d3ff:fef4:8c92]");
QTest::newRow("bugs.kde.org") << "bugs.kde.org" << (QStringList() << "bugs.kde.org" << ".bugs.kde.org" << "kde.org" << ".kde.org");
}
void testExtractDomains()
{
QFETCH(QString, fqdn);
QFETCH(QStringList, expectedDomains);
KCookieJar jar;
QStringList lst;
jar.extractDomains(fqdn, lst);
QCOMPARE(lst, expectedDomains);
}
};
QTEST_KDEMAIN(KCookieJarTest, NoGUI)
#include "kcookiejartest.moc"

View file

@ -1,597 +0,0 @@
/* This file is part of the KDE libraries
Copyright (C) 2008 Andreas Hartmetz <ahartmetz@gmail.com>
Copyright (C) 2010,2011 Rolf Eike Beer <kde@opensource.sf-tec.de>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include <ctype.h>
#include <QDir>
#include <QMap>
#include <QTextCodec>
#include <QUrl>
#include <kdebug.h>
// Advance *pos beyond spaces / tabs
static void skipSpace(const char input[], int *pos, int end)
{
int idx = *pos;
while (idx < end && (input[idx] == ' ' || input[idx] == '\t')) {
idx++;
}
*pos = idx;
return;
}
// Advance *pos to start of next line while being forgiving about line endings.
// Return false if the end of the header has been reached, true otherwise.
static bool nextLine(const char input[], int *pos, int end)
{
int idx = *pos;
while (idx < end && input[idx] != '\r' && input[idx] != '\n') {
idx++;
}
int rCount = 0;
int nCount = 0;
while (idx < end && qMax(rCount, nCount) < 2 && (input[idx] == '\r' || input[idx] == '\n')) {
input[idx] == '\r' ? rCount++ : nCount++;
idx++;
}
if (idx < end && qMax(rCount, nCount) == 2 && qMin(rCount, nCount) == 1) {
// if just one of the others is missing eat it too.
// this ensures that conforming headers using the proper
// \r\n sequence (and also \n\r) will be parsed correctly.
if ((rCount == 1 && input[idx] == '\r') || (nCount == 1 && input[idx] == '\n')) {
idx++;
}
}
*pos = idx;
return idx < end && rCount < 2 && nCount < 2;
}
// QByteArray::fromPercentEncoding() does not notify us about encoding errors so we need
// to check here if this is valid at all.
static bool isValidPercentEncoding(const QByteArray &data)
{
int i = 0;
const int last = data.length() - 1;
const char *d = data.constData();
while ( (i = data.indexOf('%', i)) != -1) {
if ( i >= last - 2 )
return false;
if ( ! isxdigit(d[i + 1]) )
return false;
if ( ! isxdigit(d[i + 2]) )
return false;
i++;
}
return true;
}
QByteArray TokenIterator::next()
{
QPair<int, int> token = m_tokens[m_currentToken++];
//fromRawData brings some speed advantage but also the requirement to keep the text buffer
//around. this together with implicit sharing (you don't know where copies end up)
//is dangerous!
//return QByteArray::fromRawData(&m_buffer[token.first], token.second - token.first);
return QByteArray(&m_buffer[token.first], token.second - token.first);
}
QByteArray TokenIterator::current() const
{
QPair<int, int> token = m_tokens[m_currentToken - 1];
//return QByteArray::fromRawData(&m_buffer[token.first], token.second - token.first);
return QByteArray(&m_buffer[token.first], token.second - token.first);
}
QList<QByteArray> TokenIterator::all() const
{
QList<QByteArray> ret;
for (int i = 0; i < m_tokens.count(); i++) {
QPair<int, int> token = m_tokens[i];
ret.append(QByteArray(&m_buffer[token.first], token.second - token.first));
}
return ret;
}
HeaderTokenizer::HeaderTokenizer(char *buffer)
: m_buffer(buffer)
{
// add information about available headers and whether they have one or multiple,
// comma-separated values.
//The following response header fields are from RFC 2616 unless otherwise specified.
//Hint: search the web for e.g. 'http "accept-ranges header"' to find information about
//a header field.
static const HeaderFieldTemplate headerFieldTemplates[] = {
{"accept-ranges", false},
{"age", false},
{"cache-control", true},
{"connection", true},
{"content-disposition", false}, //is multi-valued in a way, but with ";" separator!
{"content-encoding", true},
{"content-language", true},
{"content-length", false},
{"content-location", false},
{"content-md5", false},
{"content-type", false},
{"date", false},
{"dav", true}, //RFC 2518
{"etag", false},
{"expires", false},
{"keep-alive", true}, //RFC 2068
{"last-modified", false},
{"link", false}, //RFC 2068, multi-valued with ";" separator
{"location", false},
{"p3p", true}, // http://www.w3.org/TR/P3P/
{"pragma", true},
{"proxy-authenticate", false}, //complicated multi-valuedness: quoted commas don't separate
//multiple values. we handle this at a higher level.
{"proxy-connection", true}, //inofficial but well-known; to avoid misunderstandings
//when using "connection" when talking to a proxy.
{"refresh", false}, //not sure, only found some mailing list posts mentioning it
{"set-cookie", false}, //RFC 2109; the multi-valuedness seems to be usually achieved
//by sending several instances of this field as opposed to
//usually comma-separated lists with maybe multiple instances.
{"transfer-encoding", true},
{"upgrade", true},
{"warning", true},
{"www-authenticate", false} //see proxy-authenticate
};
for (uint i = 0; i < sizeof(headerFieldTemplates) / sizeof(HeaderFieldTemplate); i++) {
const HeaderFieldTemplate &ft = headerFieldTemplates[i];
insert(QByteArray(ft.name), HeaderField(ft.isMultiValued));
}
}
int HeaderTokenizer::tokenize(int begin, int end)
{
char *buf = m_buffer; //keep line length in check :/
int idx = begin;
int startIdx = begin; //multi-purpose start of current token
bool multiValuedEndedWithComma = false; //did the last multi-valued line end with a comma?
QByteArray headerKey;
do {
if (buf[idx] == ' ' || buf [idx] == '\t') {
// line continuation; preserve startIdx except (see below)
if (headerKey.isEmpty()) {
continue;
}
// turn CR/LF into spaces for later parsing convenience
int backIdx = idx - 1;
while (backIdx >= begin && (buf[backIdx] == '\r' || buf[backIdx] == '\n')) {
buf[backIdx--] = ' ';
}
// multiple values, comma-separated: add new value or continue previous?
if (operator[](headerKey).isMultiValued) {
if (multiValuedEndedWithComma) {
// start new value; this is almost like no line continuation
skipSpace(buf, &idx, end);
startIdx = idx;
} else {
// continue previous value; this is tricky. unit tests to the rescue!
if (operator[](headerKey).beginEnd.last().first == startIdx) {
// remove entry, it will be re-added because already idx != startIdx
operator[](headerKey).beginEnd.removeLast();
} else {
// no comma, no entry: the prev line was whitespace only - start new value
skipSpace(buf, &idx, end);
startIdx = idx;
}
}
}
} else {
// new field
startIdx = idx;
// also make sure that there is at least one char after the colon
while (idx < (end - 1) && buf[idx] != ':' && buf[idx] != '\r' && buf[idx] != '\n') {
buf[idx] = tolower(buf[idx]);
idx++;
}
if (buf[idx] != ':') {
//malformed line: no colon
headerKey.clear();
continue;
}
headerKey = QByteArray(&buf[startIdx], idx - startIdx);
if (!contains(headerKey)) {
//we don't recognize this header line
headerKey.clear();
continue;
}
// skip colon & leading whitespace
idx++;
skipSpace(buf, &idx, end);
startIdx = idx;
}
// we have the name/key of the field, now parse the value
if (!operator[](headerKey).isMultiValued) {
// scan to end of line
while (idx < end && buf[idx] != '\r' && buf[idx] != '\n') {
idx++;
}
if (!operator[](headerKey).beginEnd.isEmpty()) {
// there already is an entry; are we just in a line continuation?
if (operator[](headerKey).beginEnd.last().first == startIdx) {
// line continuation: delete previous entry and later insert a new, longer one.
operator[](headerKey).beginEnd.removeLast();
}
}
operator[](headerKey).beginEnd.append(QPair<int, int>(startIdx, idx));
} else {
// comma-separated list
while (true) {
//skip one value
while (idx < end && buf[idx] != '\r' && buf[idx] != '\n' && buf[idx] != ',') {
idx++;
}
if (idx != startIdx) {
operator[](headerKey).beginEnd.append(QPair<int, int>(startIdx, idx));
}
multiValuedEndedWithComma = buf[idx] == ',';
//skip comma(s) and leading whitespace, if any respectively
while (idx < end && buf[idx] == ',') {
idx++;
}
skipSpace(buf, &idx, end);
//next value or end-of-line / end of header?
if (buf[idx] >= end || buf[idx] == '\r' || buf[idx] == '\n') {
break;
}
//next value
startIdx = idx;
}
}
} while (nextLine(buf, &idx, end));
return idx;
}
TokenIterator HeaderTokenizer::iterator(const char *key) const
{
QByteArray keyBa = QByteArray::fromRawData(key, strlen(key));
if (contains(keyBa)) {
return TokenIterator(value(keyBa).beginEnd, m_buffer);
} else {
return TokenIterator(m_nullTokens, m_buffer);
}
}
static void skipLWS(const QString &str, int &pos)
{
while (pos < str.length() && (str[pos] == QLatin1Char(' ') || str[pos] == QLatin1Char('\t'))) {
++pos;
}
}
// keep the common ending, this allows the compiler to join them
static const char typeSpecials[] = "{}*'%()<>@,;:\\\"/[]?=";
static const char attrSpecials[] = "'%()<>@,;:\\\"/[]?=";
static const char valueSpecials[] = "()<>@,;:\\\"/[]?=";
static bool specialChar(const QChar &ch, const char *specials)
{
// WORKAROUND: According to RFC 2616, any character other than ascii
// characters should NOT be allowed in unquoted content-disposition file
// names. However, since none of the major browsers follow this rule, we do
// the same thing here and allow all printable unicode characters. See
// https://bugs.kde.org/show_bug.cgi?id=261223 for the detials.
if (!ch.isPrint()) {
return true;
}
for (int i = qstrlen(specials) - 1; i >= 0; i--) {
if (ch == QLatin1Char(specials[i])) {
return true;
}
}
return false;
}
/**
* read and parse the input until the given terminator
* @param str input string to parse
* @param term terminator
* @param pos position marker in the input string
* @param specials characters forbidden in this section
* @return the next section or an empty string if it was invalid
*
* Extracts token-like input until terminator char or EOL.
* Also skips over the terminator.
*
* pos is correctly incremented even if this functions returns
* an empty string so this can be used to skip over invalid
* parts and continue.
*/
static QString extractUntil(const QString &str, QChar term, int &pos, const char *specials)
{
QString out;
skipLWS(str, pos);
bool valid = true;
while (pos < str.length() && (str[pos] != term)) {
out += str[pos];
valid = (valid && !specialChar(str[pos], specials));
++pos;
}
if (pos < str.length()) { // Stopped due to finding term
++pos;
}
if (!valid) {
return QString();
}
// Remove trailing linear whitespace...
while (out.endsWith(QLatin1Char(' ')) || out.endsWith(QLatin1Char('\t'))) {
out.chop(1);
}
if (out.contains(QLatin1Char(' '))) {
out.clear();
}
return out;
}
// As above, but also handles quotes..
// pos is set to -1 on parse error
static QString extractMaybeQuotedUntil(const QString &str, int &pos)
{
const QChar term = QLatin1Char(';');
skipLWS(str, pos);
// Are we quoted?
if (pos < str.length() && str[pos] == QLatin1Char('"')) {
QString out;
// Skip the quote...
++pos;
// when quoted we also need an end-quote
bool endquote = false;
// Parse until trailing quote...
while (pos < str.length()) {
if (str[pos] == QLatin1Char('\\') && pos + 1 < str.length()) {
// quoted-pair = "\" CHAR
out += str[pos + 1];
pos += 2; // Skip both...
} else if (str[pos] == QLatin1Char('"')) {
++pos;
endquote = true;
break;
} else if (!str[pos].isPrint()) { // Don't allow CTL's RFC 2616 sec 2.2
break;
} else {
out += str[pos];
++pos;
}
}
if (!endquote) {
pos = -1;
return QString();
}
// Skip until term..
while (pos < str.length() && (str[pos] != term)) {
if ((str[pos] != QLatin1Char(' ')) && (str[pos] != QLatin1Char('\t'))) {
pos = -1;
return QString();
}
++pos;
}
if (pos < str.length()) { // Stopped due to finding term
++pos;
}
return out;
} else {
return extractUntil(str, term, pos, valueSpecials);
}
}
static QMap<QString, QString> contentDispositionParserInternal(const QString &disposition)
{
kDebug(7113) << "disposition: " << disposition;
int pos = 0;
const QString strDisposition = extractUntil(disposition, QLatin1Char(';'), pos, typeSpecials).toLower();
QMap<QString, QString> parameters;
QMap<QString, QString> contparams; // all parameters that contain continuations
QMap<QString, QString> encparams; // all parameters that have character encoding
// the type is invalid, the complete header is junk
if (strDisposition.isEmpty()) {
return parameters;
}
parameters.insert(QLatin1String("type"), strDisposition);
while (pos < disposition.length()) {
QString key = extractUntil(disposition, QLatin1Char('='), pos, attrSpecials).toLower();
if (key.isEmpty()) {
// parse error in this key: do not parse more, but add up
// everything we already got
kDebug(7113) << "parse error in key, abort parsing";
break;
}
QString val;
if (key.endsWith(QLatin1Char('*'))) {
val = extractUntil(disposition, QLatin1Char(';'), pos, valueSpecials);
} else {
val = extractMaybeQuotedUntil(disposition, pos);
}
if (val.isEmpty()) {
if (pos == -1) {
kDebug(7113) << "parse error in value, abort parsing";
break;
}
continue;
}
const int spos = key.indexOf(QLatin1Char('*'));
if (spos == key.length() - 1) {
key.chop(1);
encparams.insert(key, val);
} else if (spos >= 0) {
contparams.insert(key, val);
} else if (parameters.contains(key)) {
kDebug(7113) << "duplicate key" << key << "found, ignoring everything more";
parameters.remove(key);
return parameters;
} else {
parameters.insert(key, val);
}
}
QMap<QString, QString>::iterator i = contparams.begin();
while (i != contparams.end()) {
QString key = i.key();
int spos = key.indexOf(QLatin1Char('*'));
bool hasencoding = false;
if (key.at(spos + 1) != QLatin1Char('0')) {
++i;
continue;
}
// no leading zeros allowed, so delete the junk
int klen = key.length();
if (klen > spos + 2) {
// nothing but continuations and encodings may insert * into parameter name
if ((klen > spos + 3) || ((klen == spos + 3) && (key.at(spos + 2) != QLatin1Char('*')))) {
kDebug(7113) << "removing invalid key " << key << "with val" << i.value() << key.at(spos + 2);
i = contparams.erase(i);
continue;
}
hasencoding = true;
}
int seqnum = 1;
QMap<QString, QString>::iterator partsi;
// we do not need to care about encoding specifications: only the first
// part is allowed to have one
QString val = i.value();
key.chop(hasencoding ? 2 : 1);
while ((partsi = contparams.find(key + QString::number(seqnum))) != contparams.end()) {
val += partsi.value();
contparams.erase(partsi);
}
i = contparams.erase(i);
key.chop(1);
if (hasencoding) {
encparams.insert(key, val);
} else {
if (parameters.contains(key)) {
kDebug(7113) << "duplicate key" << key << "found, ignoring everything more";
parameters.remove(key);
return parameters;
}
parameters.insert(key, val);
}
}
for (QMap<QString, QString>::iterator i = encparams.begin(); i != encparams.end(); ++i) {
QString val = i.value();
// RfC 2231 encoded character set in filename
int spos = val.indexOf(QLatin1Char('\''));
if (spos == -1) {
continue;
}
int npos = val.indexOf(QLatin1Char('\''), spos + 1);
if (npos == -1) {
continue;
}
const QString charset = val.left(spos);
const QString lang = val.mid(spos + 1, npos - spos - 1);
const QByteArray encodedVal = val.mid(npos + 1).toLatin1();
if ( ! isValidPercentEncoding(encodedVal) )
continue;
const QByteArray rawval = QByteArray::fromPercentEncoding(encodedVal);
if (charset.isEmpty() || (charset == QLatin1String("us-ascii"))) {
bool valid = true;
for (int j = rawval.length() - 1; (j >= 0) && valid; j--) {
valid = (rawval.at(j) >= 32);
}
if (!valid)
continue;
val = QString::fromLatin1(rawval.constData());
} else {
QTextCodec *codec = QTextCodec::codecForName(charset.toLatin1());
if (!codec)
continue;
val = codec->toUnicode(rawval);
}
parameters.insert(i.key(), val);
}
return parameters;
}
static QMap<QString, QString> contentDispositionParser(const QString &disposition)
{
QMap<QString, QString> parameters = contentDispositionParserInternal(disposition);
const QLatin1String fn("filename");
if (parameters.contains(fn)) {
// Content-Disposition is not allowed to dictate directory
// path, thus we extract the filename only.
const QString val = QDir::toNativeSeparators(parameters[fn]);
int slpos = val.lastIndexOf(QDir::separator());
if (slpos > -1) {
parameters.insert(fn, val.mid(slpos + 1));
}
}
return parameters;
}

View file

@ -1,86 +0,0 @@
/* This file is part of the KDE libraries
Copyright (C) 2008 Andreas Hartmetz <ahartmetz@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef PARSINGHELPERS_H
#define PARSINGHELPERS_H
#include <QtCore/QList>
#include <QtCore/QPair>
#include <QtCore/QMap>
struct HeaderField {
HeaderField(bool multiValued)
{ isMultiValued = multiValued; }
// QHash requires a default constructor
HeaderField()
{ isMultiValued = false; }
bool isMultiValued;
QList<QPair<int, int> > beginEnd;
};
class HeaderTokenizer;
class TokenIterator
{
public:
inline bool hasNext() const
{
return m_currentToken < m_tokens.count();
}
QByteArray next();
QByteArray current() const;
QList<QByteArray> all() const;
private:
friend class HeaderTokenizer;
QList<QPair<int, int> > m_tokens;
int m_currentToken;
const char *m_buffer;
TokenIterator(const QList<QPair<int, int> > &tokens, const char *buffer)
: m_tokens(tokens),
m_currentToken(0),
m_buffer(buffer) {}
};
class HeaderTokenizer : public QHash<QByteArray, HeaderField>
{
public:
HeaderTokenizer(char *buffer);
// note that buffer is not const - in the parsed area CR/LF will be overwritten
// with spaces if there is a line continuation.
/// @return: index of first char after header or end
int tokenize(int begin, int end);
// after tokenize() has been called use the QHash part of this class to
// ask for a list of begin-end indexes in buffer for header values.
TokenIterator iterator(const char *key) const;
private:
char *m_buffer;
struct HeaderFieldTemplate {
const char *name;
bool isMultiValued;
};
QList<QPair<int, int> > m_nullTokens; //long-lived, allows us to pass out references.
};
#endif //PARSINGHELPERS_H

View file

@ -1,605 +0,0 @@
Audio and Apache HTTPD
ApacheCon 2001
Santa Clara, US
April 6th, 2001
Sander van Zoest <sander@vanZoest.com>
Covalent Technologies, Inc.
<http://www.covalent.net/>
Latest version can be found at:
<http://www.vanZoest.com/sander/apachecon/2001/>
Introduction:
About this paper:
Contents:
1. Why serve Audio on the Net?
This is almost like asking, why are you reading this? it might be
because of the excitement caused by the new media that has recently
crazed upon the internet. People are looking to bring their lifes onto
the net, one of the things that brings that closer to a reality is the
ability to hear live broadcasts of the worlds news, favorite sport;
hear music and to teleconference with others. Sometimes it is simply
to enhance the mood to a web site or to provide audio feedback of
actions performed by the visitor of the web site.
2. What makes delivering audio so different?
The biggest reason to what makes audio different than traditional
web media such as graphics, text and HTML is the fact that timing
is very important. This caused by the significant increase in size
of the media and the different quality levels that exist.
There really are two kinds of goals behind audio streams.
In one case there is a need for immediate response the moment
playback is requested and this can sacrifice quality. While
in the other case quality and a non-interrupted stream are much
more important.
This sort of timing is not really required of any other media,
with the exception of video. In the case of HTML and images the
files sizes are usually a lot smaller which causes the objects
to load much quicker and usually are not very useful without
having the entire file. In audio the middle of a stream can have
useful information and still set a particular mood.
3. Different ways of delivery Audio on the Net.
Embedding audio in your Web Page
This used to be a lot more common in the past. Just like embedding
an image in a web page, it is possible to add a sound clip or score
to the web page.
The linked in audio files are usually short and of low quality to
avoid a long delay for downloading the rest of the web page and the
audio format needs to be supported by the browser natively or with
a browser plug-in to avoid annoying the visitor.
This can be accomplished using the HTML 4.0 [HTML4] object element which
works similar to how to specify an applet with the object element.
In the past this could also be accomplished using the embed and bgsound
browser specific additions to HTML.
example:
<object type="audio/x-midi" data="../media/sound.mid" width="200" height="26">
<param name="src" value="../media/sound.mid">
<param name="autostart" value="true">
<param name="controls" value="ControlPanel">
</object>
Each param element is specific to each browser. Please check with each
browser for specific information in regards to what param elements are
available.
In this method of delivering audio the audio file is served up via the
web server. When using an Apache HTTPD server make sure that the appropriate
mime type is configured for the audio file and that the audio file is
named and referenced by the appropriate extension.
Although the current HTML 4.01 [HTML4] says to use the object element
many browsers out on the market today still look for the embed element.
Below find a little snipbit that will work work in many browsers.
<object type="audio/x-midi" data="../media/sound.mid" width="200" height="26">
<param name="src" value="../media/sound.mid">
<param name="autostart" value="true">
<param name="controls" value="ControlPanel">
<embed type="audio/x-midi" src="../media/sound.mid"
width="200" height="26" autoplay="true" controls="ControlPanel">
<noembed>Your browser does not support embedded WAV files.</noembed>
</object>
With the increasing installation base of the Flash browser plug-in by
Macromedia most developers that are looking to provide this kind of
functionality to a web page are creating flash elements that have their
own way of adding audio that is discussed in Flash specific documents.
Downloading via HTTP
Using this method the visitor to the website will have to download the
entire audio file and save it to the hard drive before it can be
listened to. (1) This is very popular with people that want to listen
to high quality streams of audio and have a below ISDN connection to
the internet. In some cases where the demand for a stream is high or
the internet is congested downloading the content even for high bandwidth
users can be affective and useful.
One of the advantages of downloading audio to the local computer hard
drive is that it can be played back (once downloaded) any time as long
as the audio file is accessable from the computer.
There are a lot of sites on the internet that provide this functionality
for music and other audio files. It is also one of the easiest ways to
delivery high quality audio to visitors.
(1) Microsoft Windows Media Player in conjunction with the Microsoft
Internet Explorer Browser will automatically start playing the
audio stream after a sufficient amount of the file has been
downloaded. This can be accomplished because of the tight
integration of the Browser and Media Player. With most audio players
you can listen to a file being downloaded, but you will have to
envoke the action manually.
. On-Demand streaming via HTTP
The real difference between downloading and on-demand streaming is
that in on-demand streaming the audio starts playing before the entire
audio file has been downloaded. This is accomplished by a hand of off
the browser to the audio player via an intermediate file format that
has been configured by the browser to be handled by the audio player.
Look in a further section entitled "Linking to Audio via Apache HTTPD"
below for more information about the different intermediate file formats.
This type of streaming is very popular among the open source crowd and
is the most widely implemented using the MP3 file format. Apache,
Shoutcast [SHOUTCAST] and Icecast [ICECAST] are the most common
software components used to provide on-demand streaming via HTTP. Both
Icecast and Shoutcast are not fully HTTP compliant, but Icecast is
becoming closer. For more information about the Shoutcast and Icecast
differences see the section below.
Sites like Live365.com and MP3.com are huge sites that rely on this
method of delivery of audio.
. On-Demand Streaming via RTSP/RTP
RTSP/RTP is a new set of streaming protocols that is getting more
backing and becoming more popular by the second. The specification
was developed by the Internet Engineering Task Force Working Groups
AVT [IETFAVT] and MMUSIC [IETFMMUSIC]. RTP the Realtime Transfer
Protocol has been around longer than RTSP and originally came out
of the work towards a better teleconferencing, mbone, type system.
RTSP is the Real-Time Streaming Protocol that is used as a control
protocol and acts similarily to HTTP except that it maintains state
and is bi-directional.
Currently the latest Real Networks Streaming Servers support RTSP
and RTP and Real Networks own proprietary transfer protocol RDT.
Apple's Darwin Streaming server is also RTSP/RTP compliant.
The RTSP/RTP protocol suite is very powerful and flexable in regards
to your streaming needs. It has the ability to support "server-push"
style stream redirects and has the ability to throttle streams to
ensure the stream can sustain the limited bandwidth over the network.
For On-Demand streams the RTP protocol would usually stream over
TCP and have a second TCP connection open for RTSP. Because of the
rich features provided by the protocol suite, it is not very well
suited to allow people to download the stream and therefore the
download via HTTP method might still be preferred by some.
. Live Broadcast Streaming via RTSP/RTP
In the case of a live broadcast streaming RTSP/RTP shines. RTP allowing
for UDP datagrams to be transmitted to clients allows for fast immediate
delivery of content with the sacrifice of reliability. The RTP stream
can be send over IP Multicast to minimize bandwidth on the network.
Many Content Delivery Networks (CDNs) are starting to provide support for
RTSP/RTP proxies that should provide a better quality streaming environment
on the internet.
Much work is also being done in the RTP space to provide transfers over
telecommunication networks such as cellular phones. Although not directly
related, per se, it does provide a positive feeling knowing that all the
audio related transfer groups seem to be working towards a common standard
such as RTP.
. On-Demand or Live Broadcast streaming via MMS.
This is the Microsoft Windows Media Technologies Streaming protocol. It
is only supported by Microsoft Windows Media Player and currently only
works on Microsoft Windows.
5. Configuring Mime Types
One of the most hardest things in serving audio has been the wide variety
of audio codecs and mime types available. The battle of mime types on the
audio player side of things isn't over, but it seems to be a little more
controlled.
On the server side of things provide the appropriate mime type for the
particular audio streams and/or files that are being served to the audio
players. Although some clients and operating systems handle files fully
based on the file extension. The mime type [RFC2045] is more specific
and more defined.
The registered mime types are maintained by IANA [IANA]. On their site
they have a list of all the registered mime types and their name space.
If you are planning on using a mime type that isn't registered by IANA
then signal this in the name space by adding a "x-" before the subtype.
Because this was not done very often in the audio space, there was a
lot of confusion to what the real mime type should be.
For example the MPEG 1.0 Layer 3 Audio (MP3) [ORAMP3BOOK] mime type
was not specified for the longest time. Because of this the mime type
was audio/x-mpeg. Although none of the audio players understood
audio/x-mpeg, but understood audio/mpeg it was not a technically
correct mime type. Later audio players recognized this and started
using the audio/x-mpeg mime type. Which in the end caused a lot
of hassles with clients needing to be configured differently depending
on the website and client that was used. Last november we thanked
Martin Nilsson of the ID3 tagging project for registering audo/mpeg
with IANA. [RFC3003]
Correct configuration of Mime Types is very important. Apache HTTPD
ships with a fairly up to date copy of the mime.types file, so most
of the default ones (including audio/mpeg) are there.
But in case you run into some that are not defined use the mod_mime
directives such as AddType to fix this.
Examples:
AddType audio/x-mpegurl .m3u
AddType audio/x-scpls .pls
AddType application/x-ogg .ogg
6. Common Audio File Formats
There are many audio formats and metadata formats that exist. Many of
them do not have registered mime types and are hardly documented.
This section is an attempt at providing the most accurate mime type
information for each format with a rough description of what the files
are used for.
. Real Audio
Real Networks Proprietary audio format and meta formats. This is one
of the more common streaming audio formats today. It comes in several
sub flavors such as Real 5.0, Real G2 and Real 8.0 etc. The file size
varies depending on the bitrates and what combination of bitrates are
contained within the single file.
The following mime types are used
audio/x-pn-realaudio .ra, .ram, .rm
audio/x-pn-realaudio-plugin .rpm
application/x-pn-realmedia
. MPEG 1.0 Layer 3 Audio (MP3)
This is currently one of the most popular downloaded audio formats
that was originally developed by the Motion Pictures Experts Group
and has patents by the Fraunhofer IIS Institute and Thompson
Multimedia. [ORAMP3BOOK] The file is a lossy compression that at
a bitrate of 128kbps reduces the file size to roughly a MB/minute.
The mime type is audio/mpeg with the extension of .mp3 [RFC3003]
. Windows Media Audio
Originally known as MS Audio was developed by Microsoft as the MP3
killer. Still relatively a new format but heavily marketed by
Microsoft and becoming more popular by the minute. It is a successor
to the Microsoft Audio Streaming Format (ASF).
. WAV
Windows Audio Format is a pretty semi-complicated encapsulating
format that in the most common case is PCM with a WAV header up front.
It has the mime type audio/x-wav with the extension .wav.
. Vorbis
Ogg Vorbis [VORBIS] is still a relatively new format brought to
life by CD Paranoia author Christopher Montgomery; known to the
world as Monty. It is an open source audio format free of patents
and gotchas. It is a codec/file format that is roughly as good as
the MP3 format, if not much better. The mime type for Ogg Vorbis is
application/x-ogg with the extension of .ogg.
. MIDI
The MIDI standard and file format [MIDISPEC] have been used by
Musicians for a long time. It is a great format to add music to
a website without the long download times and needing special players
or plug-ins. The mime type is audio/x-midi and the extension is .mid
. Shockwave Flash (ADPCM/MP3) [FLASH4AUDIO]
Macromedia Flash [FLASH4AUDIO] uses its own internal audio format
that is often used on Flash websites. It is based on Adaptive
Differential Pulse Code Modulation (ADPCM) and the MP3 file format.
Because it is usually used from within Flash it usually isn't served
up seperatedly but it's extension is .swf
There are many many many more audio codecs and file formats that exist.
I have listed a few that won't be discussed but should be kept in mind.
Formats such as PCM/Raw Audio (audio/basic), MOD, MIDI (audio/x-midi),
QDesign (used by Quicktime), Beatnik, Sun's AU, Apple/SGI's AIFF, AAC
by the MPEG Group, Liquid Audio and AT&T's a2b (AAC derivatives),
Dolby AC-3, Yamaha's TwinVQ (originally by Nippon Telephone and Telegraph)
and MPEG-4 audio.
7. Linking to Audio via Apache HTTPD
There are many different ways to link to audio from the Apache HTTPD
web server. It seems as if every codec has their own metafile format.
The metafile format is provided to allow the browser to hand off the
job of requesting the audio file to the audio player, because it is
more familiar with the file format and how to handle streaming or how
to actually connect to the audio server then the web browser is.
This section will discuss the more common methods to provide streaming
links to provide that gateway from the web to the audio world.
Probably the one that is the most recognized file is the RAM file.
. RAM
Real Audio Metafile. It is a pretty straight forward way that Real
Networks allowed their Real Player to take more control over their
proprietary audio streams. The file format is simply a URL on each
line that will be streamed in order by the client. The mime type
is the same as other RealAudio files audio/x-pn-realaudio where
the pn stands for Progressive Networks the old name of the company.
. M3U
This next one is the MPEG Layer 3 URL Metafile that has been around
for a very long time as a playlist format for MP3 players. It supported
URLs pretty early on by some players and got the mime type
audio/x-mpegurl and is now used by Icecast and many destination sites
such as MP3.com. The format is exactly the same as that of the RAM
file, just a list of urls that are separated by line feeds.
. PLS
This is the playlist files used by Nullsoft's Winamp MP3 Player. Later
on it got more widely used by Nullsoft's Shoutcast and has the mime
type of audio/x-scpls with the extension .pls. Before shoutcast the
mimetype was simply audio/x-pls. As you can see in the example below
it looks very much like a standard windows INI file format.
Example:
[playlist]
numberofentries=2
File1=<uri>
Title1=<title>
Length1=<length or -1>
File2=<uri>
Title2=<title>
Length2=<length or -1>
. SDP
This is the Session Description Protocol [RFC2327] which is heavily
used within RTSP and is a standard way of describing how to subscribe
to a particular RTP stream. The mime type is application/sdp with the
extension .sdp .
Sometimes you might see RTSL (Real-Time Streaming Language) floating
around. This was an old Real Networks format that has been succeeded
by SDP. It's mimetype was application/x-rtsl with the extension of .rtsl
. ASX
Is a Windows Media Metafile format [MSASX] that is based on early XML
standards. It can be found with many extensions such as .wvx, .wax
and .asx. I am not aware of a mime type for this format.
. SMIL
Is the Synchronized Multimedia Integration Language [SMIL20] that
is now a W3C Recommendation [W3SYMM]. It was originally developed
by Real Networks to provide an HTML-like language to their Real Player
that was more focused on multimedia. The mime type is application/smil
with the extensions of either .smil or .smi
. MHEG
Is a hypertext language developed by the ISO group. [MHEG1] [MHEG5]
and [MHEG5COR]. It has been adopted by the Digital Audio Visual
Council [DAVIC]. It is more used for teleconferencing, broadcasting
and television, but close enough related that it receives a mention
here. The mime type is application/x-mheg with the extension of
.mheg
8. Configuring Apache HTTPD specificly to serve large Audio Files
Some of the most common things that you will need to adjust to be
able to serve many large audio files via the Apache HTTPD Server.
Because of the difference in size between HTML files and Audio files,
the MaxClients will need to be adjusted appropriatedly depending on
the amount of time listeners end up tieing up a process. If you are
serving high quality MP3 files at 128kbps for example you should
expect more than 5 minute download times for most people.
This will significantly impact your webserver since this means that
that process is occupied for the entire time. Because of this you
will also want to in crease the TimeOut Directive to a higher
number. This is to ensure that connections do not get disconnected
half way through a transfer and having that person hit "reload"
and connect again.
Because of the amount of time the downloads tie up the processes
of the server, the smallest footprint of the server in memory would
be recommended because that would mean you could run more processes
on the machine.
After that normal performance tweaks such as max file descriptor
changes and longer tcp listen queues apply.
9. Icecast/Shoutcast Protocol.
Both protocols are very tightly based on HTTP/1.0. The main difference
is a group of new headers such as the icy headers by Shoutcast and the
new x-audiocast headers provided by Icecast.
A typical shoutcast request from the client.
GET / HTTP/1.0
ICY 200 OK
icy-notice1:<BR>This stream requires <a href="http://www.winamp.com/">
Winamp</a><BR>
icy-notice2:SHOUTcast Distributed Network Audio Server/posix v1.0b<BR>
icy-name: Great Songs
icy-genre: Jazz
icy-url: http://shout.serv.dom/
icy-pub: 1
icy-br: 24
<data><songtitle><data>
The icy headers display the song title and other formation including if
this stream is public and what the bitrate is.
A typical icecast request from the client.
GET / HTTP/1.0
Host: icecast.serv.dom
x-audiocast-udpport: 6000
Icy-MetaData: 0
Accept: */*
HTTP/1.0 200 OK
Server: Icecast/VERSION
Content-Type: audio/mpeg
x-audiocast-name: Great Songs
x-audiocast-genre: Jazz
x-audiocast-url: http://icecast.serv.dom/
x-audiocast-streamid:
x-audiocast-public: 0
x-audiocast-bitrate: 24
x-audiocast-description: served by Icecast
<data>
NOTE: I am mixing the headers of the controlling client with those form
a listening client. This might be better explained at a latter
date.
The CPAN Perl Package Apache::MP3 by Lincoln Stein implements a little of
each which works because MP3 players tend to support both.
One of the big differences in implementations between the listening clients
is that Icecast uses an out of band UDP channel to update metadata
while the Shoutcast server gets it meta data from the client embedded within
the MP3 stream. The general meta data for the stream is set up via the
icy and x-audiocast HTTP headers.
Although the MP3 standard documents were written for interrupted communication
it is not very specific on that. So although it doesn't state that there is
anything wrong with embedding garbage between MPEG frames the players that
do not understand it might make a noisy bleep and chirps because of it.
References and Further Reading:
[DAVIC]
Digital Audio Visual Council
<http://www.davic.org/>
[FLASH4AUDIO]
L. J. Lotus, "Flash 4: Audio Options", ZD, Inc. 2000.
<http://www.zdnet.com/devhead/stories/articles/0,4413,2580376,00.html>
[HTML4]
D. Ragget, A. Le Hors, I. Jacobs, "HTML 4.01 Specification", W3C
Recommendation, December, 1999.
<http://www.w3.org/TR/html401/>
[IANA]
Internet Assigned Numbers Authority.
<http:/www.iana.org/>
[ICECAST]
Icecast Open Source Streaming Audio System.
<http://www.icecast.org/>
[IETFAVT]
Audio/Video Transport WG, Internet Engineering Task Force.
<http://www.ietf.org/html.charters/avt-charter.html>
[IETFMMUSIC]
Multiparty Multimedia Session Control WG, Internet Engineering Task
Force. <http://www.ietf.org/html.charters/mmusic-charter.html>
[IETFSIP]
Session Initiation Protocol WG, Internet Engineering Task Force.
<http://www.ietf.org/html.charters/sip-charter.html>
[IPMULTICAST]
Transmit information to a group of recipients via a single transmission
by the source, in contrast to unicast.
IP Multicast Initiative
<http://www.ipmulticast.com/>
[MIDISPEC]
The International MIDI Association,"MIDI File Format Spec 1.1",
<http://www.vanZoest.com/sander/apachecon/2001/midispec.html>
[MHEG1]
ISO/IEC, "Information Technology - Coding of Multimedia and Hypermedia
Information - Part 1: MHEG Object Representation, Base Notation (ASN.1)";
Draft International Standard ISO 13522-1;1997;
<http://www.ansi.org/>
<http://www.iso.ch/cate/d22153.html>
[MHEG5]
ISO/IEC, "Information Technology - Coding of Multimedia and Hypermedia
Information - Part 5: Support for Base-Level Interactive Applications";
Draft International Standard ISO 13522-5:1997;
<http://www.ansi.org/>
<http://www.iso.ch/cate/d26876.html>
[MHEG5COR]
Information Technology - Coding of Multimedia and Hypermedia Information
- Part 5: Support for base-level interactive applications -
- Technical Corrigendum 1; ISO/IEC 13552-5:1997/Cor.1:1999(E)
<http://www.ansi.org/>
<http://www.iso.ch/cate/d31582.html>
[MSASX]
Microsoft Corp. "All About Windows Media Metafiles". October 2000.
<http://msdn.microsoft.com/workshop/imedia/windowsmedia/
crcontent/asx.asp>
[ORAMP3]
S. Hacker; MP3: The Definitive Guide; O'Reilly and Associates, Inc.
March, 2000.
<http://www.oreilly.com/catalog/mp3/>
[RFC2045]
N. Freed and N. Borenstein, "Multipurpose Internet Mail
Extensions (MIME) Part One: Format of Internet Message Bodies",
RFC 2045, November 1996. <http://www.ietf.org/rfc/2045.txt>
[RFC2327]
M. Handley and V. Jacobson, "SDP: Session Description Protocol",
RFC 2327, April 1998. <http://www.ietf.org/rfc/rfc2327.txt>
[RFC3003]
M. Nilsson, "The audio/mpeg Media Type", RFC 3003, November 2000.
<http://www.ietf.org/rfc/rfc3003.txt>
[SHOUTCAST]
Nullsoft Shoutcast MP3 Streaming Technology.
<http://www.shoutcast.com/>
[SMIL20]
L. Rutledge, J. van Ossenbruggen, L. Hardman, D. Bulterman,
"Anticipating SMIL 2.0: The Developing Cooperative Infrastructure
for Multimedia on the Web"; 8th International WWW Conference,
Proc. May, 1999.
<http://www8.org/w8-papers/3c-hypermedia-video/anticipating/
anticipating.html>
[W39CIR]
V. Krishnan and S. G. Chang, "Customized Internet Radio"; 9th
International WWW Conference Proc. May 2000.
<http://www9.org/w9cdrom/353/353.html>
[VORBIS]
Ogg Vorbis - Open Source Audio Codec
<http://www.xiph.org/ogg/vorbis/>
[W3SYMM]
W3C Synchronized Multimedia Activity (SYMM Working Group);
<http://www.w3.org/AudioVideo/>

View file

@ -1,50 +0,0 @@
include_directories(
${KDE4_KIO_INCLUDES}
${CMAKE_CURRENT_SOURCE_DIR}/..
${CMAKE_CURRENT_BINARY_DIR}/..
${CMAKE_SOURCE_DIR}/kio/httpfilter
)
kde4_add_test(kiohttp-httpheadertokenizetest httpheadertokenizetest.cpp)
target_link_libraries(kiohttp-httpheadertokenizetest
${KDE4_KDECORE_LIBS}
${QT_QTTEST_LIBRARY}
${QT_QTGUI_LIBRARY}
)
kde4_add_test(kiohttp-httpheaderdispositiontest httpheaderdispositiontest.cpp)
target_link_libraries(kiohttp-httpheaderdispositiontest
${KDE4_KDECORE_LIBS}
${QT_QTTEST_LIBRARY}
${QT_QTGUI_LIBRARY}
)
kde4_add_test(kiohttp-httpauthenticationtest httpauthenticationtest.cpp)
target_link_libraries(kiohttp-httpauthenticationtest
${KDE4_KDECORE_LIBS}
kntlm
${QT_QTTEST_LIBRARY}
${QT_QTNETWORK_LIBRARY}
${QT_QTGUI_LIBRARY}
)
if(GSSAPI_FOUND)
target_link_libraries(kiohttp-httpauthenticationtest ${GSSAPI_LIBS})
endif()
kde4_add_test(kiohttp-httpobjecttest httpobjecttest.cpp
${kioslave-http_SOURCE_DIR}/http.cpp # requires solid
${kioslave-http_SOURCE_DIR}/httpauthentication.cpp
${httpfilter_STAT_SRCS}
${CMAKE_SOURCE_DIR}/kio/httpfilter/httpfilter.cc
)
target_link_libraries(kiohttp-httpobjecttest
${KDE4_KDECORE_LIBS}
${QT_QTTEST_LIBRARY}
${QT_QTGUI_LIBRARY}
${KDE4_KIO_LIBS}
${KDE4_SOLID_LIBS}
kntlm
)
if(GSSAPI_FOUND)
target_link_libraries(kiohttp-httpobjecttest ${GSSAPI_LIBS})
endif()

View file

@ -1,227 +0,0 @@
/* This file is part of the KDE libraries
Copyright (C) 2011 Dawit Alemayehu <adawit@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "httpauthenticationtest.h"
#include <qtest_kde.h>
#include <kdebug.h>
#include <krandom.h>
#include <kconfiggroup.h>
#include <misc/kntlm/kntlm.h>
#include <QtCore/QList>
#include <QtCore/QByteArray>
#include <QtNetwork/QHostInfo>
#define ENABLE_HTTP_AUTH_NONCE_SETTER
#include "httpauthentication.cpp"
QTEST_KDEMAIN(HTTPAuthenticationTest, NoGUI)
static void parseAuthHeader(const QByteArray& header,
QByteArray* bestOffer,
QByteArray* scheme,
QList<QByteArray>* result)
{
const QList<QByteArray> authHeaders = KAbstractHttpAuthentication::splitOffers(QList<QByteArray>() << header);
QByteArray chosenHeader = KAbstractHttpAuthentication::bestOffer(authHeaders);
if (bestOffer) {
*bestOffer = chosenHeader;
}
if (!scheme && !result) {
return;
}
QByteArray authScheme;
const QList<QByteArray> parseResult = parseChallenge(chosenHeader, &authScheme);
if (scheme) {
*scheme = authScheme;
}
if (result) {
*result = parseResult;
}
}
void HTTPAuthenticationTest::testHeaderParsing_data()
{
QTest::addColumn<QByteArray>("header");
QTest::addColumn<QByteArray>("resultScheme");
QTest::addColumn<QByteArray>("resultValues");
// Tests cases from http://greenbytes.de/tech/tc/httpauth/
QTest::newRow("greenbytes-simplebasic") << QByteArray("Basic realm=\"foo\"") << QByteArray("Basic") << QByteArray("realm,foo");
QTest::newRow("greenbytes-simplebasictok") << QByteArray("Basic realm=foo") << QByteArray("Basic") << QByteArray("realm,foo");
QTest::newRow("greenbytes-simplebasiccomma") << QByteArray("Basic , realm=\"foo\"") << QByteArray("Basic") << QByteArray("realm,foo");
// there must be a space after the scheme
QTest::newRow("greenbytes-simplebasiccomma2") << QByteArray("Basic, realm=\"foo\"") << QByteArray() << QByteArray();
// we accept scheme without any parameters to maintain compatibility with too simple minded servers out there
QTest::newRow("greenbytes-simplebasicnorealm") << QByteArray("Basic") << QByteArray("Basic") << QByteArray();
QTest::newRow("greenbytes-simplebasicwsrealm") << QByteArray("Basic realm = \"foo\"") << QByteArray("Basic") << QByteArray("realm,foo");
QTest::newRow("greenbytes-simplebasicrealmsqc") << QByteArray("Basic realm=\"\\f\\o\\o\"") << QByteArray("Basic") << QByteArray("realm,foo");
QTest::newRow("greenbytes-simplebasicrealmsqc2") << QByteArray("Basic realm=\"\\\"foo\\\"\"") << QByteArray("Basic") << QByteArray("realm,\"foo\"");
QTest::newRow("greenbytes-simplebasicnewparam1") << QByteArray("Basic realm=\"foo\", bar=\"xyz\"") << QByteArray("Basic") << QByteArray("realm,foo,bar,xyz");
QTest::newRow("greenbytes-simplebasicnewparam2") << QByteArray("Basic bar=\"xyz\", realm=\"foo\"") << QByteArray("Basic") << QByteArray("bar,xyz,realm,foo");
// a Basic challenge following an empty one
QTest::newRow("greenbytes-multibasicempty") << QByteArray(",Basic realm=\"foo\"") << QByteArray("Basic") << QByteArray("realm,foo");
QTest::newRow("greenbytes-multibasicunknown") << QByteArray("Basic realm=\"basic\", Newauth realm=\"newauth\"") << QByteArray("Basic") << QByteArray("realm,basic");
QTest::newRow("greenbytes-multibasicunknown2") << QByteArray("Newauth realm=\"newauth\", Basic realm=\"basic\"") << QByteArray("Basic") << QByteArray("realm,basic");
QTest::newRow("greenbytes-unknown") << QByteArray("Newauth realm=\"newauth\"") << QByteArray() << QByteArray();
// Misc. test cases
QTest::newRow("ntlm") << QByteArray("NTLM ") << QByteArray("NTLM") << QByteArray();
QTest::newRow("unterminated-quoted-value") << QByteArray("Basic realm=\"") << QByteArray("Basic") << QByteArray();
QTest::newRow("spacing-and-tabs") << QByteArray("bAsic bar\t =\t\"baz\", realm =\t\"foo\"") << QByteArray("bAsic") << QByteArray("bar,baz,realm,foo");
QTest::newRow("empty-fields") << QByteArray("Basic realm=foo , , , ,, bar=\"baz\"\t,") << QByteArray("Basic") << QByteArray("realm,foo,bar,baz");
QTest::newRow("spacing") << QByteArray("Basic realm=foo, bar = baz") << QByteArray("Basic") << QByteArray("realm,foo,bar,baz");
QTest::newRow("missing-comma-between-fields") << QByteArray("Basic realm=foo bar = baz") << QByteArray("Basic") << QByteArray("realm,foo");
// quotes around text, every character needlessly quoted
QTest::newRow("quote-excess") << QByteArray("Basic realm=\"\\\"\\f\\o\\o\\\"\"") << QByteArray("Basic") << QByteArray("realm,\"foo\"");
// quotes around text, quoted backslashes
QTest::newRow("quoted-backslash") << QByteArray("Basic realm=\"\\\"foo\\\\\\\\\"") << QByteArray("Basic") << QByteArray("realm,\"foo\\\\");
// quotes around text, quoted backslashes, quote hidden behind them
QTest::newRow("quoted-backslash-and-quote") << QByteArray("Basic realm=\"\\\"foo\\\\\\\"\"") << QByteArray("Basic") << QByteArray("realm,\"foo\\\"");
// invalid quoted text
QTest::newRow("invalid-quoted") << QByteArray("Basic realm=\"\\\"foo\\\\\\\"") << QByteArray("Basic") << QByteArray();
// ends in backslash without quoted value
QTest::newRow("invalid-quote") << QByteArray("Basic realm=\"\\\"foo\\\\\\") << QByteArray("Basic") << QByteArray();
}
QByteArray joinQByteArray(const QList<QByteArray>& list)
{
QByteArray data;
const int count = list.count();
for (int i = 0; i < count; ++i) {
if (i > 0)
data += ',';
data += list.at(i);
}
return data;
}
void HTTPAuthenticationTest::testHeaderParsing()
{
QFETCH(QByteArray, header);
QFETCH(QByteArray, resultScheme);
QFETCH(QByteArray, resultValues);
QByteArray chosenHeader, chosenScheme;
QList<QByteArray> parsingResult, expectedResult;
parseAuthHeader(header, &chosenHeader, &chosenScheme, &parsingResult);
QCOMPARE(chosenScheme, resultScheme);
QCOMPARE(joinQByteArray(parsingResult), resultValues);
}
void HTTPAuthenticationTest::testAuthenticationSelection_data()
{
QTest::addColumn<QByteArray>("input");
QTest::addColumn<QByteArray>("expectedScheme");
QTest::addColumn<QByteArray>("expectedOffer");
#ifdef HAVE_LIBGSSAPI
QTest::newRow("all-with-negotiate") << QByteArray("Negotiate , Digest , NTLM , Basic") << QByteArray("Negotiate") << QByteArray("Negotiate");
#endif
QTest::newRow("all-without-negotiate") << QByteArray("Digest , NTLM , Basic , NewAuth") << QByteArray("Digest") << QByteArray("Digest");
QTest::newRow("ntlm-basic-unknown") << QByteArray("NTLM , Basic , NewAuth") << QByteArray("NTLM") << QByteArray("NTLM");
QTest::newRow("basic-unknown") << QByteArray("Basic , NewAuth") << QByteArray("Basic") << QByteArray("Basic");
QTest::newRow("ntlm-basic+param-ntlm") << QByteArray("NTLM , Basic realm=foo, bar = baz, NTLM") << QByteArray("NTLM") << QByteArray("NTLM");
QTest::newRow("ntlm-with-type{2|3}") << QByteArray("NTLM VFlQRV8yX09SXzNfTUVTU0FHRQo=") << QByteArray("NTLM") << QByteArray("NTLM VFlQRV8yX09SXzNfTUVTU0FHRQo=");
// Unknown schemes always return blank, i.e. auth request should be ignored
QTest::newRow("unknown-param") << QByteArray("Newauth realm=\"newauth\"") << QByteArray() << QByteArray();
QTest::newRow("unknown-unknown") << QByteArray("NewAuth , NewAuth2") << QByteArray() << QByteArray();
}
void HTTPAuthenticationTest::testAuthenticationSelection()
{
QFETCH(QByteArray, input);
QFETCH(QByteArray, expectedScheme);
QFETCH(QByteArray, expectedOffer);
QByteArray scheme, offer;
parseAuthHeader(input, &offer, &scheme, 0);
QCOMPARE(scheme, expectedScheme);
QCOMPARE(offer, expectedOffer);
}
void HTTPAuthenticationTest::testAuthentication_data()
{
QTest::addColumn<QByteArray>("input");
QTest::addColumn<QByteArray>("expectedResponse");
QTest::addColumn<QByteArray>("user");
QTest::addColumn<QByteArray>("pass");
QTest::addColumn<QByteArray>("url");
QTest::addColumn<QByteArray>("cnonce");
// Test cases from RFC 2617...
QTest::newRow("rfc-2617-basic-example")
<< QByteArray("Basic realm=\"WallyWorld\"")
<< QByteArray("Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==")
<< QByteArray("Aladdin")
<< QByteArray("open sesame")
<< QByteArray()
<< QByteArray();
QTest::newRow("rfc-2617-digest-example")
<< QByteArray("Digest realm=\"testrealm@host.com\", qop=\"auth,auth-int\", nonce=\"dcd98b7102dd2f0e8b11d0f600bfb0c093\", opaque=\"5ccc069c403ebaf9f0171e9517f40e41\"")
<< QByteArray("Digest username=\"Mufasa\", realm=\"testrealm@host.com\", nonce=\"dcd98b7102dd2f0e8b11d0f600bfb0c093\", uri=\"/dir/index.html\", algorithm=MD5, qop=auth, cnonce=\"0a4f113b\", nc=00000001, response=\"6629fae49393a05397450978507c4ef1\", opaque=\"5ccc069c403ebaf9f0171e9517f40e41\"")
<< QByteArray("Mufasa")
<< QByteArray("Circle Of Life")
<< QByteArray("http://www.nowhere.org/dir/index.html")
<< QByteArray("0a4f113b");
QTest::newRow("ntlm-negotiate-type1")
<< QByteArray("NTLM")
<< QByteArray("NTLM TlRMTVNTUAABAAAABQIAAAAAAAAAAAAAAAAAAAAAAAA=")
<< QByteArray()
<< QByteArray()
<< QByteArray()
<< QByteArray();
QTest::newRow("ntlm-challenge-type2")
<< QByteArray("NTLM TlRMTVNTUAACAAAAAAAAACgAAAABggAAU3J2Tm9uY2UAAAAAAAAAAA==")
<< QByteArray("NTLM TlRMTVNTUAADAAAAGAAYAFgAAAAYABgAQAAAAAAAAAAAAAAAAAAAAHAAAAAWABYAcAAAAAAAAAAAAAAAAYIAAJSg10BK9h+dU9d6Ijn04m4iDZHzFECXU3sG2ZrxJPWBGnO3BnTKK13Ku1qYqpcE6VcATwBSAEsAUwBUAEEAVABJAE8ATgA=")
<< QByteArray("Ursa-Minor\\Zaphod")
<< QByteArray("Beeblebrox")
<< QByteArray()
<< QByteArray();
}
void HTTPAuthenticationTest::testAuthentication()
{
QFETCH(QByteArray, input);
QFETCH(QByteArray, expectedResponse);
QFETCH(QByteArray, user);
QFETCH(QByteArray, pass);
QFETCH(QByteArray, url);
QFETCH(QByteArray, cnonce);
QByteArray bestOffer;
parseAuthHeader(input, &bestOffer, 0, 0);
KAbstractHttpAuthentication* authObj = KAbstractHttpAuthentication::newAuth(bestOffer);
QVERIFY(authObj);
if (!cnonce.isEmpty())
authObj->setDigestNonceValue(cnonce);
authObj->setChallenge(bestOffer, KUrl(url), "GET");
authObj->generateResponse(QString(user), QString(pass));
QCOMPARE(authObj->headerFragment().trimmed().constData(), expectedResponse.constData());
delete authObj;
}

View file

@ -1,32 +0,0 @@
/* This file is part of the KDE libraries
Copyright (c) 2011 Dawit Alemayehu <adawit@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include <QtCore/QObject>
class HTTPAuthenticationTest : public QObject
{
Q_OBJECT
private Q_SLOTS:
void testHeaderParsing();
void testHeaderParsing_data();
void testAuthenticationSelection();
void testAuthenticationSelection_data();
void testAuthentication();
void testAuthentication_data();
};

View file

@ -1,363 +0,0 @@
/* This file is part of the KDE libraries
Copyright (C) 2010,2011 Rolf Eike Beer <kde@opensource.sf-tec.de>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "httpheaderdispositiontest.h"
#include <qtest_kde.h>
#include <QtCore/QByteArray>
#include <kdebug.h>
#include <parsinghelpers.h>
#include "moc_httpheaderdispositiontest.cpp"
#include <parsinghelpers.cpp>
QTEST_KDEMAIN(HeaderDispositionTest, NoGUI)
static void runTest(const QString &header, const QByteArray &result)
{
QMap<QString, QString> parameters = contentDispositionParser(header);
QList<QByteArray> results = result.split('\n');
if (result.isEmpty())
results.clear();
foreach (const QByteArray &ba, results) {
QList<QByteArray> values = ba.split('\t');
const QString key(values.takeFirst());
QVERIFY(parameters.contains(key));
const QByteArray val = values.takeFirst();
QVERIFY(values.isEmpty());
QCOMPARE(parameters[key], QString::fromUtf8(val.constData(), val.length()));
}
QCOMPARE(parameters.count(), results.count());
}
void HeaderDispositionTest::runAllTests_data()
{
QTest::addColumn<QString>("header");
QTest::addColumn<QByteArray>("result");
// http://greenbytes.de/tech/tc2231/
QTest::newRow("greenbytes-inlonly") << "inline" <<
QByteArray("type\tinline");
QTest::newRow("greenbytes-inlonlyquoted") << "\"inline\"" <<
QByteArray();
QTest::newRow("greenbytes-inlwithasciifilename") << "inline; filename=\"foo.html\"" <<
QByteArray("type\tinline\n"
"filename\tfoo.html");
QTest::newRow("greenbytes-inlwithfnattach") << "inline; filename=\"Not an attachment!\"" <<
QByteArray("type\tinline\n"
"filename\tNot an attachment!");
QTest::newRow("greenbytes-inlwithasciifilenamepdf") << "inline; filename=\"foo.pdf\"" <<
QByteArray("type\tinline\n"
"filename\tfoo.pdf");
QTest::newRow("greenbytes-attonly") << "attachment" <<
QByteArray("type\tattachment");
QTest::newRow("greenbytes-attonlyquoted") << "\"attachment\"" <<
QByteArray();
QTest::newRow("greenbytes-attonlyucase") << "ATTACHMENT" <<
QByteArray("type\tattachment");
QTest::newRow("greenbytes-attwithasciifilename") << "attachment; filename=\"foo.html\"" <<
QByteArray("type\tattachment\n"
"filename\tfoo.html");
QTest::newRow("greenbytes-attwithasciifnescapedchar") << "attachment; filename=\"f\\oo.html\"" <<
QByteArray("type\tattachment\n"
"filename\tfoo.html");
QTest::newRow("greenbytes-attwithasciifnescapedquote") << "attachment; filename=\"\\\"quoting\\\" tested.html\"" <<
QByteArray("type\tattachment\n"
"filename\t\"quoting\" tested.html");
QTest::newRow("greenbytes-attwithquotedsemicolon") << "attachment; filename=\"Here's a semicolon;.html\"" <<
QByteArray("type\tattachment\n"
"filename\tHere's a semicolon;.html");
QTest::newRow("greenbytes-attwithfilenameandextparam") << "attachment; foo=\"bar\"; filename=\"foo.html\"" <<
QByteArray("type\tattachment\n"
"foo\tbar\n"
"filename\tfoo.html");
QTest::newRow("greenbytes-attwithfilenameandextparamescaped") << "attachment; foo=\"\\\"\\\\\";filename=\"foo.html\"" <<
QByteArray("type\tattachment\n"
"foo\t\"\\\n"
"filename\tfoo.html");
QTest::newRow("greenbytes-attwithasciifilenameucase") << "attachment; FILENAME=\"foo.html\"" <<
QByteArray("type\tattachment\n"
"filename\tfoo.html");
// specification bug in RfC 2616, legal through RfC 2183 and 6266
QTest::newRow("greenbytes-attwithasciifilenamenq") << "attachment; filename=foo.html" <<
QByteArray("type\tattachment\n"
"filename\tfoo.html");
QTest::newRow("greenbytes-attwithasciifilenamenqws") << "attachment; filename=foo bar.html" <<
QByteArray("type\tattachment");
QTest::newRow("greenbytes-attwithfntokensq") << "attachment; filename='foo.bar'" <<
QByteArray("type\tattachment\n"
"filename\t'foo.bar'");
QTest::newRow("greenbytes-attwithisofnplain") << QString::fromLatin1("attachment; filename=\"foo-\xe4.html\"") <<
QByteArray("type\tattachment\n"
"filename\tfoo-ä.html");
QTest::newRow("greenbytes-attwithisofnplain") << "attachment; filename=\"foo-ä.html\"" <<
QByteArray("type\tattachment\n"
"filename\tfoo-ä.html");
QTest::newRow("greenbytes-attwithfnrawpctenca") << "attachment; filename=\"foo-%41.html\"" <<
QByteArray("type\tattachment\n"
"filename\tfoo-%41.html");
QTest::newRow("greenbytes-attwithfnusingpct") << "attachment; filename=\"50%.html\"" <<
QByteArray("type\tattachment\n"
"filename\t50%.html");
QTest::newRow("greenbytes-attwithfnrawpctencaq") << "attachment; filename=\"foo-%\\41.html\"" <<
QByteArray("type\tattachment\n"
"filename\tfoo-%41.html");
QTest::newRow("greenbytes-attwithnamepct") << "attachment; name=\"foo-%41.html\"" <<
QByteArray("type\tattachment\n"
"name\tfoo-%41.html");
QTest::newRow("greenbytes-attwithfilenamepctandiso") << "attachment; filename=\"\xe4-%41.html\"" <<
QByteArray("type\tattachment\n"
"filename\tä-%41.html");
QTest::newRow("greenbytes-attwithfnrawpctenclong") << "attachment; filename=\"foo-%c3%a4-%e2%82%ac.html\"" <<
QByteArray("type\tattachment\n"
"filename\tfoo-%c3%a4-%e2%82%ac.html");
QTest::newRow("greenbytes-attwithasciifilenamews1") << "attachment; filename =\"foo.html\"" <<
QByteArray("type\tattachment\n"
"filename\tfoo.html");
QTest::newRow("greenbytes-attwith2filenames") << "attachment; filename=\"foo.html\"; filename=\"bar.html\"" <<
QByteArray("type\tattachment");
QTest::newRow("greenbytes-attfnbrokentoken") << "attachment; filename=foo[1](2).html" <<
QByteArray("type\tattachment");
QTest::newRow("greenbytes-attmissingdisposition") << "filename=foo.html" <<
QByteArray();
QTest::newRow("greenbytes-attmissingdisposition2") << "x=y; filename=foo.html" <<
QByteArray();
QTest::newRow("greenbytes-attmissingdisposition3") << "\"foo; filename=bar;baz\"; filename=qux" <<
QByteArray();
QTest::newRow("greenbytes-attmissingdisposition4") << "filename=foo.html, filename=bar.html" <<
QByteArray();
QTest::newRow("greenbytes-emptydisposition") << "; filename=foo.html" <<
QByteArray();
QTest::newRow("greenbytes-attbrokenquotedfn") << "attachment; filename=\"foo.html\".txt" <<
QByteArray("type\tattachment");
QTest::newRow("greenbytes-attbrokenquotedfn2") << "attachment; filename=\"bar" <<
QByteArray("type\tattachment");
QTest::newRow("greenbytes-attbrokenquotedfn3") << "attachment; filename=foo\"bar;baz\"qux" <<
QByteArray("type\tattachment");
QTest::newRow("greenbytes-attreversed") << "filename=foo.html; attachment" <<
QByteArray();
QTest::newRow("greenbytes-attconfusedparam") << "attachment; xfilename=foo.html" <<
QByteArray("type\tattachment\n"
"xfilename\tfoo.html");
QTest::newRow("greenbytes-attabspath") << "attachment; filename=\"/foo.html\"" <<
QByteArray("type\tattachment\n"
"filename\tfoo.html");
QTest::newRow("greenbytes-attabspath") << "attachment; filename=\"\\\\foo.html\"" <<
QByteArray("type\tattachment\n"
"filename\t\\foo.html");
QTest::newRow("greenbytes-") << "attachment; creation-date=\"Wed, 12 Feb 1997 16:29:51 -0500\"" <<
QByteArray("type\tattachment\n"
"creation-date\tWed, 12 Feb 1997 16:29:51 -0500");
QTest::newRow("greenbytes-") << "attachment; modification-date=\"Wed, 12 Feb 1997 16:29:51 -0500\"" <<
QByteArray("type\tattachment\n"
"modification-date\tWed, 12 Feb 1997 16:29:51 -0500");
QTest::newRow("greenbytes-dispext") << "foobar" <<
QByteArray("type\tfoobar");
QTest::newRow("greenbytes-dispextbadfn") << "attachment; example=\"filename=example.txt\"" <<
QByteArray("type\tattachment\n"
"example\tfilename=example.txt");
QTest::newRow("greenbytes-attwithisofn2231iso") << "attachment; filename*=iso-8859-1''foo-%E4.html" <<
QByteArray("type\tattachment\n"
"filename\tfoo-ä.html");
QTest::newRow("greenbytes-attwithfn2231utf8") << "attachment; filename*=UTF-8''foo-%c3%a4-%e2%82%ac.html" <<
QByteArray("type\tattachment\n"
"filename\tfoo-ä-€.html");
QTest::newRow("greenbytes-attwithfn2231noc") << "attachment; filename*=''foo-%c3%a4-%e2%82%ac.html" <<
QByteArray("type\tattachment");
// it's not filename, but "filename "
QTest::newRow("greenbytes-attwithfn2231ws1") << "attachment; filename *=UTF-8''foo-%c3%a4.html" <<
QByteArray("type\tattachment");
QTest::newRow("greenbytes-attwithfn2231ws2") << "attachment; filename*= UTF-8''foo-%c3%a4.html" <<
QByteArray("type\tattachment\n"
"filename\tfoo-ä.html");
QTest::newRow("greenbytes-attwithfn2231ws3") << "attachment; filename* =UTF-8''foo-%c3%a4.html" <<
QByteArray("type\tattachment\n"
"filename\tfoo-ä.html");
// argument must not be enclosed in double quotes
QTest::newRow("greenbytes-attwithfn2231quot") << "attachment; filename*=\"UTF-8''foo-%c3%a4.html\"" <<
QByteArray("type\tattachment");
QTest::newRow("greenbytes-attwithfn2231dpct") << "attachment; filename*=UTF-8''A-%2541.html" <<
QByteArray("type\tattachment\n"
"filename\tA-%41.html");
QTest::newRow("greenbytes-attwithfn2231abspathdisguised") << "attachment; filename*=UTF-8''%5cfoo.html" <<
QByteArray("type\tattachment\n"
"filename\t\\foo.html");
QTest::newRow("greenbytes-attfncont") << "attachment; filename*0=\"foo.\"; filename*1=\"html\"" <<
QByteArray("type\tattachment\n"
"filename\tfoo.html");
QTest::newRow("greenbytes-attfncontenc") << "attachment; filename*0*=UTF-8''foo-%c3%a4; filename*1=\".html\"" <<
QByteArray("type\tattachment\n"
"filename\tfoo-ä.html");
// no leading zeros
QTest::newRow("greenbytes-attfncontlz") << "attachment; filename*0=\"foo\"; filename*01=\"bar\"" <<
QByteArray("type\tattachment\n"
"filename\tfoo");
QTest::newRow("greenbytes-attfncontnc") << "attachment; filename*0=\"foo\"; filename*2=\"bar\"" <<
QByteArray("type\tattachment\n"
"filename\tfoo");
// first element must have number 0
QTest::newRow("greenbytes-attfnconts1") << "attachment; filename*1=\"foo.\"; filename*2=\"html\"" <<
QByteArray("type\tattachment");
// we must not rely on element ordering
QTest::newRow("greenbytes-attfncontord") << "attachment; filename*1=\"bar\"; filename*0=\"foo\"" <<
QByteArray("type\tattachment\n"
"filename\tfoobar");
// specifying both param and param* is allowed, param* should be taken
QTest::newRow("greenbytes-attfnboth") << "attachment; filename=\"foo-ae.html\"; filename*=UTF-8''foo-%c3%a4.html" <<
QByteArray("type\tattachment\n"
"filename\tfoo-ä.html");
// specifying both param and param* is allowed, param* should be taken
QTest::newRow("greenbytes-attfnboth2") << "attachment; filename*=UTF-8''foo-%c3%a4.html; filename=\"foo-ae.html\"" <<
QByteArray("type\tattachment\n"
"filename\tfoo-ä.html");
QTest::newRow("greenbytes-attnewandfn") << "attachment; foobar=x; filename=\"foo.html\"" <<
QByteArray("type\tattachment\n"
"filename\tfoo.html\n"
"foobar\tx");
// invalid argument, should be ignored
QTest::newRow("greenbytes-attrfc2047token") << "attachment; filename==?ISO-8859-1?Q?foo-=E4.html?=" <<
QByteArray("type\tattachment");
QTest::newRow("space_before_value") << "attachment; filename= \"foo.html\"" <<
QByteArray("type\tattachment\n"
"filename\tfoo.html");
// no character set given but 8 bit characters
QTest::newRow("8bit_in_ascii") << "attachment; filename*=''foo-%c3%a4.html" <<
QByteArray("type\tattachment");
// there may not be gaps in numbering
QTest::newRow("continuation013") << "attachment; filename*0=\"foo.\"; filename*1=\"html\"; filename*3=\"bar\"" <<
QByteArray("type\tattachment\n"
"filename\tfoo.html");
// "wrong" element ordering and encoding
QTest::newRow("reversed_continuation+encoding") << "attachment; filename*1=\"html\"; filename*0*=us-ascii''foo." <<
QByteArray("type\tattachment\n"
"filename\tfoo.html");
// unknown charset
QTest::newRow("unknown_charset") << "attachment; filename*=unknown''foo" <<
QByteArray("type\tattachment");
// no apostrophs
QTest::newRow("encoding-no-apostrophs") << "attachment; filename*=foo" <<
QByteArray("type\tattachment");
// only one apostroph
QTest::newRow("encoding-one-apostroph") << "attachment; filename*=us-ascii'foo" <<
QByteArray("type\tattachment");
// duplicate filename, both should be ignored and parsing should stop
QTest::newRow("duplicate-filename") << "attachment; filename=foo; filename=bar; foo=bar" <<
QByteArray("type\tattachment");
// garbage after closing quote, parsing should stop there
QTest::newRow("garbage_after_closing_quote") << "attachment; filename*=''foo; bar=\"f\"oo; baz=foo" <<
QByteArray("type\tattachment\n"
"filename\tfoo");
// trailing whitespace should be ignored
QTest::newRow("whitespace_after_value") << "attachment; filename=\"foo\" ; bar=baz" <<
QByteArray("type\tattachment\n"
"filename\tfoo\n"
"bar\tbaz");
// invalid syntax for type
QTest::newRow("invalid_type1") << "filename=foo.html" <<
QByteArray();
// invalid syntax for type
QTest::newRow("invalid_type2") << "inline{; filename=\"foo\"" <<
QByteArray();
QTest::newRow("invalid_type3") << "foo bar; filename=\"foo\"" <<
QByteArray();
QTest::newRow("invalid_type4") << "foo\tbar; filename=\"foo\"" <<
QByteArray();
// missing closing quote, so parameter is broken
QTest::newRow("no_closing_quote") << "attachment; filename=\"bar" <<
QByteArray("type\tattachment");
// we ignore any path given in the header and use only the filename
QTest::newRow("full_path_given") << "attachment; filename=\"/etc/shadow\"" <<
QByteArray("type\tattachment\n"
"filename\tshadow");
// we ignore any path given in the header and use only the filename even if there is an error later
QTest::newRow("full_path_and_parse_error") << "attachment; filename=\"/etc/shadow\"; foo=\"baz\"; foo=\"bar\"" <<
QByteArray("type\tattachment\n"
"filename\tshadow");
// control characters are forbidden in the quoted string
QTest::newRow("control_character_in_value") << "attachment; filename=\"foo\003\"" <<
QByteArray("type\tattachment");
// duplicate keys must be ignored
QTest::newRow("duplicate_with_continuation") << "attachment; filename=\"bar\"; filename*0=\"foo.\"; filename*1=\"html\"" <<
QByteArray("type\tattachment");
// percent encoding, invalid first character
QTest::newRow("percent-first-char-invalid") << "attachment; filename*=UTF-8''foo-%o5.html" <<
QByteArray("type\tattachment");
// percent encoding, invalid second character
QTest::newRow("percent-second-char-invalid") << "attachment; filename*=UTF-8''foo-%5o.html" <<
QByteArray("type\tattachment");
// percent encoding, both characters invalid
QTest::newRow("greenbytes-attwithfn2231nbadpct2") << "attachment; filename*=UTF-8''foo-%oo.html" <<
QByteArray("type\tattachment");
// percent encoding, invalid second character
QTest::newRow("percent-second-char-missing") << "attachment; filename*=UTF-8''foo-%f.html" <<
QByteArray("type\tattachment");
// percent encoding, too short value
QTest::newRow("percent-short-encoding-at-end") << "attachment; filename*=UTF-8''foo-%f" <<
QByteArray("type\tattachment");
}
#if 0
// currently unclear if our behaviour is only accidentially correct
// invalid syntax
{ "inline; attachment; filename=foo.html",
"type\tinline" },
// invalid syntax
{ "attachment; inline; filename=foo.html",
"type\tattachment" },
// deactivated for now: failing due to missing implementation
{"attachment; filename=\"foo-&#xc3;&#xa4;.html\"",
"type\tattachment\n"
"filename\tfoo-ä.html" },
// deactivated for now: not the same utf, no idea if the expected value is actually correct
{ "attachment; filename*=UTF-8''foo-a%cc%88.html",
"type\tattachment\n"
"filename\tfoo-ä.html" }
// deactivated for now: only working to missing implementation
// string is not valid iso-8859-1 so filename should be ignored
//"attachment; filename*=iso-8859-1''foo-%c3%a4-%e2%82%ac.html",
//"type\tattachment",
// deactivated for now: only working to missing implementation
// should not be decoded
//"attachment; filename=\"=?ISO-8859-1?Q?foo-=E4.html?=\"",
//"type\tattachment\n"
//"filename\t=?ISO-8859-1?Q?foo-=E4.html?=",
};
#endif
void HeaderDispositionTest::runAllTests()
{
QFETCH(QString, header);
QFETCH(QByteArray, result);
runTest(header, result);
}

View file

@ -1,33 +0,0 @@
/* This file is part of the KDE libraries
Copyright (c) 2010 Rolf Eike Beer <kde@opensource.sf-tec.de>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef HTTPHEADERDISPOSITIONTEST_H
#define HTTPHEADERDISPOSITIONTEST_H
#include <QtCore/QObject>
class HeaderDispositionTest : public QObject
{
Q_OBJECT
private Q_SLOTS:
void runAllTests();
void runAllTests_data();
};
#endif //HTTPHEADERDISPOSITIONTEST_H

View file

@ -1,198 +0,0 @@
/* This file is part of the KDE libraries
Copyright (c) 2008 Andreas Hartmetz <ahartmetz@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "httpheadertokenizetest.h"
#include <qtest_kde.h>
#include <QtCore/QByteArray>
#include <QtCore/QHash>
#include <kdebug.h>
#include <parsinghelpers.h>
#include "moc_httpheadertokenizetest.cpp"
#include <parsinghelpers.cpp>
QTEST_KDEMAIN(HeaderTokenizeTest, NoGUI)
/* some possible fields that can be used for test headers
{"accept-ranges", false},
{"cache-control", true},
{"connection", true},
{"content-disposition", false}, //is multi-valued in a way, but with ";" separator!
{"content-encoding", true},
{"content-language", true},
{"content-length", false},
{"content-location", false},
{"content-md5", false},
{"content-type", false},
{"date", false},
{"dav", true}, //RFC 2518
{"etag", false},
{"expires", false},
{"keep-alive", false}, //RFC 2068
{"last-modified", false},
{"link", false}, //RFC 2068, multi-valued with ";" separator
{"location", false},
*/
//no use testing many different headers, just a couple each of the multi-valued
//and the single-valued group to make sure that corner cases work both if there
//are already entries for the header and if there are no entries.
static const char messyHeader[] =
"\n"
"accept-ranges:foo\r\n"
"connection: one\r\n"
" t_\r\n"
" wo,\r\n"
"\tthree\r\n"
"accept-ranges:42\n"
"accept-Ranges:\tmaybe \r"
" or not\n"
"CoNNectIoN:four, , ,, , \r\n"
" :fi:ve\r\n"
":invalid stuff\r\n"
"\tinvalid: connection:close\t\r"
"connection: Six, seven ,, , eight\r" //one malformed newline...
"\n\r "; //two malformed newlines; end of header. also observe the trailing space.
//tab separates values, newline separates header lines. the first word is the key.
static const char messyResult[] =
"accept-ranges\tfoo\t42\tmaybe or not\n"
"connection\tone t_ wo\tthree\tfour\t:fi:ve\tSix\tseven\teight";
static const char redirectHeader[] =
//"HTTP/1.1 302 Moved Temporarily\r\n"
"Location: http://www.hertz.de/rentacar/index.jsp?bsc=t&targetPage=reservationOnHomepage.jsp\r\n"
"Connection:close\r\n"
"Cache-Control: no-cache\r\n"
"Pragma: no-cache\r\n"
"\r\n";
static const char redirectResult[] =
"cache-control\tno-cache\n"
"connection\tclose\n"
"location\thttp://www.hertz.de/rentacar/index.jsp?bsc=t&targetPage=reservationOnHomepage.jsp\n"
"pragma\tno-cache";
static const int bufSize = 4096;
char buffer[bufSize];
void HeaderTokenizeTest::testMessyHeader()
{
//Copy the header into a writable buffer
for (int i = 0; i < bufSize; i++) {
buffer[i] = 0;
}
strcpy(buffer, messyHeader);
HeaderTokenizer tokenizer(buffer);
int tokenizeEnd = tokenizer.tokenize(0, strlen(messyHeader));
QCOMPARE(tokenizeEnd, (int)(strlen(messyHeader) - 1));
// If the output of the tokenizer contains all the terms that should be there and
// exactly the number of terms that should be there then it's exactly correct.
// We are lax wrt trailing whitespace, by the way. It does neither explicitly matter
// nor not matter according to the standard. Internal whitespace similarly should not
// matter but we have to be exact because the tokenizer does not move strings around,
// it only overwrites \r and \n in case of line continuations.
typedef QPair<int, int> intPair; //foreach is a macro and does not like commas
int nValues = 0;
foreach (const QByteArray &ba, QByteArray(messyResult).split('\n')) {
QList<QByteArray> values = ba.split('\t');
QByteArray key = values.takeFirst();
nValues += values.count();
QList<QByteArray> comparisonValues;
foreach (const intPair &be, tokenizer.value(key).beginEnd) {
comparisonValues.append(QByteArray(buffer + be.first, be.second - be.first));
}
QCOMPARE(comparisonValues.count(), values.count());
for (int i = 0; i < values.count(); i++) {
QVERIFY(comparisonValues[i].startsWith(values[i]));
}
}
int nValues2 = 0;
HeaderTokenizer::ConstIterator it = tokenizer.constBegin();
for (; it != tokenizer.constEnd(); ++it) {
nValues2 += it.value().beginEnd.count();
}
QCOMPARE(nValues2, nValues);
return; //comment out for parsed header dump to stdout
it = tokenizer.constBegin();
for (; it != tokenizer.constEnd(); ++it) {
if (!it.value().beginEnd.isEmpty()) {
kDebug() << it.key() << ":";
}
foreach (const intPair &be, it.value().beginEnd) {
kDebug() << " " << QByteArray(buffer + be.first, be.second - be.first);
}
}
}
void HeaderTokenizeTest::testRedirectHeader()
{
//Copy the header into a writable buffer
for (int i = 0; i < bufSize; i++) {
buffer[i] = 0;
}
strcpy(buffer, redirectHeader);
HeaderTokenizer tokenizer(buffer);
int tokenizeEnd = tokenizer.tokenize(0, strlen(redirectHeader));
QCOMPARE(tokenizeEnd, (int)strlen(redirectHeader));
typedef QPair<int, int> intPair;
int nValues = 0;
foreach (const QByteArray &ba, QByteArray(redirectResult).split('\n')) {
QList<QByteArray> values = ba.split('\t');
QByteArray key = values.takeFirst();
nValues += values.count();
QList<QByteArray> comparisonValues;
foreach (const intPair &be, tokenizer.value(key).beginEnd) {
comparisonValues.append(QByteArray(buffer + be.first, be.second - be.first));
}
QCOMPARE(comparisonValues.count(), values.count());
for (int i = 0; i < values.count(); i++) {
QVERIFY(comparisonValues[i].startsWith(values[i]));
}
}
int nValues2 = 0;
HeaderTokenizer::ConstIterator it = tokenizer.constBegin();
for (; it != tokenizer.constEnd(); ++it) {
nValues2 += it.value().beginEnd.count();
}
QCOMPARE(nValues2, nValues);
// Fix compiler warning
(void)contentDispositionParser;
}

View file

@ -1,33 +0,0 @@
/* This file is part of the KDE libraries
Copyright (c) 2008 Andreas Hartmetz <ahartmetz@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef HTTPHEADERTOKENIZETEST_H
#define HTTPHEADERTOKENIZETEST_H
#include <QtCore/QObject>
class HeaderTokenizeTest : public QObject
{
Q_OBJECT
private Q_SLOTS:
void testMessyHeader();
void testRedirectHeader();
};
#endif //HTTPHEADERTOKENIZETEST_H

View file

@ -1,55 +0,0 @@
/* This file is part of the KDE libraries
Copyright (C) 2012 Rolf Eike Beer <kde@opensource.sf-tec.de>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "httpobjecttest.h"
#include <qtest_kde.h>
#include <QtCore/QByteArray>
#include <kdebug.h>
#include "moc_httpobjecttest.cpp"
QTEST_KDEMAIN(HeaderObjectTest, NoGUI)
static void runTest()
{
TestHTTPProtocol protocol("http", QByteArray(), "tcp://");
protocol.testParseContentDisposition(QLatin1String("inline; filename=\"foo.pdf\""));
}
void HeaderObjectTest::runAllTests()
{
runTest();
}
TestHTTPProtocol::TestHTTPProtocol ( const QByteArray& protocol, const QByteArray& pool, const QByteArray& app )
: HTTPProtocol(protocol, pool, app)
{
}
TestHTTPProtocol::~TestHTTPProtocol()
{
}
void TestHTTPProtocol::testParseContentDisposition ( const QString& disposition )
{
parseContentDisposition(disposition);
}

View file

@ -1,44 +0,0 @@
/* This file is part of the KDE libraries
Copyright (c) 2012 Rolf Eike Beer <kde@opensource.sf-tec.de>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef HTTPOBJECTTEST_H
#define HTTPOBJECTTEST_H
#include <QtCore/QObject>
#include <http.h>
class HeaderObjectTest : public QObject
{
Q_OBJECT
private Q_SLOTS:
void runAllTests();
};
class TestHTTPProtocol : public HTTPProtocol
{
Q_OBJECT
public:
TestHTTPProtocol( const QByteArray &protocol, const QByteArray &pool,
const QByteArray &app );
virtual ~TestHTTPProtocol();
void testParseContentDisposition(const QString &disposition);
};
#endif //HTTPOBJECTTEST_H

View file

@ -3,18 +3,19 @@ exec=kio_http
protocol=webdav
input=none
output=filesystem
copyToFile=false
copyFromFile=false
listing=Name,Type,Size,Date,AccessDate,Access
reading=true
writing=true
makedir=true
deleting=true
moving=true
deleteRecursive=true
copyFromFile=true
defaultMimetype=application/octet-stream
determineMimetypeFromExtension=false
makedir=false
deleting=false
moving=false
ProxiedBy=http
Icon=folder-remote
maxInstances=20
maxInstancesPerHost=5
defaultMimetype=application/octet-stream
determineMimetypeFromExtension=false
X-DocPath=kioslave/webdav/index.html
Class=:internet

View file

@ -3,18 +3,19 @@ exec=kio_http
protocol=webdavs
input=none
output=filesystem
copyToFile=false
copyFromFile=false
listing=Name,Type,Size,Date,AccessDate,Access
reading=true
writing=true
makedir=true
deleting=true
moving=true
deleteRecursive=true
defaultMimetype=application/octet-stream
determineMimetypeFromExtension=false
makedir=false
deleting=false
moving=false
ProxiedBy=http
Icon=folder-remote
config=webdav
maxInstances=20
maxInstancesPerHost=5
defaultMimetype=application/octet-stream
determineMimetypeFromExtension=false
X-DocPath=kioslave/webdav/index.html
Class=:internet

View file

@ -155,15 +155,9 @@ void BrowserRun::scanFile()
metaData.insert("PropagateHttpHeader", "TRUE");
}
KIO::TransferJob *job;
if ( d->m_browserArgs.doPost() && KRun::url().protocol().startsWith(QLatin1String("http"))) {
job = KIO::http_post( KRun::url(), d->m_browserArgs.postData, KIO::HideProgressInfo );
job->addMetaData( "content-type", d->m_browserArgs.contentType() );
} else {
job = KIO::get(KRun::url(),
d->m_args.reload() ? KIO::Reload : KIO::NoReload,
KIO::HideProgressInfo);
}
KIO::TransferJob *job = KIO::get(KRun::url(),
d->m_args.reload() ? KIO::Reload : KIO::NoReload,
KIO::HideProgressInfo);
if ( d->m_bRemoveReferrer )
metaData.remove("referrer");

View file

@ -108,7 +108,6 @@ set(plasma_LIB_SRCS
private/themedwidgetinterface.cpp
private/tooltip.cpp
private/windowpreview.cpp
private/declarative/declarativenetworkaccessmanagerfactory.cpp
private/effects/halopainter.cpp
querymatch.cpp
runnercontext.cpp

View file

@ -342,7 +342,6 @@ void Applet::restore(KConfigGroup &group)
// local shortcut, if any
//TODO: implement; the shortcut will need to be registered with the containment
/*
#include "accessmanager.h"
#include "private/plasmoidservice_p.h"
#include "authorizationmanager.h"
#include "authorizationmanager.h"

View file

@ -1,39 +0,0 @@
/*
* Copyright 2010 Marco Martin <notmart@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library General Public License as
* published by the Free Software Foundation; either version 2, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "declarativenetworkaccessmanagerfactory_p.h"
#include "config-plasma.h"
#ifndef PLASMA_NO_KIO
#include <kio/accessmanager.h>
#else
#include <QtNetwork/QNetworkAccessManager>
#endif
QNetworkAccessManager *DeclarativeNetworkAccessManagerFactory::create(QObject *parent)
{
#ifndef PLASMA_NO_KIO
return new KIO::AccessManager(parent);
#else
return new QNetworkAccessManager(parent);
#endif
}

View file

@ -1,31 +0,0 @@
/*
* Copyright 2010 Marco Martin <notmart@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library General Public License as
* published by the Free Software Foundation; either version 2, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef DECLARATIVENETWORKACCESSMANAGERFACTORY_H
#define DECLARATIVENETWORKACCESSMANAGERFACTORY_H
#include <QDeclarativeNetworkAccessManagerFactory>
class DeclarativeNetworkAccessManagerFactory : public QDeclarativeNetworkAccessManagerFactory
{
public:
QNetworkAccessManager *create(QObject *parent);
};
#endif

View file

@ -34,7 +34,6 @@
#include <kglobal.h>
#include <kstandarddirs.h>
#include "private/declarative/declarativenetworkaccessmanagerfactory_p.h"
#include "private/dataenginebindings_p.h"
namespace Plasma
@ -259,16 +258,12 @@ DeclarativeWidget::DeclarativeWidget(QGraphicsWidget *parent)
setFlag(QGraphicsItem::ItemHasNoContents);
d->engine = new QDeclarativeEngine(this);
d->engine->setNetworkAccessManagerFactory(new DeclarativeNetworkAccessManagerFactory);
d->component = new QDeclarativeComponent(d->engine, this);
}
DeclarativeWidget::~DeclarativeWidget()
{
QDeclarativeNetworkAccessManagerFactory *factory = d->engine->networkAccessManagerFactory();
d->engine->setNetworkAccessManagerFactory(0);
delete factory;
delete d;
}