mirror of
https://bitbucket.org/smil3y/kdelibs.git
synced 2025-02-23 10:22:48 +00:00
generic: drop KIO HTTP(S) support
Signed-off-by: Ivailo Monev <xakepa10@gmail.com>
This commit is contained in:
parent
ab7d1d326a
commit
581afd3caa
71 changed files with 125 additions and 18388 deletions
2
.github/workflows/codeql-analysis.yml
vendored
2
.github/workflows/codeql-analysis.yml
vendored
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -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)
|
|
@ -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
|
||||
|
|
|
@ -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)
|
|
@ -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"
|
|
@ -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
|
|
@ -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"
|
||||
|
|
@ -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
|
|
@ -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"
|
|
@ -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
|
|
@ -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 );
|
||||
|
|
271
kio/kio/job.cpp
271
kio/kio/job.cpp
|
@ -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;
|
||||
|
|
|
@ -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.
|
||||
*
|
||||
|
|
|
@ -309,7 +309,6 @@ namespace KIO {
|
|||
|
||||
void slotErrorPage();
|
||||
void slotCanResume( KIO::filesize_t offset );
|
||||
void slotPostRedirection();
|
||||
void slotNeedSubUrlData();
|
||||
void slotSubUrlData(KIO::Job*, const QByteArray &);
|
||||
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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}
|
||||
)
|
||||
|
|
|
@ -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
|
|
@ -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>
|
|
@ -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
|
|
@ -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).
|
|
@ -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>
|
|
@ -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
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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 sạch bộ nhớ tạm 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 mục cũ khỏi 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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
@ -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 mạng
|
||||
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
|
|
@ -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
|
|
@ -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
|
|
@ -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"
|
||||
|
|
@ -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
|
|
@ -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"
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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> | <A
|
||||
HREF="http://home.netscape.com/misc/nav_redir/site_map.html" TARGET="_top">Site Map</A> | <A
|
||||
HREF="http://home.netscape.com/misc/nav_redir/howtoget.html" TARGET="_top">How to Get Netscape Products</A> | <A HREF="http://home.netscape.com/misc/nav_redir/ad.html" TARGET="_top">Advertise With Us</A> | <A HREF="http://home.netscape.com/misc/nav_redir/addsite.html" TARGET="_top">Add Site</A> | <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> | <A
|
||||
HREF="http://home.netscape.com/misc/nav_redir/channels/business.html" TARGET="_top">Business</A> | <A HREF="http://home.netscape.com/misc/nav_redir/channels/computers_internet.html" TARGET="_top">Computing & Internet</A> | <A HREF="http://home.netscape.com/misc/nav_redir/channels/entertainment.html" TARGET="_top">Entertainment</A> | <A
|
||||
HREF="http://home.netscape.com/misc/nav_redir/channels/kids_family.html" TARGET="_top">Family</A> | <A
|
||||
HREF="http://home.netscape.com/misc/nav_redir/channels/games.html" TARGET="_top">Games</A> | <A HREF="http://home.netscape.com/misc/nav_redir/channels/health.html" TARGET="_top">Health</A> | <A HREF="http://home.netscape.com/misc/nav_redir/channels/lifestyles.html" TARGET="_top">Lifestyles</A> | <A
|
||||
HREF="http://home.netscape.com/misc/nav_redir/channels/local.html" TARGET="_top">Local</A> | <A HREF="http://home.netscape.com/misc/nav_redir/channels/netscape.html" TARGET="_top">Netscape</A> | <A HREF="http://home.netscape.com/misc/nav_redir/channels/open_directory.html">Netscape Open Directory</A> | <A
|
||||
HREF="http://home.netscape.com/misc/nav_redir/channels/news.html" TARGET="_top">News</A> | <A HREF="http://home.netscape.com/misc/nav_redir/channels/personalize_finance.html" TARGET="_top">Personal Finance</A> | <A
|
||||
HREF="http://home.netscape.com/misc/nav_redir/channels/real_estate.html" TARGET="_top">Real Estate</A> | <A HREF="http://home.netscape.com/misc/nav_redir/channels/education.html" TARGET="_top">Research & Learn</A> | <A HREF="http://home.netscape.com/misc/nav_redir/channels/shopping.html" TARGET="_top">Shopping</A> | <A HREF="http://home.netscape.com/misc/nav_redir/channels/smallbiz.html" TARGET="_top">Small Business</A> | <A
|
||||
HREF="http://home.netscape.com/misc/nav_redir/channels/sports.html" TARGET="_top">Sports</A> | <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>
|
||||
© 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>
|
|
@ -1,3 +0,0 @@
|
|||
HTTP State Management Mechanism
|
||||
http://www.ietf.org/rfc/rfc2965.txt
|
||||
http://www.ietf.org/rfc/rfc2109.txt
|
|
@ -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})
|
|
@ -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"}
|
|
@ -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/
|
|
@ -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"
|
|
@ -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/
|
|
@ -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="/"
|
|
@ -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"
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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/>
|
|
@ -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()
|
|
@ -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;
|
||||
}
|
|
@ -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();
|
||||
};
|
|
@ -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-ä.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);
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue