From 581afd3caafab9e652eb14e279c6d285613fc5bc Mon Sep 17 00:00:00 2001 From: Ivailo Monev Date: Wed, 16 Feb 2022 08:49:13 +0200 Subject: [PATCH] generic: drop KIO HTTP(S) support Signed-off-by: Ivailo Monev --- .github/workflows/codeql-analysis.yml | 2 +- CMakeLists.txt | 8 - appveyor.yml | 8 +- cmake/modules/FindGSSAPI.cmake | 79 - kio/CMakeLists.txt | 3 - kio/httpfilter/README | 12 - kio/httpfilter/httpfilter.cc | 193 - kio/httpfilter/httpfilter.h | 107 - kio/kio/accessmanager.cpp | 535 -- kio/kio/accessmanager.h | 324 - kio/kio/accessmanagerreply_p.cpp | 483 -- kio/kio/accessmanagerreply_p.h | 106 - kio/kio/deletejob.cpp | 22 +- kio/kio/job.cpp | 271 - kio/kio/job.h | 99 +- kio/kio/job_p.h | 1 - kio/kio/jobclasses.h | 1 - kioslave/http/CMakeLists.txt | 70 +- kioslave/http/README.http_cache_cleaner | 20 - kioslave/http/README.webdav | 184 - kioslave/http/RFCs | 7 - kioslave/http/THOUGHTS | 28 - kioslave/http/TODO | 35 - kioslave/http/config-gssapi.h.cmake | 5 - kioslave/http/http.cpp | 5561 +---------------- kioslave/http/http.h | 616 +- kioslave/http/http.protocol | 12 +- kioslave/http/http_cache_cleaner.cpp | 868 --- kioslave/http/http_cache_cleaner.desktop | 195 - kioslave/http/httpauthentication.cpp | 927 --- kioslave/http/httpauthentication.h | 251 - kioslave/http/https.protocol | 13 +- kioslave/http/kcookiejar/CMakeLists.txt | 62 - kioslave/http/kcookiejar/kcookiejar.cpp | 1633 ----- kioslave/http/kcookiejar/kcookiejar.desktop | 137 - kioslave/http/kcookiejar/kcookiejar.h | 394 -- kioslave/http/kcookiejar/kcookiejar_include.h | 11 - kioslave/http/kcookiejar/kcookieserver.cpp | 575 -- kioslave/http/kcookiejar/kcookieserver.h | 101 - kioslave/http/kcookiejar/kcookiewin.cpp | 341 - kioslave/http/kcookiejar/kcookiewin.h | 82 - kioslave/http/kcookiejar/main.cpp | 78 - .../http/kcookiejar/netscape_cookie_spec.html | 331 - kioslave/http/kcookiejar/specifications | 3 - kioslave/http/kcookiejar/tests/CMakeLists.txt | 7 - kioslave/http/kcookiejar/tests/cookie.test | 188 - .../http/kcookiejar/tests/cookie_rfc.test | 172 - .../http/kcookiejar/tests/cookie_saving.test | 434 -- .../http/kcookiejar/tests/cookie_session.test | 51 - .../kcookiejar/tests/cookie_settings.test | 112 - .../http/kcookiejar/tests/kcookiejartest.cpp | 313 - kioslave/http/parsinghelpers.cpp | 597 -- kioslave/http/parsinghelpers.h | 86 - kioslave/http/shoutcast-icecast.txt | 605 -- kioslave/http/tests/CMakeLists.txt | 50 - .../http/tests/httpauthenticationtest.cpp | 227 - kioslave/http/tests/httpauthenticationtest.h | 32 - .../http/tests/httpheaderdispositiontest.cpp | 363 -- .../http/tests/httpheaderdispositiontest.h | 33 - .../http/tests/httpheadertokenizetest.cpp | 198 - kioslave/http/tests/httpheadertokenizetest.h | 33 - kioslave/http/tests/httpobjecttest.cpp | 55 - kioslave/http/tests/httpobjecttest.h | 44 - kioslave/http/webdav.protocol | 15 +- kioslave/http/webdavs.protocol | 15 +- kparts/browserrun.cpp | 12 +- plasma/CMakeLists.txt | 1 - plasma/applet.cpp | 1 - ...declarativenetworkaccessmanagerfactory.cpp | 39 - ...declarativenetworkaccessmanagerfactory_p.h | 31 - plasma/widgets/declarativewidget.cpp | 5 - 71 files changed, 125 insertions(+), 18388 deletions(-) delete mode 100644 cmake/modules/FindGSSAPI.cmake delete mode 100644 kio/httpfilter/README delete mode 100644 kio/httpfilter/httpfilter.cc delete mode 100644 kio/httpfilter/httpfilter.h delete mode 100644 kio/kio/accessmanager.cpp delete mode 100644 kio/kio/accessmanager.h delete mode 100644 kio/kio/accessmanagerreply_p.cpp delete mode 100644 kio/kio/accessmanagerreply_p.h delete mode 100644 kioslave/http/README.http_cache_cleaner delete mode 100644 kioslave/http/README.webdav delete mode 100644 kioslave/http/RFCs delete mode 100644 kioslave/http/THOUGHTS delete mode 100644 kioslave/http/TODO delete mode 100644 kioslave/http/config-gssapi.h.cmake delete mode 100644 kioslave/http/http_cache_cleaner.cpp delete mode 100644 kioslave/http/http_cache_cleaner.desktop delete mode 100644 kioslave/http/httpauthentication.cpp delete mode 100644 kioslave/http/httpauthentication.h delete mode 100644 kioslave/http/kcookiejar/CMakeLists.txt delete mode 100644 kioslave/http/kcookiejar/kcookiejar.cpp delete mode 100644 kioslave/http/kcookiejar/kcookiejar.desktop delete mode 100644 kioslave/http/kcookiejar/kcookiejar.h delete mode 100644 kioslave/http/kcookiejar/kcookiejar_include.h delete mode 100644 kioslave/http/kcookiejar/kcookieserver.cpp delete mode 100644 kioslave/http/kcookiejar/kcookieserver.h delete mode 100644 kioslave/http/kcookiejar/kcookiewin.cpp delete mode 100644 kioslave/http/kcookiejar/kcookiewin.h delete mode 100644 kioslave/http/kcookiejar/main.cpp delete mode 100644 kioslave/http/kcookiejar/netscape_cookie_spec.html delete mode 100644 kioslave/http/kcookiejar/specifications delete mode 100644 kioslave/http/kcookiejar/tests/CMakeLists.txt delete mode 100644 kioslave/http/kcookiejar/tests/cookie.test delete mode 100644 kioslave/http/kcookiejar/tests/cookie_rfc.test delete mode 100644 kioslave/http/kcookiejar/tests/cookie_saving.test delete mode 100644 kioslave/http/kcookiejar/tests/cookie_session.test delete mode 100644 kioslave/http/kcookiejar/tests/cookie_settings.test delete mode 100644 kioslave/http/kcookiejar/tests/kcookiejartest.cpp delete mode 100644 kioslave/http/parsinghelpers.cpp delete mode 100644 kioslave/http/parsinghelpers.h delete mode 100644 kioslave/http/shoutcast-icecast.txt delete mode 100644 kioslave/http/tests/CMakeLists.txt delete mode 100644 kioslave/http/tests/httpauthenticationtest.cpp delete mode 100644 kioslave/http/tests/httpauthenticationtest.h delete mode 100644 kioslave/http/tests/httpheaderdispositiontest.cpp delete mode 100644 kioslave/http/tests/httpheaderdispositiontest.h delete mode 100644 kioslave/http/tests/httpheadertokenizetest.cpp delete mode 100644 kioslave/http/tests/httpheadertokenizetest.h delete mode 100644 kioslave/http/tests/httpobjecttest.cpp delete mode 100644 kioslave/http/tests/httpobjecttest.h delete mode 100644 plasma/private/declarative/declarativenetworkaccessmanagerfactory.cpp delete mode 100644 plasma/private/declarative/declarativenetworkaccessmanagerfactory_p.h diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 7ebece79..67b855a5 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -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 diff --git a/CMakeLists.txt b/CMakeLists.txt index feb7f403..fa2bb54b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 diff --git a/appveyor.yml b/appveyor.yml index 0c5a5d66..12e1f177 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -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" diff --git a/cmake/modules/FindGSSAPI.cmake b/cmake/modules/FindGSSAPI.cmake deleted file mode 100644 index 35fd3909..00000000 --- a/cmake/modules/FindGSSAPI.cmake +++ /dev/null @@ -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 -# -# 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) diff --git a/kio/CMakeLists.txt b/kio/CMakeLists.txt index 08d5e374..95e0cb25 100644 --- a/kio/CMakeLists.txt +++ b/kio/CMakeLists.txt @@ -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 diff --git a/kio/httpfilter/README b/kio/httpfilter/README deleted file mode 100644 index 0245fb8e..00000000 --- a/kio/httpfilter/README +++ /dev/null @@ -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) diff --git a/kio/httpfilter/httpfilter.cc b/kio/httpfilter/httpfilter.cc deleted file mode 100644 index 38203c60..00000000 --- a/kio/httpfilter/httpfilter.cc +++ /dev/null @@ -1,193 +0,0 @@ -/* - This file is part of the KDE libraries - Copyright (c) 2002 Waldo Bastian - Copyright 2009 David Faure - - 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 -#include - -#include - -#include - -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" diff --git a/kio/httpfilter/httpfilter.h b/kio/httpfilter/httpfilter.h deleted file mode 100644 index 1af9b885..00000000 --- a/kio/httpfilter/httpfilter.h +++ /dev/null @@ -1,107 +0,0 @@ -/* - This file is part of the KDE libraries - Copyright (c) 2002 Waldo Bastian - Copyright 2009 David Faure - - 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 - -class KGzipFilter; -#include - -#include -#include - -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 diff --git a/kio/kio/accessmanager.cpp b/kio/kio/accessmanager.cpp deleted file mode 100644 index 1f8c95e8..00000000 --- a/kio/kio/accessmanager.cpp +++ /dev/null @@ -1,535 +0,0 @@ -/* - * This file is part of the KDE project. - * - * Copyright (C) 2009 - 2012 Dawit Alemayehu - * Copyright (C) 2008 - 2009 Urs Wolfer - * 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 -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -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 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 (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(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(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 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 CookieJar::cookiesForUrl(const QUrl &url) const -{ - QList cookieList; - - if (!d->isEnabled) { - return cookieList; - } - QDBusInterface kcookiejar("org.kde.kded", "/modules/kcookiejar", "org.kde.KCookieServer"); - QDBusReply 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 &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" - diff --git a/kio/kio/accessmanager.h b/kio/kio/accessmanager.h deleted file mode 100644 index 8fe07805..00000000 --- a/kio/kio/accessmanager.h +++ /dev/null @@ -1,324 +0,0 @@ -/* - * This file is part of the KDE project. - * - * Copyright (C) 2008 - 2009 Urs Wolfer - * Copyright (C) 2009 - 2012 Dawit Alemayehu - * - * 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 - -#include -#include -#include - -#include - -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(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. - * - * IMPORTANTThis 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 \ - * @author Dawit Alemayehu \ - * - * @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(view->page()->accessManager()->cookieJar()); - * @endcode - * - * IMPORTANTThis 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 \ - * @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 cookiesForUrl(const QUrl &url) const; - - /** - * Reimplemented for internal reasons, the API is not affected. - * - * @see QNetworkCookieJar::setCookiesFromUrl - * @internal - */ - bool setCookiesFromUrl(const QList &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 diff --git a/kio/kio/accessmanagerreply_p.cpp b/kio/kio/accessmanagerreply_p.cpp deleted file mode 100644 index 31d96efb..00000000 --- a/kio/kio/accessmanagerreply_p.cpp +++ /dev/null @@ -1,483 +0,0 @@ -/* - * This file is part of the KDE project. - * - * Copyright (C) 2008 Alex Merry - * Copyright (C) 2008 - 2009 Urs Wolfer - * Copyright (C) 2009 - 2012 Dawit Alemayehu - * - * 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 -#include -#include -#include - -#include - -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(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(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 ":" - 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(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(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(kJob)); - } - - emitFinished(true); -} - -void AccessManagerReply::slotStatResult(KJob* kJob) -{ - if (jobError(kJob)) { - emit error (error()); - emitFinished(true); - return; - } - - KIO::StatJob* statJob = qobject_cast(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" diff --git a/kio/kio/accessmanagerreply_p.h b/kio/kio/accessmanagerreply_p.h deleted file mode 100644 index 65ac1a72..00000000 --- a/kio/kio/accessmanagerreply_p.h +++ /dev/null @@ -1,106 +0,0 @@ -/* - * This file is part of the KDE project. - * - * Copyright (C) 2008 - 2009 Urs Wolfer - * Copyright (C) 2009 - 2012 Dawit Alemayehu - * - * 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 -#include - -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 \ - */ - -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 m_kioJob; -}; - -} - -#endif // KIO_ACCESSMANAGERREPLY_P_H diff --git a/kio/kio/deletejob.cpp b/kio/kio/deletejob.cpp index d53d388b..83ef5660 100644 --- a/kio/kio/deletejob.cpp +++ b/kio/kio/deletejob.cpp @@ -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 ); diff --git a/kio/kio/job.cpp b/kio/kio/job.cpp index 58823015..94f75f08 100644 --- a/kio/kio/job.cpp +++ b/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() ); - 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(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; diff --git a/kio/kio/job.h b/kio/kio/job.h index c22d39ae..072cdd62 100644 --- a/kio/kio/job.h +++ b/kio/kio/job.h @@ -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. * diff --git a/kio/kio/job_p.h b/kio/kio/job_p.h index 14c683e9..1b72128a 100644 --- a/kio/kio/job_p.h +++ b/kio/kio/job_p.h @@ -309,7 +309,6 @@ namespace KIO { void slotErrorPage(); void slotCanResume( KIO::filesize_t offset ); - void slotPostRedirection(); void slotNeedSubUrlData(); void slotSubUrlData(KIO::Job*, const QByteArray &); diff --git a/kio/kio/jobclasses.h b/kio/kio/jobclasses.h index 7ded45f9..56f0aa90 100644 --- a/kio/kio/jobclasses.h +++ b/kio/kio/jobclasses.h @@ -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()) diff --git a/kioslave/http/CMakeLists.txt b/kioslave/http/CMakeLists.txt index 1b11c400..ac57d679 100644 --- a/kioslave/http/CMakeLists.txt +++ b/kioslave/http/CMakeLists.txt @@ -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} ) diff --git a/kioslave/http/README.http_cache_cleaner b/kioslave/http/README.http_cache_cleaner deleted file mode 100644 index 7714bfba..00000000 --- a/kioslave/http/README.http_cache_cleaner +++ /dev/null @@ -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 diff --git a/kioslave/http/README.webdav b/kioslave/http/README.webdav deleted file mode 100644 index b24baf64..00000000 --- a/kioslave/http/README.webdav +++ /dev/null @@ -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 - or . - -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:) - - - - - http://www.foo.bar/container/ - - - - - - - 0 - Jane Smith - Infinite - - - opaquelocktoken:f81de2ad-7f3d-a1b2-4f3c-00a0c91a9d76A - - - - - - - 1 - John Doe - - - opaquelocktoken:f81de2ad-7f3d-a1b2-4f3c-00a0c91a9d76B - - - - - - - - - - - - - - - - HTTP/1.1 200 OK - - - diff --git a/kioslave/http/RFCs b/kioslave/http/RFCs deleted file mode 100644 index 5c034ac4..00000000 --- a/kioslave/http/RFCs +++ /dev/null @@ -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 diff --git a/kioslave/http/THOUGHTS b/kioslave/http/THOUGHTS deleted file mode 100644 index 9715b5c2..00000000 --- a/kioslave/http/THOUGHTS +++ /dev/null @@ -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). diff --git a/kioslave/http/TODO b/kioslave/http/TODO deleted file mode 100644 index a21ff872..00000000 --- a/kioslave/http/TODO +++ /dev/null @@ -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 -Dawit Alemayehu -WebDAV support: Hamish Rodda diff --git a/kioslave/http/config-gssapi.h.cmake b/kioslave/http/config-gssapi.h.cmake deleted file mode 100644 index 73b1999e..00000000 --- a/kioslave/http/config-gssapi.h.cmake +++ /dev/null @@ -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 diff --git a/kioslave/http/http.cpp b/kioslave/http/http.cpp index 49ba5e92..1f0d2711 100644 --- a/kioslave/http/http.cpp +++ b/kioslave/http/http.cpp @@ -1,132 +1,37 @@ -/* - Copyright (C) 2000-2003 Waldo Bastian - Copyright (C) 2000-2002 George Staikos - Copyright (C) 2000-2002 Dawit Alemayehu - Copyright (C) 2001,2002 Hamish Rodda - Copyright (C) 2007 Nick Shaforostoff - Copyright (C) 2007 Daniel Nicoletti - Copyright (C) 2008,2009 Andreas Hartmetz +/* This file is part of the KDE libraries + Copyright (C) 2022 Ivailo Monev - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public - License (LGPL) 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. */ -// TODO delete / do not save very big files; "very big" to be defined - -#define QT_NO_CAST_FROM_ASCII - #include "http.h" +#include "kdebug.h" +#include "kcomponentdata.h" -#include +#include -#include -#include -#include -#include -#include -#include -#include // must be explicitly included for MacOSX - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include - -#include - -#include -#include -#include -#include -#include -#include - -#include "httpauthentication.h" - -// HeaderTokenizer declarations -#include "parsinghelpers.h" -//string parsing helpers and HeaderTokenizer implementation -#include "parsinghelpers.cpp" - -// KDE5 TODO (QT5) : use QString::htmlEscape or whatever https://qt.gitorious.org/qt/qtbase/merge_requests/56 -// ends up with. -static QString htmlEscape(const QString &plain) -{ - QString rich; - rich.reserve(int(plain.length() * 1.1)); - for (int i = 0; i < plain.length(); ++i) { - if (plain.at(i) == QLatin1Char('<')) - rich += QLatin1String("<"); - else if (plain.at(i) == QLatin1Char('>')) - rich += QLatin1String(">"); - else if (plain.at(i) == QLatin1Char('&')) - rich += QLatin1String("&"); - else if (plain.at(i) == QLatin1Char('"')) - rich += QLatin1String("""); - else - rich += plain.at(i); - } - rich.squeeze(); - return rich; -} - -static bool supportedProxyScheme(const QString& scheme) -{ - // scheme is supposed to be lowercase - return (scheme.startsWith(QLatin1String("http")) - || scheme == QLatin1String("socks")); -} - -// see filenameFromUrl(): a sha1 hash is 160 bits -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_MaxInMemPostBufSize = 256 * 1024; // Write anyting over 256 KB to file... - -using namespace KIO; +#include +#include extern "C" int Q_DECL_EXPORT kdemain( int argc, char **argv ) { - QCoreApplication app( argc, argv ); // needed for QSocketNotifier + QCoreApplication app(argc, argv); KComponentData componentData( "kio_http", "kdelibs4" ); - (void) KGlobal::locale(); + ( void ) KGlobal::locale(); + + kDebug(7103) << "Starting " << getpid(); if (argc != 4) { @@ -134,5424 +39,50 @@ extern "C" int Q_DECL_EXPORT kdemain( int argc, char **argv ) exit(-1); } - HTTPProtocol slave(argv[1], argv[2], argv[3]); + HttpProtocol slave(argv[2], argv[3]); slave.dispatchLoop(); + + kDebug(7103) << "Done"; return 0; } -/*********************************** Generic utility functions ********************/ - -static QString toQString(const QByteArray& value) -{ - return QString::fromLatin1(value.constData(), value.size()); -} - -static bool isCrossDomainRequest( const QString& fqdn, const QString& originURL ) -{ - //TODO read the RFC - if (originURL == QLatin1String("true")) // Backwards compatibility - return true; - - KUrl url ( originURL ); - - // Document Origin domain - QString a = url.host(); - // Current request domain - QString b = fqdn; - - if (a == b) - return false; - - QStringList la = a.split(QLatin1Char('.'), QString::SkipEmptyParts); - QStringList lb = b.split(QLatin1Char('.'), QString::SkipEmptyParts); - - if (qMin(la.count(), lb.count()) < 2) { - return true; // better safe than sorry... - } - - while(la.count() > 2) - la.pop_front(); - while(lb.count() > 2) - lb.pop_front(); - - return la != lb; -} - -/* - Eliminates any custom header that could potentially alter the request -*/ -static QString sanitizeCustomHTTPHeader(const QString& _header) -{ - QString sanitizedHeaders; - const QStringList headers = _header.split(QRegExp(QLatin1String("[\r\n]"))); - - for(QStringList::ConstIterator it = headers.begin(); it != headers.end(); ++it) - { - // Do not allow Request line to be specified and ignore - // the other HTTP headers. - if (!(*it).contains(QLatin1Char(':')) || - (*it).startsWith(QLatin1String("host"), Qt::CaseInsensitive) || - (*it).startsWith(QLatin1String("proxy-authorization"), Qt::CaseInsensitive) || - (*it).startsWith(QLatin1String("via"), Qt::CaseInsensitive)) - continue; - - sanitizedHeaders += (*it); - sanitizedHeaders += QLatin1String("\r\n"); - } - sanitizedHeaders.chop(2); - - return sanitizedHeaders; -} - -static bool isPotentialSpoofingAttack(const HTTPProtocol::HTTPRequest& request, const KConfigGroup* config) -{ - // kDebug(7113) << request.url << "response code: " << request.responseCode << "previous response code:" << request.prevResponseCode; - if (config->readEntry("no-spoof-check", false)) { - return false; - } - - if (request.url.user().isEmpty()) { - return false; - } - - // We already have cached authentication. - if (config->readEntry(QLatin1String("cached-www-auth"), false)) { - return false; - } - - const QString userName = config->readEntry(QLatin1String("LastSpoofedUserName"), QString()); - return ((userName.isEmpty() || userName != request.url.user()) && request.responseCode != 401 && request.prevResponseCode != 401); -} - -// for a given response code, conclude if the response is going to/likely to have a response body -static bool canHaveResponseBody(int responseCode, KIO::HTTP_METHOD method) -{ -/* RFC 2616 says... - 1xx: false - 200: method HEAD: false, otherwise:true - 201: true - 202: true - 203: see 200 - 204: false - 205: false - 206: true - 300: see 200 - 301: see 200 - 302: see 200 - 303: see 200 - 304: false - 305: probably like 300, RFC seems to expect disconnection afterwards... - 306: (reserved), for simplicity do it just like 200 - 307: see 200 - 4xx: see 200 - 5xx :see 200 -*/ - if (responseCode >= 100 && responseCode < 200) { - return false; - } - switch (responseCode) { - case 201: - case 202: - case 206: - // RFC 2616 does not mention HEAD in the description of the above. if the assert turns out - // to be a problem the response code should probably be treated just like 200 and friends. - Q_ASSERT(method != HTTP_HEAD); - return true; - case 204: - case 205: - case 304: - return false; - default: - break; - } - // safe (and for most remaining response codes exactly correct) default - return method != HTTP_HEAD; -} - -static bool isEncryptedHttpVariety(const QByteArray &p) -{ - return p == "https" || p == "webdavs"; -} - -static bool isValidProxy(const KUrl &u) -{ - return u.isValid() && u.hasHost(); -} - -static bool isHttpProxy(const KUrl &u) -{ - return isValidProxy(u) && u.protocol() == QLatin1String("http"); -} - -static QIODevice* createPostBufferDeviceFor (KIO::filesize_t size) -{ - QIODevice* device; - if (size > static_cast(s_MaxInMemPostBufSize)) - device = new KTemporaryFile; - else - device = new QBuffer; - - if (!device->open(QIODevice::ReadWrite)) - return 0; - - return device; -} - -static qint64 toTime_t(const QString& value, KDateTime::TimeFormat format) -{ - const KDateTime dt = KDateTime::fromString(value, format); - return (dt.isValid() ? (dt.toUtc().dateTime().toMSecsSinceEpoch()/1000) : -1); -} - -static qint64 parseDateTime( const QString& input, const QString& type ) -{ - if (type == QLatin1String("dateTime.tz") ) { - return toTime_t(input, KDateTime::ISODate); - } else if (type == QLatin1String("dateTime.rfc1123")) { - return toTime_t(input, KDateTime::RFCDate); - } - - // format not advertised... try to parse anyway - qint64 tsec = toTime_t(input, KDateTime::RFCDate); - if (tsec == -1) - tsec = toTime_t(input, KDateTime::ISODate); - - return tsec; -} - -// Since a lot of webdav servers seem not to send the content-type information -// for the requested directory listings, we attempt to guess the mime-type from -// the resource name so long as the resource is not a directory. -static void updateUDSEntryMimeType(UDSEntry* entry) -{ - const QString mimeType(entry->stringValue(KIO::UDSEntry::UDS_MIME_TYPE)); - const qint64 type = entry->numberValue(KIO::UDSEntry::UDS_FILE_TYPE); - const QString name (entry->stringValue(KIO::UDSEntry::UDS_NAME)); - - kDebug(7113) << "item:" << name << ", mimeType:" << mimeType; - - if (mimeType.isEmpty() && type != S_IFDIR) { - KMimeType::Ptr mime = KMimeType::findByUrl(name, 0, false, true); - if (mime && !mime->isDefault()) { - kDebug(7113) << "Setting" << mime->name() << "as guessed mime type for" << name; - entry->insert(KIO::UDSEntry::UDS_GUESSED_MIME_TYPE, mime->name()); - } - } -} - -static void changeProtocolToHttp(KUrl* url) -{ - const QString protocol(url->protocol()); - if (protocol == QLatin1String("webdavs")) { - url->setProtocol(QLatin1String("https")); - } else if (protocol == QLatin1String("webdav")) { - url->setProtocol(QLatin1String("http")); - } -} - -/************************************************************************************************************************/ - - -QByteArray HTTPProtocol::HTTPRequest::methodString() const -{ - if (!methodStringOverride.isEmpty()) - return (methodStringOverride).toLatin1(); - - switch(method) { - case HTTP_GET: - return "GET"; - case HTTP_PUT: - return "PUT"; - case HTTP_POST: - return "POST"; - case HTTP_HEAD: - return "HEAD"; - case HTTP_DELETE: - return "DELETE"; - case HTTP_OPTIONS: - return "OPTIONS"; - case DAV_PROPFIND: - return "PROPFIND"; - case DAV_PROPPATCH: - return "PROPPATCH"; - case DAV_MKCOL: - return "MKCOL"; - case DAV_COPY: - return "COPY"; - case DAV_MOVE: - return "MOVE"; - case DAV_LOCK: - return "LOCK"; - case DAV_UNLOCK: - return "UNLOCK"; - case DAV_SEARCH: - return "SEARCH"; - case DAV_SUBSCRIBE: - return "SUBSCRIBE"; - case DAV_UNSUBSCRIBE: - return "UNSUBSCRIBE"; - case DAV_POLL: - return "POLL"; - case DAV_NOTIFY: - return "NOTIFY"; - case DAV_REPORT: - return "REPORT"; - default: - Q_ASSERT(false); - return QByteArray(); - } -} - -static QString formatHttpDate(qint64 date) -{ - KDateTime dt; - dt.setTime_t(date); - QString ret = dt.toString(KDateTime::RFCDateDay); - ret.chop(6); // remove " +0000" - // RFCDate[Day] omits the second if zero, but HTTP requires it; see bug 240585. - if (!dt.time().second()) { - ret.append(QLatin1String(":00")); - } - ret.append(QLatin1String(" GMT")); - return ret; -} - -static bool isAuthenticationRequired(int responseCode) -{ - return (responseCode == 401) || (responseCode == 407); -} - -#define NO_SIZE ((KIO::filesize_t) -1) - -#ifdef HAVE_STRTOLL -#define STRTOLL strtoll -#else -#define STRTOLL strtol -#endif - - -/************************************** HTTPProtocol **********************************************/ - - -HTTPProtocol::HTTPProtocol( const QByteArray &protocol, const QByteArray &pool, - const QByteArray &app ) - : TCPSlaveBase(protocol, pool, app, isEncryptedHttpVariety(protocol)) - , m_iSize(NO_SIZE) - , m_iPostDataSize(NO_SIZE) - , m_isBusy(false) - , m_POSTbuf(0) - , m_maxCacheAge(DEFAULT_MAX_CACHE_AGE) - , m_maxCacheSize(DEFAULT_MAX_CACHE_SIZE) - , m_protocol(protocol) - , m_wwwAuth(0) - , m_proxyAuth(0) - , m_socketProxyAuth(0) - , m_iError(0) - , m_isLoadingErrorPage(false) - , m_remoteRespTimeout(DEFAULT_RESPONSE_TIMEOUT) - , m_iEOFRetryCount(0) -{ - reparseConfiguration(); - setBlocking(true); - connect(socket(), SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)), - this, SLOT(proxyAuthenticationForSocket(QNetworkProxy,QAuthenticator*))); -} - -HTTPProtocol::~HTTPProtocol() -{ - httpClose(false); -} - -void HTTPProtocol::reparseConfiguration() -{ - kDebug(7113); - - delete m_proxyAuth; - delete m_wwwAuth; - m_proxyAuth = 0; - m_wwwAuth = 0; - m_request.proxyUrl.clear(); //TODO revisit - m_request.proxyUrls.clear(); - - TCPSlaveBase::reparseConfiguration(); -} - -void HTTPProtocol::resetConnectionSettings() -{ - m_isEOF = false; - m_iError = 0; - m_isLoadingErrorPage = false; -} - -quint16 HTTPProtocol::defaultPort() const -{ - return isEncryptedHttpVariety(m_protocol) ? DEFAULT_HTTPS_PORT : DEFAULT_HTTP_PORT; -} - -void HTTPProtocol::resetResponseParsing() -{ - m_isRedirection = false; - m_isChunked = false; - m_iSize = NO_SIZE; - clearUnreadBuffer(); - - m_responseHeaders.clear(); - m_contentEncodings.clear(); - m_transferEncodings.clear(); - m_contentMD5.clear(); - m_mimeType.clear(); - - setMetaData(QLatin1String("request-id"), m_request.id); -} - -void HTTPProtocol::resetSessionSettings() -{ - // Follow HTTP/1.1 spec and enable keep-alive by default - // unless the remote side tells us otherwise or we determine - // the persistent link has been terminated by the remote end. - m_request.isKeepAlive = true; - m_request.keepAliveTimeout = 0; - - m_request.redirectUrl = KUrl(); - m_request.useCookieJar = config()->readEntry("Cookies", false); - m_request.cacheTag.useCache = config()->readEntry("UseCache", true); - m_request.preferErrorPage = config()->readEntry("errorPage", true); - const bool noAuth = config()->readEntry("no-auth", false); - m_request.doNotWWWAuthenticate = config()->readEntry("no-www-auth", noAuth); - m_request.doNotProxyAuthenticate = config()->readEntry("no-proxy-auth", noAuth); - m_strCacheDir = config()->readPathEntry("CacheDir", QString()); - m_maxCacheAge = config()->readEntry("MaxCacheAge", DEFAULT_MAX_CACHE_AGE); - m_request.windowId = config()->readEntry("window-id"); - - m_request.methodStringOverride = metaData(QLatin1String("CustomHTTPMethod")); - m_request.sentMethodString.clear(); - - kDebug(7113) << "Window Id =" << m_request.windowId; - kDebug(7113) << "ssl_was_in_use =" << metaData(QLatin1String("ssl_was_in_use")); - - m_request.referrer.clear(); - // RFC 2616: do not send the referrer if the referrer page was served using SSL and - // the current page does not use SSL. - if ( config()->readEntry("SendReferrer", true) && - (isEncryptedHttpVariety(m_protocol) || metaData(QLatin1String("ssl_was_in_use")) != QLatin1String("TRUE") ) ) - { - KUrl refUrl(metaData(QLatin1String("referrer"))); - if (refUrl.isValid()) { - // Sanitize - QString protocol = refUrl.protocol(); - if (protocol.startsWith(QLatin1String("webdav"))) { - protocol.replace(0, 6, QLatin1String("http")); - refUrl.setProtocol(protocol); - } - - if (protocol.startsWith(QLatin1String("http"))) { - m_request.referrer = toQString(refUrl.toEncoded(QUrl::RemoveUserInfo | QUrl::RemoveFragment)); - } - } - } - - if (config()->readEntry("SendLanguageSettings", true)) { - m_request.charsets = config()->readEntry("Charsets", DEFAULT_PARTIAL_CHARSET_HEADER); - if (!m_request.charsets.contains(QLatin1String("*;"), Qt::CaseInsensitive)) { - m_request.charsets += QLatin1String(",*;q=0.5"); - } - m_request.languages = config()->readEntry("Languages", DEFAULT_LANGUAGE_HEADER); - } else { - m_request.charsets.clear(); - m_request.languages.clear(); - } - - // Adjust the offset value based on the "resume" meta-data. - QString resumeOffset = metaData(QLatin1String("resume")); - if (!resumeOffset.isEmpty()) { - m_request.offset = resumeOffset.toULongLong(); - } else { - m_request.offset = 0; - } - // Same procedure for endoffset. - QString resumeEndOffset = metaData(QLatin1String("resume_until")); - if (!resumeEndOffset.isEmpty()) { - m_request.endoffset = resumeEndOffset.toULongLong(); - } else { - m_request.endoffset = 0; - } - - m_request.disablePassDialog = config()->readEntry("DisablePassDlg", false); - m_request.allowTransferCompression = config()->readEntry("AllowCompressedPage", true); - m_request.id = metaData(QLatin1String("request-id")); - - // Store user agent for this host. - if (config()->readEntry("SendUserAgent", true)) { - m_request.userAgent = metaData(QLatin1String("UserAgent")); - } else { - m_request.userAgent.clear(); - } - - m_request.cacheTag.etag.clear(); - - m_request.cacheTag.servedDate = -1; - m_request.cacheTag.lastModifiedDate = -1; - m_request.cacheTag.expireDate = -1; - - m_request.responseCode = 0; - m_request.prevResponseCode = 0; - - delete m_wwwAuth; - m_wwwAuth = 0; - delete m_socketProxyAuth; - m_socketProxyAuth = 0; - - // Obtain timeout values - m_remoteRespTimeout = responseTimeout(); - - // Bounce back the actual referrer sent - setMetaData(QLatin1String("referrer"), m_request.referrer); - - // Reset the post data size - m_iPostDataSize = NO_SIZE; - - // Reset the EOF retry counter - m_iEOFRetryCount = 0; -} - -void HTTPProtocol::setHost( const QString& host, quint16 port, - const QString& user, const QString& pass ) -{ - // Reset the webdav-capable flags for this host - if ( m_request.url.host() != host ) - m_davHostOk = m_davHostUnsupported = false; - - m_request.url.setHost(host); - - // is it an IPv6 address? - if (host.indexOf(QLatin1Char(':')) == -1) { - m_request.encoded_hostname = toQString(QUrl::toAce(host)); - } else { - int pos = host.indexOf(QLatin1Char('%')); - if (pos == -1) - m_request.encoded_hostname = QLatin1Char('[') + host + QLatin1Char(']'); - else - // don't send the scope-id in IPv6 addresses to the server - m_request.encoded_hostname = QLatin1Char('[') + host.left(pos) + QLatin1Char(']'); - } - m_request.url.setPort((port > 0 && port != defaultPort()) ? port : -1); - m_request.url.setUser(user); - m_request.url.setPass(pass); - - // On new connection always clear previous proxy information... - m_request.proxyUrl.clear(); - m_request.proxyUrls.clear(); - - kDebug(7113) << "Hostname is now:" << m_request.url.host() - << "(" << m_request.encoded_hostname << ")"; -} - -bool HTTPProtocol::maybeSetRequestUrl(const KUrl &u) -{ - kDebug(7113) << u; - - m_request.url = u; - m_request.url.setPort(u.port(defaultPort()) != defaultPort() ? u.port() : -1); - - if (u.host().isEmpty()) { - error( KIO::ERR_UNKNOWN_HOST, i18n("No host specified.")); - return false; - } - - if (u.path().isEmpty()) { - KUrl newUrl(u); - newUrl.setPath(QLatin1String("/")); - redirection(newUrl); - finished(); - return false; - } - - return true; -} - -void HTTPProtocol::proceedUntilResponseContent( bool dataInternal /* = false */ ) -{ - kDebug (7113); - - const bool status = (proceedUntilResponseHeader() && readBody(dataInternal)); - - // If not an error condition or internal request, close - // the connection based on the keep alive settings... - if (!m_iError && !dataInternal) { - httpClose(m_request.isKeepAlive); - } - - // if data is required internally or we got error, don't finish, - // it is processed before we finish() - if (dataInternal || !status) { - return; - } - - if (!sendHttpError()) { - finished(); - } -} - -bool HTTPProtocol::proceedUntilResponseHeader() -{ - kDebug (7113); - - // Retry the request until it succeeds or an unrecoverable error occurs. - // Recoverable errors are, for example: - // - Proxy or server authentication required: Ask for credentials and try again, - // this time with an authorization header in the request. - // - Server-initiated timeout on keep-alive connection: Reconnect and try again - - while (true) { - if (!sendQuery()) { - return false; - } - if (readResponseHeader()) { - // Success, finish the request. - break; - } - - // If not loading error page and the response code requires us to resend the query, - // then throw away any error message that might have been sent by the server. - if (!m_isLoadingErrorPage && isAuthenticationRequired(m_request.responseCode)) { - // This gets rid of any error page sent with 401 or 407 authentication required response... - readBody(true); - } - - // no success, close the cache file so the cache state is reset - that way most other code - // doesn't have to deal with the cache being in various states. - cacheFileClose(); - if (m_iError || m_isLoadingErrorPage) { - // Unrecoverable error, abort everything. - // Also, if we've just loaded an error page there is nothing more to do. - // In that case we abort to avoid loops; some webservers manage to send 401 and - // no authentication request. Or an auth request we don't understand. - setMetaData(QLatin1String("responsecode"), QString::number(m_request.responseCode)); - return false; - } - - if (!m_request.isKeepAlive) { - httpCloseConnection(); - m_request.isKeepAlive = true; - m_request.keepAliveTimeout = 0; - } - } - - // Do not save authorization if the current response code is - // 4xx (client error) or 5xx (server error). - kDebug(7113) << "Previous Response:" << m_request.prevResponseCode; - kDebug(7113) << "Current Response:" << m_request.responseCode; - - setMetaData(QLatin1String("responsecode"), QString::number(m_request.responseCode)); - setMetaData(QLatin1String("content-type"), m_mimeType); - - // At this point sendBody() should have delivered any POST data. - clearPostDataBuffer(); - - return true; -} - -void HTTPProtocol::stat(const KUrl& url) -{ - kDebug(7113) << url; - - if (!maybeSetRequestUrl(url)) - return; - resetSessionSettings(); - - if ( m_protocol != "webdav" && m_protocol != "webdavs" ) - { - QString statSide = metaData(QLatin1String("statSide")); - if (statSide != QLatin1String("source")) - { - // When uploading we assume the file doesn't exit - error( ERR_DOES_NOT_EXIST, url.prettyUrl() ); - return; - } - - // When downloading we assume it exists - UDSEntry entry; - entry.insert( KIO::UDSEntry::UDS_NAME, url.fileName() ); - entry.insert( KIO::UDSEntry::UDS_FILE_TYPE, S_IFREG ); // a file - entry.insert( KIO::UDSEntry::UDS_ACCESS, S_IRUSR | S_IRGRP | S_IROTH ); // readable by everybody - - statEntry( entry ); - finished(); - return; - } - - davStatList( url ); -} - -void HTTPProtocol::listDir( const KUrl& url ) -{ - kDebug(7113) << url; - - if (!maybeSetRequestUrl(url)) - return; - resetSessionSettings(); - - davStatList( url, false ); -} - -void HTTPProtocol::davSetRequest( const QByteArray& requestXML ) -{ - // insert the document into the POST buffer, kill trailing zero byte - cachePostData(requestXML); -} - -void HTTPProtocol::davStatList( const KUrl& url, bool stat ) -{ - UDSEntry entry; - - // check to make sure this host supports WebDAV - if ( !davHostOk() ) - return; - - // Maybe it's a disguised SEARCH... - QString query = metaData(QLatin1String("davSearchQuery")); - if ( !query.isEmpty() ) - { - QByteArray request = "\r\n"; - request.append( "\r\n" ); - request.append( query.toUtf8() ); - request.append( "\r\n" ); - - davSetRequest( request ); - } else { - // We are only after certain features... - QByteArray request; - request = "" - ""; - - // insert additional XML request from the davRequestResponse metadata - if ( hasMetaData(QLatin1String("davRequestResponse")) ) - request += metaData(QLatin1String("davRequestResponse")).toUtf8(); - else { - // No special request, ask for default properties - request += "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - "" - ""; - } - request += ""; - - davSetRequest( request ); - } - - // WebDAV Stat or List... - m_request.method = query.isEmpty() ? DAV_PROPFIND : DAV_SEARCH; - m_request.url.setQuery(QString()); - m_request.cacheTag.policy = CC_Reload; - m_request.davData.depth = stat ? 0 : 1; - if (!stat) - m_request.url.adjustPath(KUrl::AddTrailingSlash); - - proceedUntilResponseContent( true ); - infoMessage(QLatin1String("")); - - // Has a redirection already been called? If so, we're done. - if (m_isRedirection || m_iError) { - if (m_isRedirection) { - davFinished(); - } - return; - } - - QDomDocument multiResponse; - multiResponse.setContent( m_webDavDataBuf, true ); - - bool hasResponse = false; - - // kDebug(7113) << endl << multiResponse.toString(2); - - for ( QDomNode n = multiResponse.documentElement().firstChild(); - !n.isNull(); n = n.nextSibling()) { - QDomElement thisResponse = n.toElement(); - if (thisResponse.isNull()) - continue; - - hasResponse = true; - - QDomElement href = thisResponse.namedItem(QLatin1String("href")).toElement(); - if ( !href.isNull() ) { - entry.clear(); - - KUrl thisURL( QUrl::fromPercentEncoding(href.text().toUtf8()) ); - - if ( thisURL.isValid() ) { - QString name = thisURL.fileName(); - - // base dir of a listDir(): name should be "." - if ( !stat && thisURL.path(KUrl::AddTrailingSlash).length() == url.path(KUrl::AddTrailingSlash).length() ) - name = QLatin1Char('.'); - - entry.insert( KIO::UDSEntry::UDS_NAME, name.isEmpty() ? href.text() : name ); - } - - QDomNodeList propstats = thisResponse.elementsByTagName(QLatin1String("propstat")); - - davParsePropstats( propstats, entry ); - - updateUDSEntryMimeType(&entry); - - if ( stat ) { - // return an item - statEntry( entry ); - davFinished(); - return; - } - - listEntry( entry, false ); - } else { - kDebug(7113) << "Error: no URL contained in response to PROPFIND on" << url; - } - } - - if ( stat || !hasResponse ) { - error( ERR_DOES_NOT_EXIST, url.prettyUrl() ); - return; - } - - listEntry( entry, true ); - davFinished(); -} - -void HTTPProtocol::davGeneric( const KUrl& url, KIO::HTTP_METHOD method, qint64 size ) -{ - kDebug(7113) << url; - - if (!maybeSetRequestUrl(url)) - return; - resetSessionSettings(); - - // check to make sure this host supports WebDAV - if ( !davHostOk() ) - return; - - // WebDAV method - m_request.method = method; - m_request.url.setQuery(QString()); - m_request.cacheTag.policy = CC_Reload; - - m_iPostDataSize = (size > -1 ? static_cast(size) : NO_SIZE); - proceedUntilResponseContent(); -} - -int HTTPProtocol::codeFromResponse( const QString& response ) -{ - const int firstSpace = response.indexOf( QLatin1Char(' ') ); - const int secondSpace = response.indexOf( QLatin1Char(' '), firstSpace + 1 ); - return response.mid( firstSpace + 1, secondSpace - firstSpace - 1 ).toInt(); -} - -void HTTPProtocol::davParsePropstats( const QDomNodeList& propstats, UDSEntry& entry ) -{ - QString mimeType; - bool foundExecutable = false; - bool isDirectory = false; - uint lockCount = 0; - uint supportedLockCount = 0; - - for ( int i = 0; i < propstats.count(); i++) - { - QDomElement propstat = propstats.item(i).toElement(); - - QDomElement status = propstat.namedItem(QLatin1String("status")).toElement(); - if ( status.isNull() ) - { - // error, no status code in this propstat - kDebug(7113) << "Error, no status code in this propstat"; - return; - } - - int code = codeFromResponse( status.text() ); - - if ( code != 200 ) - { - kDebug(7113) << "Got status code" << code << "(this may mean that some properties are unavailable)"; - continue; - } - - QDomElement prop = propstat.namedItem( QLatin1String("prop") ).toElement(); - if ( prop.isNull() ) - { - kDebug(7113) << "Error: no prop segment in this propstat."; - return; - } - - if ( hasMetaData( QLatin1String("davRequestResponse") ) ) - { - QDomDocument doc; - doc.appendChild(prop); - entry.insert( KIO::UDSEntry::UDS_XML_PROPERTIES, doc.toString() ); - } - - for ( QDomNode n = prop.firstChild(); !n.isNull(); n = n.nextSibling() ) - { - QDomElement property = n.toElement(); - if (property.isNull()) - continue; - - if ( property.namespaceURI() != QLatin1String("DAV:") ) - { - // break out - we're only interested in properties from the DAV namespace - continue; - } - - if ( property.tagName() == QLatin1String("creationdate") ) - { - // Resource creation date. Should be is ISO 8601 format. - entry.insert( KIO::UDSEntry::UDS_CREATION_TIME, parseDateTime( property.text(), property.attribute(QLatin1String("dt")) ) ); - } - else if ( property.tagName() == QLatin1String("getcontentlength") ) - { - // Content length (file size) - entry.insert( KIO::UDSEntry::UDS_SIZE, property.text().toULong() ); - } - else if ( property.tagName() == QLatin1String("displayname") ) - { - // Name suitable for presentation to the user - setMetaData( QLatin1String("davDisplayName"), property.text() ); - } - else if ( property.tagName() == QLatin1String("source") ) - { - // Source template location - QDomElement source = property.namedItem( QLatin1String("link") ).toElement() - .namedItem( QLatin1String("dst") ).toElement(); - if ( !source.isNull() ) - setMetaData( QLatin1String("davSource"), source.text() ); - } - else if ( property.tagName() == QLatin1String("getcontentlanguage") ) - { - // equiv. to Content-Language header on a GET - setMetaData( QLatin1String("davContentLanguage"), property.text() ); - } - else if ( property.tagName() == QLatin1String("getcontenttype") ) - { - // Content type (mime type) - // This may require adjustments for other server-side webdav implementations - // (tested with Apache + mod_dav 1.0.3) - if ( property.text() == QLatin1String("httpd/unix-directory") ) - { - isDirectory = true; - } - else - { - mimeType = property.text(); - } - } - else if ( property.tagName() == QLatin1String("executable") ) - { - // File executable status - if ( property.text() == QLatin1String("T") ) - foundExecutable = true; - - } - else if ( property.tagName() == QLatin1String("getlastmodified") ) - { - // Last modification date - entry.insert( KIO::UDSEntry::UDS_MODIFICATION_TIME, parseDateTime( property.text(), property.attribute(QLatin1String("dt")) ) ); - } - else if ( property.tagName() == QLatin1String("getetag") ) - { - // Entity tag - setMetaData( QLatin1String("davEntityTag"), property.text() ); - } - else if ( property.tagName() == QLatin1String("supportedlock") ) - { - // Supported locking specifications - for ( QDomNode n2 = property.firstChild(); !n2.isNull(); n2 = n2.nextSibling() ) - { - QDomElement lockEntry = n2.toElement(); - if ( lockEntry.tagName() == QLatin1String("lockentry") ) - { - QDomElement lockScope = lockEntry.namedItem( QLatin1String("lockscope") ).toElement(); - QDomElement lockType = lockEntry.namedItem( QLatin1String("locktype") ).toElement(); - if ( !lockScope.isNull() && !lockType.isNull() ) - { - // Lock type was properly specified - supportedLockCount++; - const QString lockCountStr = QString::number(supportedLockCount); - const QString scope = lockScope.firstChild().toElement().tagName(); - const QString type = lockType.firstChild().toElement().tagName(); - - setMetaData( QLatin1String("davSupportedLockScope") + lockCountStr, scope ); - setMetaData( QLatin1String("davSupportedLockType") + lockCountStr, type ); - } - } - } - } - else if ( property.tagName() == QLatin1String("lockdiscovery") ) - { - // Lists the available locks - davParseActiveLocks( property.elementsByTagName( QLatin1String("activelock") ), lockCount ); - } - else if ( property.tagName() == QLatin1String("resourcetype") ) - { - // Resource type. "Specifies the nature of the resource." - if ( !property.namedItem( QLatin1String("collection") ).toElement().isNull() ) - { - // This is a collection (directory) - isDirectory = true; - } - } - else - { - kDebug(7113) << "Found unknown webdav property:" << property.tagName(); - } - } - } - - setMetaData( QLatin1String("davLockCount"), QString::number(lockCount) ); - setMetaData( QLatin1String("davSupportedLockCount"), QString::number(supportedLockCount) ); - - entry.insert( KIO::UDSEntry::UDS_FILE_TYPE, isDirectory ? S_IFDIR : S_IFREG ); - - if ( foundExecutable || isDirectory ) - { - // File was executable, or is a directory. - entry.insert( KIO::UDSEntry::UDS_ACCESS, 0700 ); - } - else - { - entry.insert( KIO::UDSEntry::UDS_ACCESS, 0600 ); - } - - if ( !isDirectory && !mimeType.isEmpty() ) - { - entry.insert( KIO::UDSEntry::UDS_MIME_TYPE, mimeType ); - } -} - -void HTTPProtocol::davParseActiveLocks( const QDomNodeList& activeLocks, - uint& lockCount ) -{ - for ( int i = 0; i < activeLocks.count(); i++ ) - { - const QDomElement activeLock = activeLocks.item(i).toElement(); - - lockCount++; - // required - const QDomElement lockScope = activeLock.namedItem( QLatin1String("lockscope") ).toElement(); - const QDomElement lockType = activeLock.namedItem( QLatin1String("locktype") ).toElement(); - const QDomElement lockDepth = activeLock.namedItem( QLatin1String("depth") ).toElement(); - // optional - const QDomElement lockOwner = activeLock.namedItem( QLatin1String("owner") ).toElement(); - const QDomElement lockTimeout = activeLock.namedItem( QLatin1String("timeout") ).toElement(); - const QDomElement lockToken = activeLock.namedItem( QLatin1String("locktoken") ).toElement(); - - if ( !lockScope.isNull() && !lockType.isNull() && !lockDepth.isNull() ) - { - // lock was properly specified - lockCount++; - const QString lockCountStr = QString::number(lockCount); - const QString scope = lockScope.firstChild().toElement().tagName(); - const QString type = lockType.firstChild().toElement().tagName(); - const QString depth = lockDepth.text(); - - setMetaData( QLatin1String("davLockScope") + lockCountStr, scope ); - setMetaData( QLatin1String("davLockType") + lockCountStr, type ); - setMetaData( QLatin1String("davLockDepth") + lockCountStr, depth ); - - if ( !lockOwner.isNull() ) - setMetaData( QLatin1String("davLockOwner") + lockCountStr, lockOwner.text() ); - - if ( !lockTimeout.isNull() ) - setMetaData( QLatin1String("davLockTimeout") + lockCountStr, lockTimeout.text() ); - - if ( !lockToken.isNull() ) - { - QDomElement tokenVal = lockScope.namedItem( QLatin1String("href") ).toElement(); - if ( !tokenVal.isNull() ) - setMetaData( QLatin1String("davLockToken") + lockCountStr, tokenVal.text() ); - } - } - } -} - -QString HTTPProtocol::davProcessLocks() -{ - if ( hasMetaData( QLatin1String("davLockCount") ) ) - { - QString response = QLatin1String("If:"); - int numLocks = metaData( QLatin1String("davLockCount") ).toInt(); - bool bracketsOpen = false; - for ( int i = 0; i < numLocks; i++ ) - { - const QString countStr = QString::number(i); - if ( hasMetaData( QLatin1String("davLockToken") + countStr ) ) - { - if ( hasMetaData( QLatin1String("davLockURL") + countStr ) ) - { - if ( bracketsOpen ) - { - response += QLatin1Char(')'); - bracketsOpen = false; - } - response += QLatin1String(" <") + metaData( QLatin1String("davLockURL") + countStr ) + QLatin1Char('>'); - } - - if ( !bracketsOpen ) - { - response += QLatin1String(" ("); - bracketsOpen = true; - } - else - { - response += QLatin1Char(' '); - } - - if ( hasMetaData( QLatin1String("davLockNot") + countStr ) ) - response += QLatin1String("Not "); - - response += QLatin1Char('<') + metaData( QLatin1String("davLockToken") + countStr ) + QLatin1Char('>'); - } - } - - if ( bracketsOpen ) - response += QLatin1Char(')'); - - response += QLatin1String("\r\n"); - return response; - } - - return QString(); -} - -bool HTTPProtocol::davHostOk() -{ - // FIXME needs to be reworked. Switched off for now. - return true; - - // cached? - if ( m_davHostOk ) - { - kDebug(7113) << "true"; - return true; - } - else if ( m_davHostUnsupported ) - { - kDebug(7113) << " false"; - davError( -2 ); - return false; - } - - m_request.method = HTTP_OPTIONS; - - // query the server's capabilities generally, not for a specific URL - m_request.url.setPath(QLatin1String("*")); - m_request.url.setQuery(QString()); - m_request.cacheTag.policy = CC_Reload; - - // clear davVersions variable, which holds the response to the DAV: header - m_davCapabilities.clear(); - - proceedUntilResponseHeader(); - - if (m_davCapabilities.count()) - { - for (int i = 0; i < m_davCapabilities.count(); i++) - { - bool ok; - uint verNo = m_davCapabilities[i].toUInt(&ok); - if (ok && verNo > 0 && verNo < 3) - { - m_davHostOk = true; - kDebug(7113) << "Server supports DAV version" << verNo; - } - } - - if ( m_davHostOk ) - return true; - } - - m_davHostUnsupported = true; - davError( -2 ); - return false; -} - -// This function is for closing proceedUntilResponseHeader(); requests -// Required because there may or may not be further info expected -void HTTPProtocol::davFinished() -{ - // TODO: Check with the DAV extension developers - httpClose(m_request.isKeepAlive); - finished(); -} - -void HTTPProtocol::mkdir( const KUrl& url, int ) -{ - kDebug(7113) << url; - - if (!maybeSetRequestUrl(url)) - return; - resetSessionSettings(); - - m_request.method = DAV_MKCOL; - m_request.url.setQuery(QString()); - m_request.cacheTag.policy = CC_Reload; - - proceedUntilResponseContent(true); - - if ( m_request.responseCode == 201 ) - davFinished(); - else - davError(); -} - -void HTTPProtocol::get( const KUrl& url ) -{ - kDebug(7113) << url; - - if (!maybeSetRequestUrl(url)) - return; - resetSessionSettings(); - - m_request.method = HTTP_GET; - const QString tmp (metaData(QLatin1String("cache"))); - m_request.cacheTag.policy = (tmp.isEmpty() ? DEFAULT_CACHE_CONTROL : parseCacheControl(tmp)); - - proceedUntilResponseContent(); -} - -void HTTPProtocol::put( const KUrl &url, int, KIO::JobFlags flags ) -{ - kDebug(7113) << url; - - if (!maybeSetRequestUrl(url)) - return; - - resetSessionSettings(); - - // Webdav hosts are capable of observing overwrite == false - if (m_protocol.startsWith("webdav")) { // krazy:exclude=strings - if (!(flags & KIO::Overwrite)) { - // check to make sure this host supports WebDAV - if (!davHostOk()) - return; - - // Checks if the destination exists and return an error if it does. - if (!davStatDestination()) { - return; - } - } - } - - m_request.method = HTTP_PUT; - m_request.cacheTag.policy = CC_Reload; - - proceedUntilResponseContent(); -} - -void HTTPProtocol::copy( const KUrl& src, const KUrl& dest, int, KIO::JobFlags flags ) -{ - kDebug(7113) << src << "->" << dest; - - const bool isSourceLocal = src.isLocalFile(); - const bool isDestinationLocal = dest.isLocalFile(); - - if (isSourceLocal && !isDestinationLocal) { - copyPut(src, dest, flags); - } else { - if (!maybeSetRequestUrl(dest) || !maybeSetRequestUrl(src)) { - return; - } - - resetSessionSettings(); - - // destination has to be "http(s)://..." - KUrl newDest (dest); - changeProtocolToHttp(&newDest); - - m_request.method = DAV_COPY; - m_request.davData.desturl = newDest.url(); - m_request.davData.overwrite = (flags & KIO::Overwrite); - m_request.url.setQuery(QString()); - m_request.cacheTag.policy = CC_Reload; - - proceedUntilResponseHeader(); - - // The server returns a HTTP/1.1 201 Created or 204 No Content on successful completion - if (m_request.responseCode == 201 || m_request.responseCode == 204) { - davFinished(); - } else { - davError(); - } - } -} - -void HTTPProtocol::rename( const KUrl& src, const KUrl& dest, KIO::JobFlags flags ) -{ - kDebug(7113) << src << "->" << dest; - - if (!maybeSetRequestUrl(dest) || !maybeSetRequestUrl(src)) - return; - resetSessionSettings(); - - // destination has to be "http://..." - KUrl newDest(dest); - changeProtocolToHttp(&newDest); - - m_request.method = DAV_MOVE; - m_request.davData.desturl = newDest.url(); - m_request.davData.overwrite = (flags & KIO::Overwrite); - m_request.url.setQuery(QString()); - m_request.cacheTag.policy = CC_Reload; - - proceedUntilResponseHeader(); - - // Work around strict Apache-2 WebDAV implementation which refuses to cooperate - // with webdav://host/directory, instead requiring webdav://host/directory/ - // (strangely enough it accepts Destination: without a trailing slash) - // See BR# 209508 and BR#187970 - if ( m_request.responseCode == 301) { - m_request.url = m_request.redirectUrl; - m_request.method = DAV_MOVE; - m_request.davData.desturl = newDest.url(); - m_request.davData.overwrite = (flags & KIO::Overwrite); - m_request.url.setQuery(QString()); - m_request.cacheTag.policy = CC_Reload; - // force re-authentication... - delete m_wwwAuth; - m_wwwAuth = 0; - proceedUntilResponseHeader(); - } - - if ( m_request.responseCode == 201 ) - davFinished(); - else - davError(); -} - -void HTTPProtocol::del(const KUrl& url, bool) -{ - kDebug(7113) << url; - - if (!maybeSetRequestUrl(url)) - return; - - resetSessionSettings(); - - m_request.method = HTTP_DELETE; - m_request.cacheTag.policy = CC_Reload; - - if (m_protocol.startsWith("webdav")) { //krazy:exclude=strings due to QByteArray - m_request.url.setQuery(QString()); - if (!proceedUntilResponseHeader()) { - return; - } - - // The server returns a HTTP/1.1 200 Ok or HTTP/1.1 204 No Content - // on successful completion. - if ( m_request.responseCode == 200 || m_request.responseCode == 204 || m_isRedirection) - davFinished(); - else - davError(); - - return; - } - - proceedUntilResponseContent(); -} - -void HTTPProtocol::post( const KUrl& url, qint64 size ) -{ - kDebug(7113) << url; - - if (!maybeSetRequestUrl(url)) - return; - resetSessionSettings(); - - m_request.method = HTTP_POST; - m_request.cacheTag.policy= CC_Reload; - - m_iPostDataSize = (size > -1 ? static_cast(size) : NO_SIZE); - proceedUntilResponseContent(); -} - -void HTTPProtocol::davLock( const KUrl& url, const QString& scope, - const QString& type, const QString& owner ) -{ - kDebug(7113) << url; - - if (!maybeSetRequestUrl(url)) - return; - resetSessionSettings(); - - m_request.method = DAV_LOCK; - m_request.url.setQuery(QString()); - m_request.cacheTag.policy= CC_Reload; - - /* Create appropriate lock XML request. */ - QDomDocument lockReq; - - QDomElement lockInfo = lockReq.createElementNS( QLatin1String("DAV:"), QLatin1String("lockinfo") ); - lockReq.appendChild( lockInfo ); - - QDomElement lockScope = lockReq.createElement( QLatin1String("lockscope") ); - lockInfo.appendChild( lockScope ); - - lockScope.appendChild( lockReq.createElement( scope ) ); - - QDomElement lockType = lockReq.createElement( QLatin1String("locktype") ); - lockInfo.appendChild( lockType ); - - lockType.appendChild( lockReq.createElement( type ) ); - - if ( !owner.isNull() ) { - QDomElement ownerElement = lockReq.createElement( QLatin1String("owner") ); - lockReq.appendChild( ownerElement ); - - QDomElement ownerHref = lockReq.createElement( QLatin1String("href") ); - ownerElement.appendChild( ownerHref ); - - ownerHref.appendChild( lockReq.createTextNode( owner ) ); - } - - // insert the document into the POST buffer - cachePostData(lockReq.toByteArray()); - - proceedUntilResponseContent( true ); - - if ( m_request.responseCode == 200 ) { - // success - QDomDocument multiResponse; - multiResponse.setContent( m_webDavDataBuf, true ); - - QDomElement prop = multiResponse.documentElement().namedItem( QLatin1String("prop") ).toElement(); - - QDomElement lockdiscovery = prop.namedItem( QLatin1String("lockdiscovery") ).toElement(); - - uint lockCount = 0; - davParseActiveLocks( lockdiscovery.elementsByTagName( QLatin1String("activelock") ), lockCount ); - - setMetaData( QLatin1String("davLockCount"), QString::number( lockCount ) ); - - finished(); - - } else - davError(); -} - -void HTTPProtocol::davUnlock( const KUrl& url ) -{ - kDebug(7113) << url; - - if (!maybeSetRequestUrl(url)) - return; - resetSessionSettings(); - - m_request.method = DAV_UNLOCK; - m_request.url.setQuery(QString()); - m_request.cacheTag.policy= CC_Reload; - - proceedUntilResponseContent( true ); - - if ( m_request.responseCode == 200 ) - finished(); - else - davError(); -} - -QString HTTPProtocol::davError( int code /* = -1 */, const QString &_url ) -{ - bool callError = false; - if ( code == -1 ) { - code = m_request.responseCode; - callError = true; - } - if ( code == -2 ) { - callError = true; - } - - QString url = _url; - if ( !url.isNull() ) - url = m_request.url.prettyUrl(); - - QString action, errorString; - int errorCode = ERR_SLAVE_DEFINED; - - // for 412 Precondition Failed - QString ow = i18n( "Otherwise, the request would have succeeded." ); - - switch ( m_request.method ) { - case DAV_PROPFIND: - action = i18nc( "request type", "retrieve property values" ); - break; - case DAV_PROPPATCH: - action = i18nc( "request type", "set property values" ); - break; - case DAV_MKCOL: - action = i18nc( "request type", "create the requested folder" ); - break; - case DAV_COPY: - action = i18nc( "request type", "copy the specified file or folder" ); - break; - case DAV_MOVE: - action = i18nc( "request type", "move the specified file or folder" ); - break; - case DAV_SEARCH: - action = i18nc( "request type", "search in the specified folder" ); - break; - case DAV_LOCK: - action = i18nc( "request type", "lock the specified file or folder" ); - break; - case DAV_UNLOCK: - action = i18nc( "request type", "unlock the specified file or folder" ); - break; - case HTTP_DELETE: - action = i18nc( "request type", "delete the specified file or folder" ); - break; - case HTTP_OPTIONS: - action = i18nc( "request type", "query the server's capabilities" ); - break; - case HTTP_GET: - action = i18nc( "request type", "retrieve the contents of the specified file or folder" ); - break; - case DAV_REPORT: - action = i18nc( "request type", "run a report in the specified folder" ); - break; - case HTTP_PUT: - case HTTP_POST: - case HTTP_HEAD: - default: - // this should not happen, this function is for webdav errors only - Q_ASSERT(0); - } - - // default error message if the following code fails - errorString = i18nc("%1: code, %2: request type", "An unexpected error (%1) occurred " - "while attempting to %2.", code, action); - - switch ( code ) - { - case -2: - // internal error: OPTIONS request did not specify DAV compliance - // ERR_UNSUPPORTED_PROTOCOL - errorString = i18n("The server does not support the WebDAV protocol."); - break; - case 207: - // 207 Multi-status - { - // our error info is in the returned XML document. - // retrieve the XML document - - // there was an error retrieving the XML document. - // ironic, eh? - if ( !readBody( true ) && m_iError ) - return QString(); - - QStringList errors; - QDomDocument multiResponse; - - multiResponse.setContent( m_webDavDataBuf, true ); - - QDomElement multistatus = multiResponse.documentElement().namedItem( QLatin1String("multistatus") ).toElement(); - - QDomNodeList responses = multistatus.elementsByTagName( QLatin1String("response") ); - - for (int i = 0; i < responses.count(); i++) - { - int errCode; - QString errUrl; - - QDomElement response = responses.item(i).toElement(); - QDomElement code = response.namedItem( QLatin1String("status") ).toElement(); - - if ( !code.isNull() ) - { - errCode = codeFromResponse( code.text() ); - QDomElement href = response.namedItem( QLatin1String("href") ).toElement(); - if ( !href.isNull() ) - errUrl = href.text(); - errors << davError( errCode, errUrl ); - } - } - - //kError = ERR_SLAVE_DEFINED; - errorString = i18nc( "%1: request type, %2: url", - "An error occurred while attempting to %1, %2. A " - "summary of the reasons is below.", action, url ); - - errorString += QLatin1String("
    "); - - Q_FOREACH(const QString& error, errors) - errorString += QLatin1String("
  • ") + error + QLatin1String("
  • "); - - errorString += QLatin1String("
"); - } - case 403: - case 500: // hack: Apache mod_dav returns this instead of 403 (!) - // 403 Forbidden - // ERR_ACCESS_DENIED - errorString = i18nc( "%1: request type", "Access was denied while attempting to %1.", action ); - break; - case 405: - // 405 Method Not Allowed - if ( m_request.method == DAV_MKCOL ) { - // ERR_DIR_ALREADY_EXIST - errorString = url; - errorCode = ERR_DIR_ALREADY_EXIST; - } - break; - case 409: - // 409 Conflict - // ERR_ACCESS_DENIED - errorString = i18n("A resource cannot be created at the destination " - "until one or more intermediate collections (folders) " - "have been created."); - break; - case 412: - // 412 Precondition failed - if ( m_request.method == DAV_COPY || m_request.method == DAV_MOVE ) { - // ERR_ACCESS_DENIED - errorString = i18n("The server was unable to maintain the liveness of " - "the properties listed in the propertybehavior XML " - "element or you attempted to overwrite a file while " - "requesting that files are not overwritten. %1", - ow ); - - } else if ( m_request.method == DAV_LOCK ) { - // ERR_ACCESS_DENIED - errorString = i18n("The requested lock could not be granted. %1", ow ); - } - break; - case 415: - // 415 Unsupported Media Type - // ERR_ACCESS_DENIED - errorString = i18n("The server does not support the request type of the body."); - break; - case 423: - // 423 Locked - // ERR_ACCESS_DENIED - errorString = i18nc( "%1: request type", "Unable to %1 because the resource is locked.", action ); - break; - case 425: - // 424 Failed Dependency - errorString = i18n("This action was prevented by another error."); - break; - case 502: - // 502 Bad Gateway - if ( m_request.method == DAV_COPY || m_request.method == DAV_MOVE ) { - // ERR_WRITE_ACCESS_DENIED - errorString = i18nc( "%1: request type", "Unable to %1 because the destination server refuses " - "to accept the file or folder.", action ); - } - break; - case 507: - // 507 Insufficient Storage - // ERR_DISK_FULL - errorString = i18n("The destination resource does not have sufficient space " - "to record the state of the resource after the execution " - "of this method."); - break; - default: - break; - } - - // if ( kError != ERR_SLAVE_DEFINED ) - //errorString += " (" + url + ')'; - - if ( callError ) - error( errorCode, errorString ); - - return errorString; -} - -// HTTP generic error -static int httpGenericError(const HTTPProtocol::HTTPRequest& request, QString* errorString) -{ - Q_ASSERT(errorString); - - int errorCode = 0; - errorString->clear(); - - if (request.responseCode == 204) { - errorCode = ERR_NO_CONTENT; - } - - return errorCode; -} - -// HTTP DELETE specific errors -static int httpDelError(const HTTPProtocol::HTTPRequest& request, QString* errorString) -{ - Q_ASSERT(errorString); - - int errorCode = 0; - const int responseCode = request.responseCode; - errorString->clear(); - - switch (responseCode) { - case 204: - errorCode = ERR_NO_CONTENT; - break; - default: - break; - } - - if (!errorCode - && (responseCode < 200 || responseCode > 400) - && responseCode != 404) { - errorCode = ERR_SLAVE_DEFINED; - *errorString = i18n( "The resource cannot be deleted." ); - } - - return errorCode; -} - -// HTTP PUT specific errors -static int httpPutError(const HTTPProtocol::HTTPRequest& request, QString* errorString) -{ - Q_ASSERT(errorString); - - int errorCode = 0; - const int responseCode = request.responseCode; - const QString action (i18nc("request type", "upload %1", request.url.prettyUrl())); - - switch (responseCode) { - case 403: - case 405: - case 500: // hack: Apache mod_dav returns this instead of 403 (!) - // 403 Forbidden - // 405 Method Not Allowed - // ERR_ACCESS_DENIED - *errorString = i18nc( "%1: request type", "Access was denied while attempting to %1.", action ); - errorCode = ERR_SLAVE_DEFINED; - break; - case 409: - // 409 Conflict - // ERR_ACCESS_DENIED - *errorString = i18n("A resource cannot be created at the destination " - "until one or more intermediate collections (folders) " - "have been created."); - errorCode = ERR_SLAVE_DEFINED; - break; - case 423: - // 423 Locked - // ERR_ACCESS_DENIED - *errorString = i18nc( "%1: request type", "Unable to %1 because the resource is locked.", action ); - errorCode = ERR_SLAVE_DEFINED; - break; - case 502: - // 502 Bad Gateway - // ERR_WRITE_ACCESS_DENIED; - *errorString = i18nc( "%1: request type", "Unable to %1 because the destination server refuses " - "to accept the file or folder.", action ); - errorCode = ERR_SLAVE_DEFINED; - break; - case 507: - // 507 Insufficient Storage - // ERR_DISK_FULL - *errorString = i18n("The destination resource does not have sufficient space " - "to record the state of the resource after the execution " - "of this method."); - errorCode = ERR_SLAVE_DEFINED; - break; - default: - break; - } - - if (!errorCode - && (responseCode < 200 || responseCode > 400) - && responseCode != 404) { - errorCode = ERR_SLAVE_DEFINED; - *errorString = i18nc("%1: response code, %2: request type", - "An unexpected error (%1) occurred while attempting to %2.", - responseCode, action); - } - - return errorCode; -} - -bool HTTPProtocol::sendHttpError() -{ - QString errorString; - int errorCode = 0; - - switch (m_request.method) { - case HTTP_GET: - case HTTP_POST: - errorCode = httpGenericError(m_request, &errorString); - break; - case HTTP_PUT: - errorCode = httpPutError(m_request, &errorString); - break; - case HTTP_DELETE: - errorCode = httpDelError(m_request, &errorString); - break; - default: - break; - } - - // Force any message previously shown by the client to be cleared. - infoMessage(QLatin1String("")); - - if (errorCode) { - error( errorCode, errorString ); - return true; - } - - return false; -} - -bool HTTPProtocol::sendErrorPageNotification() -{ - if (!m_request.preferErrorPage) - return false; - - if (m_isLoadingErrorPage) - kWarning(7113) << "called twice during one request, something is probably wrong."; - - m_isLoadingErrorPage = true; - SlaveBase::errorPage(); - return true; -} - -bool HTTPProtocol::isOffline() -{ - // ### TEMPORARY WORKAROUND (While investigating why solid may - // produce false positives) - return false; - - Solid::Networking::Status status = Solid::Networking::status(); - - kDebug(7113) << "networkstatus:" << status; - - // on error or unknown, we assume online - return status == Solid::Networking::Unconnected; -} - -void HTTPProtocol::multiGet(const QByteArray &data) -{ - QDataStream stream(data); - quint32 n; - stream >> n; - - kDebug(7113) << n; - - HTTPRequest saveRequest; - if (m_isBusy) - saveRequest = m_request; - - resetSessionSettings(); - - for (unsigned i = 0; i < n; ++i) { - KUrl url; - stream >> url >> mIncomingMetaData; - - if (!maybeSetRequestUrl(url)) - continue; - - //### should maybe call resetSessionSettings() if the server/domain is - // different from the last request! - - kDebug(7113) << url; - - m_request.method = HTTP_GET; - m_request.isKeepAlive = true; //readResponseHeader clears it if necessary - - QString tmp = metaData(QLatin1String("cache")); - if (!tmp.isEmpty()) - m_request.cacheTag.policy= parseCacheControl(tmp); - else - m_request.cacheTag.policy= DEFAULT_CACHE_CONTROL; - - m_requestQueue.append(m_request); - } - - if (m_isBusy) - m_request = saveRequest; -#if 0 - if (!m_isBusy) { - m_isBusy = true; - QMutableListIterator it(m_requestQueue); - while (it.hasNext()) { - m_request = it.next(); - it.remove(); - proceedUntilResponseContent(); - } - m_isBusy = false; - } -#endif - if (!m_isBusy) { - m_isBusy = true; - QMutableListIterator it(m_requestQueue); - // send the requests - while (it.hasNext()) { - m_request = it.next(); - sendQuery(); - // save the request state so we can pick it up again in the collection phase - it.setValue(m_request); - kDebug(7113) << "check one: isKeepAlive =" << m_request.isKeepAlive; - if (m_request.cacheTag.ioMode != ReadFromCache) { - m_server.initFrom(m_request); - } - } - // collect the responses - //### for the moment we use a hack: instead of saving and restoring request-id - // we just count up like ParallelGetJobs does. - int requestId = 0; - Q_FOREACH (const HTTPRequest &r, m_requestQueue) { - m_request = r; - kDebug(7113) << "check two: isKeepAlive =" << m_request.isKeepAlive; - setMetaData(QLatin1String("request-id"), QString::number(requestId++)); - sendAndKeepMetaData(); - if (!(readResponseHeader() && readBody())) { - return; - } - // the "next job" signal for ParallelGetJob is data of size zero which - // readBody() sends without our intervention. - kDebug(7113) << "check three: isKeepAlive =" << m_request.isKeepAlive; - httpClose(m_request.isKeepAlive); //actually keep-alive is mandatory for pipelining - } - - finished(); - m_requestQueue.clear(); - m_isBusy = false; - } -} - -ssize_t HTTPProtocol::write (const void *_buf, size_t nbytes) -{ - size_t sent = 0; - const char* buf = static_cast(_buf); - while (sent < nbytes) - { - int n = TCPSlaveBase::write(buf + sent, nbytes - sent); - - if (n < 0) { - // some error occurred - return -1; - } - - sent += n; - } - - return sent; -} - -void HTTPProtocol::clearUnreadBuffer() -{ - m_unreadBuf.clear(); -} - -// Note: the implementation of unread/readBuffered assumes that unread will only -// be used when there is extra data we don't want to handle, and not to wait for more data. -void HTTPProtocol::unread(char *buf, size_t size) -{ - // implement LIFO (stack) semantics - const int newSize = m_unreadBuf.size() + size; - m_unreadBuf.resize(newSize); - for (size_t i = 0; i < size; i++) { - m_unreadBuf.data()[newSize - i - 1] = buf[i]; - } - if (size) { - //hey, we still have data, closed connection or not! - m_isEOF = false; - } -} - -size_t HTTPProtocol::readBuffered(char *buf, size_t size, bool unlimited) -{ - size_t bytesRead = 0; - if (!m_unreadBuf.isEmpty()) { - const int bufSize = m_unreadBuf.size(); - bytesRead = qMin((int)size, bufSize); - - for (size_t i = 0; i < bytesRead; i++) { - buf[i] = m_unreadBuf.constData()[bufSize - i - 1]; - } - m_unreadBuf.truncate(bufSize - bytesRead); - - // If we have an unread buffer and the size of the content returned by the - // server is unknown, e.g. chuncked transfer, return the bytes read here since - // we may already have enough data to complete the response and don't want to - // wait for more. See BR# 180631. - if (unlimited) - return bytesRead; - } - if (bytesRead < size) { - int rawRead = TCPSlaveBase::read(buf + bytesRead, size - bytesRead); - if (rawRead < 1) { - m_isEOF = true; - return bytesRead; - } - bytesRead += rawRead; - } - return bytesRead; -} - -//### this method will detect an n*(\r\n) sequence if it crosses invocations. -// it will look (n*2 - 1) bytes before start at most and never before buf, naturally. -// supported number of newlines are one and two, in line with HTTP syntax. -// return true if numNewlines newlines were found. -bool HTTPProtocol::readDelimitedText(char *buf, int *idx, int end, int numNewlines) -{ - Q_ASSERT(numNewlines >=1 && numNewlines <= 2); - char mybuf[64]; //somewhere close to the usual line length to avoid unread()ing too much - int pos = *idx; - while (pos < end && !m_isEOF) { - int step = qMin((int)sizeof(mybuf), end - pos); - if (m_isChunked) { - //we might be reading the end of the very last chunk after which there is no data. - //don't try to read any more bytes than there are because it causes stalls - //(yes, it shouldn't stall but it does) - step = 1; - } - size_t bufferFill = readBuffered(mybuf, step); - - for (size_t i = 0; i < bufferFill ; ++i, ++pos) { - // we copy the data from mybuf to buf immediately and look for the newlines in buf. - // that way we don't miss newlines split over several invocations of this method. - buf[pos] = mybuf[i]; - - // did we just copy one or two times the (usually) \r\n delimiter? - // until we find even more broken webservers in the wild let's assume that they either - // send \r\n (RFC compliant) or \n (broken) as delimiter... - if (buf[pos] == '\n') { - bool found = numNewlines == 1; - if (!found) { // looking for two newlines - // Detect \n\n and \n\r\n. The other cases (\r\n\n, \r\n\r\n) are covered by the first two. - found = ((pos >= 1 && buf[pos - 1] == '\n') || - (pos >= 2 && buf[pos - 2] == '\n' && buf[pos - 1] == '\r')); - } - if (found) { - i++; // unread bytes *after* CRLF - unread(&mybuf[i], bufferFill - i); - *idx = pos + 1; - return true; - } - } - } - } - *idx = pos; - return false; -} - -static bool isCompatibleNextUrl(const KUrl &previous, const KUrl &now) -{ - if (previous.host() != now.host() || previous.port() != now.port()) { - return false; - } - if (previous.user().isEmpty() && previous.pass().isEmpty()) { - return true; - } - return previous.user() == now.user() && previous.pass() == now.pass(); -} - -bool HTTPProtocol::httpShouldCloseConnection() -{ - kDebug(7113); - - if (!isConnected()) { - return false; - } - - if (!m_request.proxyUrls.isEmpty() && !isAutoSsl()) { - Q_FOREACH(const QString& url, m_request.proxyUrls) { - if (url != QLatin1String("DIRECT")) { - if (isCompatibleNextUrl(m_server.proxyUrl, KUrl(url))) { - return false; - } - } - } - return true; - } - - return !isCompatibleNextUrl(m_server.url, m_request.url); -} - -bool HTTPProtocol::httpOpenConnection() -{ - kDebug(7113); - m_server.clear(); - - // Only save proxy auth information after proxy authentication has - // actually taken place, which will set up exactly this connection. - disconnect(socket(), SIGNAL(connected()), - this, SLOT(saveProxyAuthenticationForSocket())); - - clearUnreadBuffer(); - - int connectError = 0; - QString errorString; - - // Get proxy information... - if (m_request.proxyUrls.isEmpty()) { - m_request.proxyUrls = config()->readEntry("ProxyUrls", QStringList()); - kDebug(7113) << "Proxy URLs:" << m_request.proxyUrls; - } - - if (m_request.proxyUrls.isEmpty()) { - QNetworkProxy::setApplicationProxy(QNetworkProxy::NoProxy); - connectError = connectToHost(m_request.url.host(), m_request.url.port(defaultPort()), &errorString); - } else { - KUrl::List badProxyUrls; - Q_FOREACH(const QString& proxyUrl, m_request.proxyUrls) { - if (proxyUrl == QLatin1String("DIRECT")) { - QNetworkProxy::setApplicationProxy(QNetworkProxy::NoProxy); - connectError = connectToHost(m_request.url.host(), m_request.url.port(defaultPort()), &errorString); - if (connectError == 0) { - kDebug(7113) << "Connected DIRECT: host=" << m_request.url.host() << "port=" << m_request.url.port(defaultPort()); - break; - } else { - continue; - } - } - - const KUrl url(proxyUrl); - const QString proxyScheme(url.protocol()); - if (!supportedProxyScheme(proxyScheme)) { - connectError = ERR_COULD_NOT_CONNECT; - errorString = url.url(); - badProxyUrls << url; - continue; - } - - QNetworkProxy::ProxyType proxyType = QNetworkProxy::NoProxy; - if (proxyScheme == QLatin1String("socks")) { - proxyType = QNetworkProxy::Socks5Proxy; - } else if (isAutoSsl()) { - proxyType = QNetworkProxy::HttpProxy; - } - - kDebug(7113) << "Connecting to proxy: address=" << proxyUrl << "type=" << proxyType; - - if (proxyType == QNetworkProxy::NoProxy) { - QNetworkProxy::setApplicationProxy(QNetworkProxy::NoProxy); - connectError = connectToHost(url.host(), url.port(), &errorString); - if (connectError == 0) { - m_request.proxyUrl = url; - kDebug(7113) << "Connected to proxy: host=" << url.host() << "port=" << url.port(); - break; - } else { - if (connectError == ERR_UNKNOWN_HOST) { - connectError = ERR_UNKNOWN_PROXY_HOST; - } - kDebug(7113) << "Failed to connect to proxy:" << proxyUrl; - badProxyUrls << url; - } - } else { - QNetworkProxy proxy(proxyType, url.host(), url.port(), url.user(), url.pass()); - QNetworkProxy::setApplicationProxy(proxy); - connectError = connectToHost(m_request.url.host(), m_request.url.port(defaultPort()), &errorString); - if (connectError == 0) { - kDebug(7113) << "Tunneling thru proxy: host=" << url.host() << "port=" << url.port(); - break; - } else { - if (connectError == ERR_UNKNOWN_HOST) { - connectError = ERR_UNKNOWN_PROXY_HOST; - } - kDebug(7113) << "Failed to connect to proxy:" << proxyUrl; - badProxyUrls << url; - QNetworkProxy::setApplicationProxy(QNetworkProxy::NoProxy); - } - } - } - - if (!badProxyUrls.isEmpty()) { - //TODO: Notify the client of BAD proxy addresses (needed for PAC setups). - } - } - - if (connectError != 0) { - error(connectError, errorString); - return false; - } - - // Disable Nagle's algorithm, i.e turn on TCP_NODELAY. - QSslSocket *sock = qobject_cast(socket()); - if (sock) { - // kDebug(7113) << "TCP_NODELAY:" << sock->socketOption(QAbstractSocket::LowDelayOption); - sock->setSocketOption(QAbstractSocket::LowDelayOption, 1); - } - - m_server.initFrom(m_request); - connected(); - return true; -} - -bool HTTPProtocol::satisfyRequestFromCache(bool *cacheHasPage) -{ - kDebug(7113); - - if (m_request.cacheTag.useCache) { - const bool offline = isOffline(); - - if (offline && m_request.cacheTag.policy != KIO::CC_Reload) { - m_request.cacheTag.policy= KIO::CC_CacheOnly; - } - - const bool isCacheOnly = m_request.cacheTag.policy == KIO::CC_CacheOnly; - const CacheTag::CachePlan plan = m_request.cacheTag.plan(m_maxCacheAge); - - bool openForReading = false; - if (plan == CacheTag::UseCached || plan == CacheTag::ValidateCached) { - openForReading = cacheFileOpenRead(); - - if (!openForReading && (isCacheOnly || offline)) { - // cache-only or offline -> we give a definite answer and it is "no" - *cacheHasPage = false; - if (isCacheOnly) { - error(ERR_DOES_NOT_EXIST, m_request.url.url()); - } else if (offline) { - error(ERR_COULD_NOT_CONNECT, m_request.url.url()); - } - return true; - } - } - - if (openForReading) { - m_request.cacheTag.ioMode = ReadFromCache; - *cacheHasPage = true; - // return false if validation is required, so a network request will be sent - return m_request.cacheTag.plan(m_maxCacheAge) == CacheTag::UseCached; - } - } - *cacheHasPage = false; - return false; -} - -QString HTTPProtocol::formatRequestUri() const -{ - // Only specify protocol, host and port when they are not already clear, i.e. when - // we handle HTTP proxying ourself and the proxy server needs to know them. - // Sending protocol/host/port in other cases confuses some servers, and it's not their fault. - if (isHttpProxy(m_request.proxyUrl) && !isAutoSsl()) { - KUrl u; - - QString protocol = m_request.url.protocol(); - if (protocol.startsWith(QLatin1String("webdav"))) { - protocol.replace(0, qstrlen("webdav"), QLatin1String("http")); - } - u.setProtocol(protocol); - - u.setHost(m_request.url.host()); - // if the URL contained the default port it should have been stripped earlier - Q_ASSERT(m_request.url.port() != defaultPort()); - u.setPort(m_request.url.port()); - u.setEncodedPathAndQuery(m_request.url.encodedPathAndQuery( - KUrl::LeaveTrailingSlash, KUrl::AvoidEmptyPath)); - return u.url(); - } else { - return m_request.url.encodedPathAndQuery(KUrl::LeaveTrailingSlash, KUrl::AvoidEmptyPath); - } -} - -/** - * This function is responsible for opening up the connection to the remote - * HTTP server and sending the header. If this requires special - * authentication or other such fun stuff, then it will handle it. This - * function will NOT receive anything from the server, however. This is in - * contrast to previous incarnations of 'httpOpen' as this method used to be - * called. - * - * The basic process now is this: - * - * 1) Open up the socket and port - * 2) Format our request/header - * 3) Send the header to the remote server - * 4) Call sendBody() if the HTTP method requires sending body data - */ -bool HTTPProtocol::sendQuery() -{ - kDebug(7113); - - // Cannot have an https request without autoSsl! This can - // only happen if the current installation does not support SSL... - if (isEncryptedHttpVariety(m_protocol) && !isAutoSsl()) { - error(ERR_UNSUPPORTED_PROTOCOL, toQString(m_protocol)); - return false; - } - - // Check the reusability of the current connection. - if (httpShouldCloseConnection()) { - httpCloseConnection(); - } - - // Create a new connection to the remote machine if we do - // not already have one... - // NB: the !m_socketProxyAuth condition is a workaround for a proxied Qt socket sometimes - // looking disconnected after receiving the initial 407 response. - // I guess the Qt socket fails to hide the effect of proxy-connection: close after receiving - // the 407 header. - if ((!isConnected() && !m_socketProxyAuth)) - { - if (!httpOpenConnection()) - { - kDebug(7113) << "Couldn't connect, oopsie!"; - return false; - } - } - - m_request.cacheTag.ioMode = NoCache; - m_request.cacheTag.servedDate = -1; - m_request.cacheTag.lastModifiedDate = -1; - m_request.cacheTag.expireDate = -1; - - QString header; - - bool hasBodyData = false; - bool hasDavData = false; - - { - m_request.sentMethodString = m_request.methodString(); - header = toQString(m_request.sentMethodString) + QLatin1Char(' '); - - QString davHeader; - - // Fill in some values depending on the HTTP method to guide further processing - switch (m_request.method) - { - case HTTP_GET: { - bool cacheHasPage = false; - if (satisfyRequestFromCache(&cacheHasPage)) { - kDebug(7113) << "cacheHasPage =" << cacheHasPage; - return cacheHasPage; - } - if (!cacheHasPage) { - // start a new cache file later if appropriate - m_request.cacheTag.ioMode = WriteToCache; - } - break; - } - case HTTP_HEAD: - break; - case HTTP_PUT: - case HTTP_POST: - hasBodyData = true; - break; - case HTTP_DELETE: - case HTTP_OPTIONS: - break; - case DAV_PROPFIND: - hasDavData = true; - davHeader = QLatin1String("Depth: "); - if ( hasMetaData( QLatin1String("davDepth") ) ) - { - kDebug(7113) << "Reading DAV depth from metadata:" << metaData( QLatin1String("davDepth") ); - davHeader += metaData( QLatin1String("davDepth") ); - } - else - { - if ( m_request.davData.depth == 2 ) - davHeader += QLatin1String("infinity"); - else - davHeader += QString::number( m_request.davData.depth ); - } - davHeader += QLatin1String("\r\n"); - break; - case DAV_PROPPATCH: - hasDavData = true; - break; - case DAV_MKCOL: - break; - case DAV_COPY: - case DAV_MOVE: - davHeader = QLatin1String("Destination: ") + m_request.davData.desturl; - // infinity depth means copy recursively - // (optional for copy -> but is the desired action) - davHeader += QLatin1String("\r\nDepth: infinity\r\nOverwrite: "); - davHeader += QLatin1Char(m_request.davData.overwrite ? 'T' : 'F'); - davHeader += QLatin1String("\r\n"); - break; - case DAV_LOCK: - davHeader = QLatin1String("Timeout: "); - { - uint timeout = 0; - if ( hasMetaData( QLatin1String("davTimeout") ) ) - timeout = metaData( QLatin1String("davTimeout") ).toUInt(); - if ( timeout == 0 ) - davHeader += QLatin1String("Infinite"); - else - davHeader += QLatin1String("Seconds-") + QString::number(timeout); - } - davHeader += QLatin1String("\r\n"); - hasDavData = true; - break; - case DAV_UNLOCK: - davHeader = QLatin1String("Lock-token: ") + metaData(QLatin1String("davLockToken")) + QLatin1String("\r\n"); - break; - case DAV_SEARCH: - case DAV_REPORT: - hasDavData = true; - /* fall through */ - case DAV_SUBSCRIBE: - case DAV_UNSUBSCRIBE: - case DAV_POLL: - break; - default: - error (ERR_UNSUPPORTED_ACTION, QString()); - return false; - } - // DAV_POLL; DAV_NOTIFY - - header += formatRequestUri() + QLatin1String(" HTTP/1.1\r\n"); /* start header */ - - /* support for virtual hosts and required by HTTP 1.1 */ - header += QLatin1String("Host: ") + m_request.encoded_hostname; - if (m_request.url.port(defaultPort()) != defaultPort()) { - header += QLatin1Char(':') + QString::number(m_request.url.port()); - } - header += QLatin1String("\r\n"); - - // Support old HTTP/1.0 style keep-alive header for compatibility - // purposes as well as performance improvements while giving end - // users the ability to disable this feature for proxy servers that - // don't support it, e.g. junkbuster proxy server. - if (isHttpProxy(m_request.proxyUrl) && !isAutoSsl()) { - header += QLatin1String("Proxy-Connection: "); - } else { - header += QLatin1String("Connection: "); - } - if (m_request.isKeepAlive) { - header += QLatin1String("keep-alive\r\n"); - } else { - header += QLatin1String("close\r\n"); - } - - if (!m_request.userAgent.isEmpty()) - { - header += QLatin1String("User-Agent: "); - header += m_request.userAgent; - header += QLatin1String("\r\n"); - } - - if (!m_request.referrer.isEmpty()) - { - header += QLatin1String("Referer: "); //Don't try to correct spelling! - header += m_request.referrer; - header += QLatin1String("\r\n"); - } - - if ( m_request.endoffset > m_request.offset ) - { - header += QLatin1String("Range: bytes="); - header += KIO::number(m_request.offset); - header += QLatin1Char('-'); - header += KIO::number(m_request.endoffset); - header += QLatin1String("\r\n"); - kDebug(7103) << "kio_http : Range =" << KIO::number(m_request.offset) - << "-" << KIO::number(m_request.endoffset); - } - else if ( m_request.offset > 0 && m_request.endoffset == 0 ) - { - header += QLatin1String("Range: bytes="); - header += KIO::number(m_request.offset); - header += QLatin1String("-\r\n"); - kDebug(7103) << "kio_http: Range =" << KIO::number(m_request.offset); - } - - if ( !m_request.cacheTag.useCache || m_request.cacheTag.policy==CC_Reload ) - { - /* No caching for reload */ - header += QLatin1String("Pragma: no-cache\r\n"); /* for HTTP/1.0 caches */ - header += QLatin1String("Cache-control: no-cache\r\n"); /* for HTTP >=1.1 caches */ - } - else if (m_request.cacheTag.plan(m_maxCacheAge) == CacheTag::ValidateCached) - { - kDebug(7113) << "needs validation, performing conditional get."; - /* conditional get */ - if (!m_request.cacheTag.etag.isEmpty()) - header += QLatin1String("If-None-Match: ") + m_request.cacheTag.etag + QLatin1String("\r\n"); - - if (m_request.cacheTag.lastModifiedDate != -1) { - const QString httpDate = formatHttpDate(m_request.cacheTag.lastModifiedDate); - header += QLatin1String("If-Modified-Since: ") + httpDate + QLatin1String("\r\n"); - setMetaData(QLatin1String("modified"), httpDate); - } - } - - header += QLatin1String("Accept: "); - const QString acceptHeader = metaData(QLatin1String("accept")); - if (!acceptHeader.isEmpty()) - header += acceptHeader; - else - header += QLatin1String(DEFAULT_ACCEPT_HEADER); - header += QLatin1String("\r\n"); - - if (m_request.allowTransferCompression) - header += QLatin1String("Accept-Encoding: gzip, deflate, x-gzip, x-deflate\r\n"); - - if (!m_request.charsets.isEmpty()) - header += QLatin1String("Accept-Charset: ") + m_request.charsets + QLatin1String("\r\n"); - - if (!m_request.languages.isEmpty()) - header += QLatin1String("Accept-Language: ") + m_request.languages + QLatin1String("\r\n"); - - QString cookieStr; - const QString cookieMode = metaData(QLatin1String("cookies")).toLower(); - - if (cookieMode == QLatin1String("none")) - { - m_request.cookieMode = HTTPRequest::CookiesNone; - } - else if (cookieMode == QLatin1String("manual")) - { - m_request.cookieMode = HTTPRequest::CookiesManual; - cookieStr = metaData(QLatin1String("setcookies")); - } - else - { - m_request.cookieMode = HTTPRequest::CookiesAuto; - if (m_request.useCookieJar) - cookieStr = findCookies(m_request.url.url()); - } - - if (!cookieStr.isEmpty()) - header += cookieStr + QLatin1String("\r\n"); - - const QString customHeader = metaData( QLatin1String("customHTTPHeader") ); - if (!customHeader.isEmpty()) - { - header += sanitizeCustomHTTPHeader(customHeader); - header += QLatin1String("\r\n"); - } - - const QString contentType = metaData(QLatin1String("content-type")); - if (!contentType.isEmpty()) - { - if (!contentType.startsWith(QLatin1String("content-type"), Qt::CaseInsensitive)) - header += QLatin1String("Content-Type: "); - header += contentType; - header += QLatin1String("\r\n"); - } - - // DoNotTrack feature... - if (config()->readEntry("DoNotTrack", false)) - header += QLatin1String("DNT: 1\r\n"); - - // Remember that at least one failed (with 401 or 407) request/response - // roundtrip is necessary for the server to tell us that it requires - // authentication. However, we proactively add authentication headers if when - // we have cached credentials to avoid the extra roundtrip where possible. - header += authenticationHeader(); - - if ( m_protocol == "webdav" || m_protocol == "webdavs" ) - { - header += davProcessLocks(); - - // add extra webdav headers, if supplied - davHeader += metaData(QLatin1String("davHeader")); - - // Set content type of webdav data - if (hasDavData) - davHeader += QLatin1String("Content-Type: text/xml; charset=utf-8\r\n"); - - // add extra header elements for WebDAV - header += davHeader; - } - } - - kDebug(7103) << "============ Sending Header:"; - Q_FOREACH (const QString &s, header.split(QLatin1String("\r\n"), QString::SkipEmptyParts)) { - kDebug(7103) << s; - } - - // End the header iff there is no payload data. If we do have payload data - // sendBody() will add another field to the header, Content-Length. - if (!hasBodyData && !hasDavData) - header += QLatin1String("\r\n"); - - - // Now that we have our formatted header, let's send it! - - // Clear out per-connection settings... - resetConnectionSettings(); - - // Send the data to the remote machine... - const QByteArray headerBytes = header.toLatin1(); - ssize_t written = write(headerBytes.constData(), headerBytes.length()); - bool sendOk = (written == (ssize_t) headerBytes.length()); - if (!sendOk) - { - kDebug(7113) << "Connection broken! (" << m_request.url.host() << ")" - << " -- intended to write" << headerBytes.length() - << "bytes but wrote" << (int)written << "."; - - // The server might have closed the connection due to a timeout, or maybe - // some transport problem arose while the connection was idle. - if (m_request.isKeepAlive) - { - httpCloseConnection(); - return true; // Try again - } - - kDebug(7113) << "sendOk == false. Connection broken !" - << " -- intended to write" << headerBytes.length() - << "bytes but wrote" << (int)written << "."; - error( ERR_CONNECTION_BROKEN, m_request.url.host() ); - return false; - } - else - kDebug(7113) << "sent it!"; - - bool res = true; - if (hasBodyData || hasDavData) - res = sendBody(); - - infoMessage(i18n("%1 contacted. Waiting for reply...", m_request.url.host())); - - return res; -} - -void HTTPProtocol::forwardHttpResponseHeader(bool forwardImmediately) -{ - // Send the response header if it was requested... - if (!config()->readEntry("PropagateHttpHeader", false)) - return; - - setMetaData(QLatin1String("HTTP-Headers"), m_responseHeaders.join(QString(QLatin1Char('\n')))); - - if (forwardImmediately) - sendMetaData(); -} - -bool HTTPProtocol::parseHeaderFromCache() -{ - kDebug(7113); - if (!cacheFileReadTextHeader2()) { - return false; - } - - Q_FOREACH (const QString &str, m_responseHeaders) { - const QString header = str.trimmed(); - if (header.startsWith(QLatin1String("content-type:"), Qt::CaseInsensitive)) { - int pos = header.indexOf(QLatin1String("charset="), Qt::CaseInsensitive); - if (pos != -1) { - const QString charset = header.mid(pos + 8).toLower(); - m_request.cacheTag.charset = charset; - setMetaData(QLatin1String("charset"), charset); - } - } else if (header.startsWith(QLatin1String("content-language:"), Qt::CaseInsensitive)) { - const QString language = header.mid(17).trimmed().toLower(); - setMetaData(QLatin1String("content-language"), language); - } else if (header.startsWith(QLatin1String("content-disposition:"), Qt::CaseInsensitive)) { - parseContentDisposition(header.mid(20).toLower()); - } - } - - if (m_request.cacheTag.lastModifiedDate != -1) { - setMetaData(QLatin1String("modified"), formatHttpDate(m_request.cacheTag.lastModifiedDate)); - } - - // this header comes from the cache, so the response must have been cacheable :) - setCacheabilityMetadata(true); - kDebug(7113) << "Emitting mimeType" << m_mimeType; - forwardHttpResponseHeader(false); - mimeType(m_mimeType); - // IMPORTANT: Do not remove the call below or the http response headers will - // not be available to the application if this slave is put on hold. - forwardHttpResponseHeader(); - return true; -} - -void HTTPProtocol::fixupResponseMimetype() -{ - if (m_mimeType.isEmpty()) - return; - - kDebug(7113) << "before fixup" << m_mimeType; - // Convert some common mimetypes to standard mimetypes - if (m_mimeType == QLatin1String("application/x-targz")) - m_mimeType = QLatin1String("application/x-compressed-tar"); - else if (m_mimeType == QLatin1String("image/x-png")) - m_mimeType = QLatin1String("image/png"); - else if (m_mimeType == QLatin1String("audio/x-mp3") || m_mimeType == QLatin1String("audio/x-mpeg") || m_mimeType == QLatin1String("audio/mp3")) - m_mimeType = QLatin1String("audio/mpeg"); - else if (m_mimeType == QLatin1String("audio/microsoft-wave")) - m_mimeType = QLatin1String("audio/x-wav"); - else if (m_mimeType == QLatin1String("image/x-ms-bmp")) - m_mimeType = QLatin1String("image/bmp"); - - // Crypto ones.... - else if (m_mimeType == QLatin1String("application/pkix-cert") || - m_mimeType == QLatin1String("application/binary-certificate")) { - m_mimeType = QLatin1String("application/x-x509-ca-cert"); - } - - // Prefer application/x-compressed-tar or x-gzpostscript over application/x-gzip. - else if (m_mimeType == QLatin1String("application/x-gzip")) { - if ((m_request.url.path().endsWith(QLatin1String(".tar.gz"))) || - (m_request.url.path().endsWith(QLatin1String(".tar")))) - m_mimeType = QLatin1String("application/x-compressed-tar"); - if ((m_request.url.path().endsWith(QLatin1String(".ps.gz")))) - m_mimeType = QLatin1String("application/x-gzpostscript"); - } - - // Prefer application/x-xz-compressed-tar over application/x-xz for LMZA compressed - // tar files. Arch Linux AUR servers notoriously send the wrong mimetype for this. - else if(m_mimeType == QLatin1String("application/x-xz")) { - if (m_request.url.path().endsWith(QLatin1String(".tar.xz")) || - m_request.url.path().endsWith(QLatin1String(".txz"))) { - m_mimeType = QLatin1String("application/x-xz-compressed-tar"); - } - } - - // Some webservers say "text/plain" when they mean "application/x-bzip" - else if ((m_mimeType == QLatin1String("text/plain")) || (m_mimeType == QLatin1String("application/octet-stream"))) { - const QString ext = QFileInfo(m_request.url.path()).suffix().toUpper(); - if (ext == QLatin1String("BZ2")) - m_mimeType = QLatin1String("application/x-bzip"); - else if (ext == QLatin1String("PEM")) - m_mimeType = QLatin1String("application/x-x509-ca-cert"); - else if (ext == QLatin1String("SWF")) - m_mimeType = QLatin1String("application/x-shockwave-flash"); - else if (ext == QLatin1String("PLS")) - m_mimeType = QLatin1String("audio/x-scpls"); - else if (ext == QLatin1String("WMV")) - m_mimeType = QLatin1String("video/x-ms-wmv"); - else if (ext == QLatin1String("WEBM")) - m_mimeType = QLatin1String("video/webm"); - else if (ext == QLatin1String("DEB")) - m_mimeType = QLatin1String("application/x-deb"); - } - kDebug(7113) << "after fixup" << m_mimeType; -} - - -void HTTPProtocol::fixupResponseContentEncoding() -{ - // WABA: Correct for tgz files with a gzip-encoding. - // They really shouldn't put gzip in the Content-Encoding field! - // Web-servers really shouldn't do this: They let Content-Size refer - // to the size of the tgz file, not to the size of the tar file, - // while the Content-Type refers to "tar" instead of "tgz". - if (!m_contentEncodings.isEmpty() && m_contentEncodings.last() == QLatin1String("gzip")) { - if (m_mimeType == QLatin1String("application/x-tar")) { - m_contentEncodings.removeLast(); - m_mimeType = QLatin1String("application/x-compressed-tar"); - } else if (m_mimeType == QLatin1String("application/postscript")) { - // LEONB: Adding another exception for psgz files. - // Could we use the mimelnk files instead of hardcoding all this? - m_contentEncodings.removeLast(); - m_mimeType = QLatin1String("application/x-gzpostscript"); - } else if ((m_request.allowTransferCompression && - m_mimeType == QLatin1String("text/html")) - || - (m_request.allowTransferCompression && - m_mimeType != QLatin1String("application/x-compressed-tar") && - m_mimeType != QLatin1String("application/x-tgz") && // deprecated name - m_mimeType != QLatin1String("application/x-targz") && // deprecated name - m_mimeType != QLatin1String("application/x-gzip"))) { - // Unzip! - } else { - m_contentEncodings.removeLast(); - m_mimeType = QLatin1String("application/x-gzip"); - } - } - - // We can't handle "bzip2" encoding (yet). So if we get something with - // bzip2 encoding, we change the mimetype to "application/x-bzip". - // Note for future changes: some web-servers send both "bzip2" as - // encoding and "application/x-bzip[2]" as mimetype. That is wrong. - // currently that doesn't bother us, because we remove the encoding - // and set the mimetype to x-bzip anyway. - if (!m_contentEncodings.isEmpty() && m_contentEncodings.last() == QLatin1String("bzip2")) { - m_contentEncodings.removeLast(); - m_mimeType = QLatin1String("application/x-bzip"); - } -} - -//Return true if the term was found, false otherwise. Advance *pos. -//If (*pos + strlen(term) >= end) just advance *pos to end and return false. -//This means that users should always search for the shortest terms first. -static bool consume(const char input[], int *pos, int end, const char *term) -{ - // note: gcc/g++ is quite good at optimizing away redundant strlen()s - int idx = *pos; - if (idx + (int)strlen(term) >= end) { - *pos = end; - return false; - } - if (strncasecmp(&input[idx], term, strlen(term)) == 0) { - *pos = idx + strlen(term); - return true; - } - return false; -} - -/** - * This function will read in the return header from the server. It will - * not read in the body of the return message. It will also not transmit - * the header to our client as the client doesn't need to know the gory - * details of HTTP headers. - */ -bool HTTPProtocol::readResponseHeader() -{ - resetResponseParsing(); - if (m_request.cacheTag.ioMode == ReadFromCache && - m_request.cacheTag.plan(m_maxCacheAge) == CacheTag::UseCached) { - // parseHeaderFromCache replaces this method in case of cached content - return parseHeaderFromCache(); - } - -try_again: - kDebug(7113); - - bool upgradeRequired = false; // Server demands that we upgrade to something - // This is also true if we ask to upgrade and - // the server accepts, since we are now - // committed to doing so - bool noHeadersFound = false; - - m_request.cacheTag.charset.clear(); - m_responseHeaders.clear(); - - static const int maxHeaderSize = 128 * 1024; - - char buffer[maxHeaderSize]; - bool cont = false; - bool bCanResume = false; - - if (!isConnected()) { - kDebug(7113) << "No connection."; - return false; // Reestablish connection and try again - } - -#if 0 - // NOTE: This is unnecessary since TCPSlaveBase::read does the same exact - // thing. Plus, if we are unable to read from the socket we need to resend - // the request as done below, not error out! Do not assume remote server - // will honor persistent connections!! - if (!waitForResponse(m_remoteRespTimeout)) { - kDebug(7113) << "Got socket error:" << socket()->errorString(); - // No response error - error(ERR_SERVER_TIMEOUT , m_request.url.host()); - return false; - } -#endif - - int bufPos = 0; - bool foundDelimiter = readDelimitedText(buffer, &bufPos, maxHeaderSize, 1); - if (!foundDelimiter && bufPos < maxHeaderSize) { - kDebug(7113) << "EOF while waiting for header start."; - if (m_request.isKeepAlive && m_iEOFRetryCount < 2) { - m_iEOFRetryCount++; - httpCloseConnection(); // Try to reestablish connection. - return false; // Reestablish connection and try again. - } - - if (m_request.method == HTTP_HEAD) { - // HACK - // Some web-servers fail to respond properly to a HEAD request. - // We compensate for their failure to properly implement the HTTP standard - // by assuming that they will be sending html. - kDebug(7113) << "HEAD -> returned mimetype:" << DEFAULT_MIME_TYPE; - mimeType(QLatin1String(DEFAULT_MIME_TYPE)); - return true; - } - - kDebug(7113) << "Connection broken !"; - error( ERR_CONNECTION_BROKEN, m_request.url.host() ); - return false; - } - if (!foundDelimiter) { - //### buffer too small for first line of header(!) - Q_ASSERT(0); - } - - kDebug(7103) << "============ Received Status Response:"; - kDebug(7103) << QByteArray(buffer, bufPos).trimmed(); - - HTTP_REV httpRev = HTTP_None; - int idx = 0; - - if (idx != bufPos && buffer[idx] == '<') { - kDebug(7103) << "No valid HTTP header found! Document starts with XML/HTML tag"; - // document starts with a tag, assume HTML instead of text/plain - m_mimeType = QLatin1String("text/html"); - m_request.responseCode = 200; // Fake it - httpRev = HTTP_Unknown; - m_request.isKeepAlive = false; - noHeadersFound = true; - // put string back - unread(buffer, bufPos); - goto endParsing; - } - - // "HTTP/1.1" or similar - if (consume(buffer, &idx, bufPos, "ICY ")) { - httpRev = SHOUTCAST; - m_request.isKeepAlive = false; - } else if (consume(buffer, &idx, bufPos, "HTTP/")) { - if (consume(buffer, &idx, bufPos, "1.0")) { - httpRev = HTTP_10; - m_request.isKeepAlive = false; - } else if (consume(buffer, &idx, bufPos, "1.1")) { - httpRev = HTTP_11; - } - } - - if (httpRev == HTTP_None && bufPos != 0) { - // Remote server does not seem to speak HTTP at all - // Put the crap back into the buffer and hope for the best - kDebug(7113) << "DO NOT WANT." << bufPos; - unread(buffer, bufPos); - if (m_request.responseCode) { - m_request.prevResponseCode = m_request.responseCode; - } - m_request.responseCode = 200; // Fake it - httpRev = HTTP_Unknown; - m_request.isKeepAlive = false; - noHeadersFound = true; - goto endParsing; - } - - // response code //### maybe wrong if we need several iterations for this response... - //### also, do multiple iterations (cf. try_again) to parse one header work w/ pipelining? - if (m_request.responseCode) { - m_request.prevResponseCode = m_request.responseCode; - } - skipSpace(buffer, &idx, bufPos); - //TODO saner handling of invalid response code strings - if (idx != bufPos) { - m_request.responseCode = atoi(&buffer[idx]); - } else { - m_request.responseCode = 200; - } - // move idx to start of (yet to be fetched) next line, skipping the "OK" - idx = bufPos; - // (don't bother parsing the "OK", what do we do if it isn't there anyway?) - - // immediately act on most response codes... - - // Protect users against bogus username intended to fool them into visiting - // sites they had no intention of visiting. - if (isPotentialSpoofingAttack(m_request, config())) { - // kDebug(7113) << "**** POTENTIAL ADDRESS SPOOFING:" << m_request.url; - const int result = - messageBox(WarningYesNo, - i18nc("@info Security check on url being accessed", - "

You are about to log in to the site \"%1\" " - "with the username \"%2\", but the website " - "does not require authentication. " - "This may be an attempt to trick you.

" - "

Is \"%1\" the site you want to visit?

", - m_request.url.host(), m_request.url.user()), - i18nc("@title:window", "Confirm Website Access")); - if (result == KMessageBox::No) { - error(ERR_USER_CANCELED, m_request.url.url()); - return false; - } - setMetaData(QLatin1String("{internal~currenthost}LastSpoofedUserName"), m_request.url.user()); - } - - if (m_request.responseCode != 200 && m_request.responseCode != 304) { - m_request.cacheTag.ioMode = NoCache; - - if (m_request.responseCode >= 500 && m_request.responseCode <= 599) { - // Server side errors - if (m_request.method == HTTP_HEAD) { - ; // Ignore error - } else { - if (!sendErrorPageNotification()) { - error(ERR_INTERNAL_SERVER, m_request.url.prettyUrl()); - return false; - } - } - } else if (m_request.responseCode == 416) { - // Range not supported - m_request.offset = 0; - return false; // Try again. - } else if (m_request.responseCode == 426) { - // Upgrade Required - upgradeRequired = true; - } else if (m_request.responseCode >= 400 && m_request.responseCode <= 499 && !isAuthenticationRequired(m_request.responseCode)) { - // Any other client errors - // Tell that we will only get an error page here. - if (!sendErrorPageNotification()) { - if (m_request.responseCode == 403) - error(ERR_ACCESS_DENIED, m_request.url.prettyUrl()); - else - error(ERR_DOES_NOT_EXIST, m_request.url.prettyUrl()); - return false; - } - } else if (m_request.responseCode >= 301 && m_request.responseCode<= 308) { - // NOTE: According to RFC 2616 (section 10.3.[2-4,8]), 301 and 302 - // redirects for a POST operation should not be convered to a GET - // request. That should only be done for a 303 response. However, - // because almost all other client implementations do exactly that - // in violation of the spec, many servers have simply adapted to - // this way of doing things! Thus, we are forced to do the same - // thing here. Otherwise, we lose compatibility and might not be - // able to correctly retrieve sites that redirect. - switch (m_request.responseCode) { - case 301: // Moved Permanently - setMetaData(QLatin1String("permanent-redirect"), QLatin1String("true")); - // fall through - case 302: // Found - if (m_request.sentMethodString == "POST") { - setMetaData(QLatin1String("redirect-to-get"), QLatin1String("true")); - } - break; - case 303: // See Other - if (m_request.method != HTTP_HEAD) { - setMetaData(QLatin1String("redirect-to-get"), QLatin1String("true")); - } - break; - case 308: // Permanent Redirect - setMetaData(QLatin1String("permanent-redirect"), QLatin1String("true")); - break; - default: - break; - } - } else if (m_request.responseCode == 204) { - // No content - - // error(ERR_NO_CONTENT, i18n("Data have been successfully sent.")); - // Short circuit and do nothing! - - // The original handling here was wrong, this is not an error: eg. in the - // example of a 204 No Content response to a PUT completing. - // m_iError = true; - // return false; - } else if (m_request.responseCode == 206) { - if (m_request.offset) { - bCanResume = true; - } - } else if (m_request.responseCode == 102) { - // Processing (for WebDAV) - /*** - * This status code is given when the server expects the - * command to take significant time to complete. So, inform - * the user. - */ - infoMessage( i18n( "Server processing request, please wait..." ) ); - cont = true; - } else if (m_request.responseCode == 100) { - // We got 'Continue' - ignore it - cont = true; - } - } // (m_request.responseCode != 200 && m_request.responseCode != 304) - -endParsing: - bool authRequiresAnotherRoundtrip = false; - - // Skip the whole header parsing if we got no HTTP headers at all - if (!noHeadersFound) { - // Auth handling - const bool wasAuthError = isAuthenticationRequired(m_request.prevResponseCode); - const bool isAuthError = isAuthenticationRequired(m_request.responseCode); - const bool sameAuthError = (m_request.responseCode == m_request.prevResponseCode); - kDebug(7113) << "wasAuthError=" << wasAuthError << "isAuthError=" << isAuthError - << "sameAuthError=" << sameAuthError; - // Not the same authorization error as before and no generic error? - // -> save the successful credentials. - if (wasAuthError && (m_request.responseCode < 400 || (isAuthError && !sameAuthError))) { - saveAuthenticationData(); - } - - // done with the first line; now tokenize the other lines - - // TODO review use of STRTOLL vs. QByteArray::toInt() - - foundDelimiter = readDelimitedText(buffer, &bufPos, maxHeaderSize, 2); - kDebug(7113) << " -- full response:" << endl << QByteArray(buffer, bufPos).trimmed(); - // Use this to see newlines: - //kDebug(7113) << " -- full response:" << endl << QByteArray(buffer, bufPos).replace("\r", "\\r").replace("\n", "\\n\n"); - Q_ASSERT(foundDelimiter); - - //NOTE because tokenizer will overwrite newlines in case of line continuations in the header - // unread(buffer, bufSize) will not generally work anymore. we don't need it either. - // either we have a http response line -> try to parse the header, fail if it doesn't work - // or we have garbage -> fail. - HeaderTokenizer tokenizer(buffer); - tokenizer.tokenize(idx, sizeof(buffer)); - - // Note that not receiving "accept-ranges" means that all bets are off - // wrt the server supporting ranges. - TokenIterator tIt = tokenizer.iterator("accept-ranges"); - if (tIt.hasNext() && tIt.next().toLower().startsWith("none")) { // krazy:exclude=strings - bCanResume = false; - } - - tIt = tokenizer.iterator("keep-alive"); - while (tIt.hasNext()) { - QByteArray ka = tIt.next().trimmed().toLower(); - if (ka.startsWith("timeout=")) { // krazy:exclude=strings - int ka_timeout = ka.mid(qstrlen("timeout=")).trimmed().toInt(); - if (ka_timeout > 0) - m_request.keepAliveTimeout = ka_timeout; - if (httpRev == HTTP_10) { - m_request.isKeepAlive = true; - } - - break; // we want to fetch ka timeout only - } - } - - // get the size of our data - tIt = tokenizer.iterator("content-length"); - if (tIt.hasNext()) { - m_iSize = STRTOLL(tIt.next().constData(), 0, 10); - } - - tIt = tokenizer.iterator("content-location"); - if (tIt.hasNext()) { - setMetaData(QLatin1String("content-location"), toQString(tIt.next().trimmed())); - } - - // which type of data do we have? - QString mediaValue; - QString mediaAttribute; - tIt = tokenizer.iterator("content-type"); - if (tIt.hasNext()) { - QList l = tIt.next().split(';'); - if (!l.isEmpty()) { - // Assign the mime-type. - m_mimeType = toQString(l.first().trimmed().toLower()); - if (m_mimeType.startsWith(QLatin1Char('"'))) { - m_mimeType.remove(0, 1); - } - if (m_mimeType.endsWith(QLatin1Char('"'))) { - m_mimeType.chop(1); - } - kDebug(7113) << "Content-type:" << m_mimeType; - l.removeFirst(); - } - - // If we still have text, then it means we have a mime-type with a - // parameter (eg: charset=iso-8851) ; so let's get that... - Q_FOREACH (const QByteArray &statement, l) { - const int index = statement.indexOf('='); - if (index <= 0) { - mediaAttribute = toQString(statement.mid(0, index)); - } else { - mediaAttribute = toQString(statement.mid(0, index)); - mediaValue = toQString(statement.mid(index+1)); - } - mediaAttribute = mediaAttribute.trimmed(); - mediaValue = mediaValue.trimmed(); - - bool quoted = false; - if (mediaValue.startsWith(QLatin1Char('"'))) { - quoted = true; - mediaValue.remove(0, 1); - } - - if (mediaValue.endsWith(QLatin1Char('"'))) { - mediaValue.chop(1); - } - - kDebug (7113) << "Encoding-type:" << mediaAttribute << "=" << mediaValue; - - if (mediaAttribute == QLatin1String("charset")) { - mediaValue = mediaValue.toLower(); - m_request.cacheTag.charset = mediaValue; - setMetaData(QLatin1String("charset"), mediaValue); - } else { - setMetaData(QLatin1String("media-") + mediaAttribute, mediaValue); - if (quoted) { - setMetaData(QLatin1String("media-") + mediaAttribute + QLatin1String("-kio-quoted"), - QLatin1String("true")); - } - } - } - } - - // content? - tIt = tokenizer.iterator("content-encoding"); - while (tIt.hasNext()) { - // This is so wrong !! No wonder kio_http is stripping the - // gzip encoding from downloaded files. This solves multiple - // bug reports and caitoo's problem with downloads when such a - // header is encountered... - - // A quote from RFC 2616: - // " When present, its (Content-Encoding) value indicates what additional - // content have been applied to the entity body, and thus what decoding - // mechanism must be applied to obtain the media-type referenced by the - // Content-Type header field. Content-Encoding is primarily used to allow - // a document to be compressed without loosing the identity of its underlying - // media type. Simply put if it is specified, this is the actual mime-type - // we should use when we pull the resource !!! - addEncoding(toQString(tIt.next()), m_contentEncodings); - } - // Refer to RFC 2616 sec 15.5/19.5.1 and RFC 2183 - tIt = tokenizer.iterator("content-disposition"); - if (tIt.hasNext()) { - parseContentDisposition(toQString(tIt.next())); - } - tIt = tokenizer.iterator("content-language"); - if (tIt.hasNext()) { - QString language = toQString(tIt.next().trimmed()); - if (!language.isEmpty()) { - setMetaData(QLatin1String("content-language"), language); - } - } - - tIt = tokenizer.iterator("proxy-connection"); - if (tIt.hasNext() && isHttpProxy(m_request.proxyUrl) && !isAutoSsl()) { - QByteArray pc = tIt.next().toLower(); - if (pc.startsWith("close")) { // krazy:exclude=strings - m_request.isKeepAlive = false; - } else if (pc.startsWith("keep-alive")) { // krazy:exclude=strings - m_request.isKeepAlive = true; - } - } - - tIt = tokenizer.iterator("link"); - if (tIt.hasNext()) { - // We only support Link: ; rel="type" so far - QStringList link = toQString(tIt.next()).split(QLatin1Char(';'), QString::SkipEmptyParts); - if (link.count() == 2) { - QString rel = link[1].trimmed(); - if (rel.startsWith(QLatin1String("rel=\""))) { - rel = rel.mid(5, rel.length() - 6); - if (rel.toLower() == QLatin1String("pageservices")) { - //### the remove() part looks fishy! - QString url = link[0].remove(QRegExp(QLatin1String("[<>]"))).trimmed(); - setMetaData(QLatin1String("PageServices"), url); - } - } - } - } - - tIt = tokenizer.iterator("p3p"); - if (tIt.hasNext()) { - // P3P privacy policy information - QStringList policyrefs, compact; - while (tIt.hasNext()) { - QStringList policy = toQString(tIt.next().simplified()) - .split(QLatin1Char('='), QString::SkipEmptyParts); - if (policy.count() == 2) { - if (policy[0].toLower() == QLatin1String("policyref")) { - policyrefs << policy[1].remove(QRegExp(QLatin1String("[\")\']"))).trimmed(); - } else if (policy[0].toLower() == QLatin1String("cp")) { - // We convert to cp\ncp\ncp\n[...]\ncp to be consistent with - // other metadata sent in strings. This could be a bit more - // efficient but I'm going for correctness right now. - const QString s = policy[1].remove(QRegExp(QLatin1String("[\")\']"))); - const QStringList cps = s.split(QLatin1Char(' '), QString::SkipEmptyParts); - compact << cps; - } - } - } - if (!policyrefs.isEmpty()) { - setMetaData(QLatin1String("PrivacyPolicy"), policyrefs.join(QLatin1String("\n"))); - } - if (!compact.isEmpty()) { - setMetaData(QLatin1String("PrivacyCompactPolicy"), compact.join(QLatin1String("\n"))); - } - } - - // continue only if we know that we're at least HTTP/1.0 - if (httpRev == HTTP_11 || httpRev == HTTP_10) { - // let them tell us if we should stay alive or not - tIt = tokenizer.iterator("connection"); - while (tIt.hasNext()) { - QByteArray connection = tIt.next().toLower(); - if (!(isHttpProxy(m_request.proxyUrl) && !isAutoSsl())) { - if (connection.startsWith("close")) { // krazy:exclude=strings - m_request.isKeepAlive = false; - } else if (connection.startsWith("keep-alive")) { // krazy:exclude=strings - m_request.isKeepAlive = true; - } - } - if (connection.startsWith("upgrade")) { // krazy:exclude=strings - if (m_request.responseCode == 101) { - // Ok, an upgrade was accepted, now we must do it - upgradeRequired = true; - } else if (upgradeRequired) { // 426 - // Nothing to do since we did it above already - } - } - } - // what kind of encoding do we have? transfer? - tIt = tokenizer.iterator("transfer-encoding"); - while (tIt.hasNext()) { - // If multiple encodings have been applied to an entity, the - // transfer-codings MUST be listed in the order in which they - // were applied. - addEncoding(toQString(tIt.next().trimmed()), m_transferEncodings); - } - - // md5 signature - tIt = tokenizer.iterator("content-md5"); - if (tIt.hasNext()) { - m_contentMD5 = toQString(tIt.next().trimmed()); - } - - // *** Responses to the HTTP OPTIONS method follow - // WebDAV capabilities - tIt = tokenizer.iterator("dav"); - while (tIt.hasNext()) { - m_davCapabilities << toQString(tIt.next()); - } - // *** Responses to the HTTP OPTIONS method finished - } - - - // Now process the HTTP/1.1 upgrade - QStringList upgradeOffers; - tIt = tokenizer.iterator("upgrade"); - if (tIt.hasNext()) { - // Now we have to check to see what is offered for the upgrade - QString offered = toQString(tIt.next()); - upgradeOffers = offered.split(QRegExp(QLatin1String("[ \n,\r\t]")), QString::SkipEmptyParts); - } - Q_FOREACH (const QString &opt, upgradeOffers) { - if (opt == QLatin1String("TLS/1.0")) { - if (!startSsl() && upgradeRequired) { - error(ERR_UPGRADE_REQUIRED, opt); - return false; - } - } else if (opt == QLatin1String("HTTP/1.1")) { - httpRev = HTTP_11; - } else if (upgradeRequired) { - // we are told to do an upgrade we don't understand - error(ERR_UPGRADE_REQUIRED, opt); - return false; - } - } - - // Harvest cookies (mmm, cookie fields!) - QByteArray cookieStr; // In case we get a cookie. - tIt = tokenizer.iterator("set-cookie"); - while (tIt.hasNext()) { - cookieStr += "Set-Cookie: "; - cookieStr += tIt.next(); - cookieStr += '\n'; - } - if (!cookieStr.isEmpty()) { - if ((m_request.cookieMode == HTTPRequest::CookiesAuto) && m_request.useCookieJar) { - // Give cookies to the cookiejar. - const QString domain = config()->readEntry("cross-domain"); - if (!domain.isEmpty() && isCrossDomainRequest(m_request.url.host(), domain)) { - cookieStr = "Cross-Domain\n" + cookieStr; - } - addCookies( m_request.url.url(), cookieStr ); - } else if (m_request.cookieMode == HTTPRequest::CookiesManual) { - // Pass cookie to application - setMetaData(QLatin1String("setcookies"), QString::fromUtf8(cookieStr)); // ## is encoding ok? - } - } - - // We need to reread the header if we got a '100 Continue' or '102 Processing' - // This may be a non keepalive connection so we handle this kind of loop internally - if ( cont ) { - kDebug(7113) << "cont; returning to mark try_again"; - goto try_again; - } - - if (!m_isChunked && (m_iSize == NO_SIZE) && m_request.isKeepAlive && - canHaveResponseBody(m_request.responseCode, m_request.method)) { - kDebug(7113) << "Ignoring keep-alive: otherwise unable to determine response body length."; - m_request.isKeepAlive = false; - } - - // TODO cache the proxy auth data (not doing this means a small performance regression for now) - - // we may need to send (Proxy or WWW) authorization data - if ((!m_request.doNotWWWAuthenticate && m_request.responseCode == 401) || - (!m_request.doNotProxyAuthenticate && m_request.responseCode == 407)) { - authRequiresAnotherRoundtrip = handleAuthenticationHeader(&tokenizer); - if (m_iError) { - // If error is set, then handleAuthenticationHeader failed. - return false; - } - } else { - authRequiresAnotherRoundtrip = false; - } - - QString locationStr; - // In fact we should do redirection only if we have a redirection response code (300 range) - tIt = tokenizer.iterator("location"); - if (tIt.hasNext() && m_request.responseCode > 299 && m_request.responseCode < 400) { - locationStr = QString::fromUtf8(tIt.next().trimmed()); - } - // We need to do a redirect - if (!locationStr.isEmpty()) - { - KUrl u(m_request.url, locationStr); - if(!u.isValid()) - { - error(ERR_MALFORMED_URL, u.prettyUrl()); - return false; - } - - // preserve #ref: (bug 124654) - // if we were at http://host/resource1#ref, we sent a GET for "/resource1" - // if we got redirected to http://host/resource2, then we have to re-add - // the fragment: - if (m_request.url.hasRef() && !u.hasRef() && - (m_request.url.host() == u.host()) && - (m_request.url.protocol() == u.protocol())) - u.setRef(m_request.url.ref()); - - m_isRedirection = true; - - if (!m_request.id.isEmpty()) - { - sendMetaData(); - } - - // If we're redirected to a http:// url, remember that we're doing webdav... - if (m_protocol == "webdav" || m_protocol == "webdavs"){ - if(u.protocol() == QLatin1String("http")){ - u.setProtocol(QLatin1String("webdav")); - }else if(u.protocol() == QLatin1String("https")){ - u.setProtocol(QLatin1String("webdavs")); - } - - m_request.redirectUrl = u; - } - - kDebug(7113) << "Re-directing from" << m_request.url - << "to" << u; - - redirection(u); - - // It would be hard to cache the redirection response correctly. The possible benefit - // is small (if at all, assuming fast disk and slow network), so don't do it. - cacheFileClose(); - setCacheabilityMetadata(false); - } - - // Inform the job that we can indeed resume... - if (bCanResume && m_request.offset) { - //TODO turn off caching??? - canResume(); - } else { - m_request.offset = 0; - } - - // Correct a few common wrong content encodings - fixupResponseContentEncoding(); - - // Correct some common incorrect pseudo-mimetypes - fixupResponseMimetype(); - - // parse everything related to expire and other dates, and cache directives; also switch - // between cache reading and writing depending on cache validation result. - cacheParseResponseHeader(tokenizer); - } - - if (m_request.cacheTag.ioMode == ReadFromCache) { - if (m_request.cacheTag.policy == CC_Verify && - m_request.cacheTag.plan(m_maxCacheAge) != CacheTag::UseCached) { - kDebug(7113) << "Reading resource from cache even though the cache plan is not " - "UseCached; the server is probably sending wrong expiry information."; - } - // parseHeaderFromCache replaces this method in case of cached content - return parseHeaderFromCache(); - } - - if (config()->readEntry("PropagateHttpHeader", false) || - m_request.cacheTag.ioMode == WriteToCache) { - // store header lines if they will be used; note that the tokenizer removing - // line continuation special cases is probably more good than bad. - int nextLinePos = 0; - int prevLinePos = 0; - bool haveMore = true; - while (haveMore) { - haveMore = nextLine(buffer, &nextLinePos, bufPos); - int prevLineEnd = nextLinePos; - while (buffer[prevLineEnd - 1] == '\r' || buffer[prevLineEnd - 1] == '\n') { - prevLineEnd--; - } - - m_responseHeaders.append(QString::fromLatin1(&buffer[prevLinePos], - prevLineEnd - prevLinePos)); - prevLinePos = nextLinePos; - } - - // IMPORTANT: Do not remove this line because forwardHttpResponseHeader - // is called below. This line is here to ensure the response headers are - // available to the client before it receives mimetype information. - // The support for putting ioslaves on hold in the KIO-QNAM integration - // will break if this line is removed. - setMetaData(QLatin1String("HTTP-Headers"), m_responseHeaders.join(QString(QLatin1Char('\n')))); - } - - // Let the app know about the mime-type iff this is not a redirection and - // the mime-type string is not empty. - if (!m_isRedirection && m_request.responseCode != 204 && - (!m_mimeType.isEmpty() || m_request.method == HTTP_HEAD) && - (m_isLoadingErrorPage || !authRequiresAnotherRoundtrip)) { - kDebug(7113) << "Emitting mimetype " << m_mimeType; - mimeType( m_mimeType ); - } - - // IMPORTANT: Do not move the function call below before doing any - // redirection. Otherwise it might mess up some sites, see BR# 150904. - forwardHttpResponseHeader(); - - if (m_request.method == HTTP_HEAD) - return true; - - return !authRequiresAnotherRoundtrip; // return true if no more credentials need to be sent -} - -void HTTPProtocol::parseContentDisposition(const QString &disposition) -{ - const QMap parameters = contentDispositionParser(disposition); - - QMapIterator it(parameters); - while (it.hasNext()) { - it.next(); - setMetaData(QLatin1String("content-disposition-") + it.key(), it.value()); - kDebug(7113) << "Content-Disposition:" << it.key() << "=" << it.value(); - } -} - -void HTTPProtocol::addEncoding(const QString &_encoding, QStringList &encs) -{ - QString encoding = _encoding.trimmed().toLower(); - // Identity is the same as no encoding - if (encoding == QLatin1String("identity")) { - return; - } else if (encoding == QLatin1String("8bit")) { - // Strange encoding returned by http://linac.ikp.physik.tu-darmstadt.de - return; - } else if (encoding == QLatin1String("chunked")) { - m_isChunked = true; - // Anyone know of a better way to handle unknown sizes possibly/ideally with unsigned ints? - //if ( m_cmd != CMD_COPY ) - m_iSize = NO_SIZE; - } else if ((encoding == QLatin1String("x-gzip")) || (encoding == QLatin1String("gzip"))) { - encs.append(QLatin1String("gzip")); - } else if ((encoding == QLatin1String("x-bzip2")) || (encoding == QLatin1String("bzip2"))) { - encs.append(QLatin1String("bzip2")); // Not yet supported! - } else if ((encoding == QLatin1String("x-deflate")) || (encoding == QLatin1String("deflate"))) { - encs.append(QLatin1String("deflate")); - } else { - kDebug(7113) << "Unknown encoding encountered. " - << "Please write code. Encoding =" << encoding; - } -} - -void HTTPProtocol::cacheParseResponseHeader(const HeaderTokenizer &tokenizer) -{ - if (!m_request.cacheTag.useCache) - return; - - // might have to add more response codes - if (m_request.responseCode != 200 && m_request.responseCode != 304) { - return; - } - - m_request.cacheTag.servedDate = -1; - m_request.cacheTag.lastModifiedDate = -1; - m_request.cacheTag.expireDate = -1; - - const qint64 currentDate = QDateTime::currentMSecsSinceEpoch()/1000; - bool mayCache = m_request.cacheTag.ioMode != NoCache; - - TokenIterator tIt = tokenizer.iterator("last-modified"); - if (tIt.hasNext()) { - m_request.cacheTag.lastModifiedDate = toTime_t(toQString(tIt.next()), KDateTime::RFCDate); - - //### might be good to canonicalize the date by using KDateTime::toString() - if (m_request.cacheTag.lastModifiedDate != -1) { - setMetaData(QLatin1String("modified"), toQString(tIt.current())); - } - } - - // determine from available information when the response was served by the origin server - { - qint64 dateHeader = -1; - tIt = tokenizer.iterator("date"); - if (tIt.hasNext()) { - dateHeader = toTime_t(toQString(tIt.next()), KDateTime::RFCDate); - // -1 on error - } - - qint64 ageHeader = 0; - tIt = tokenizer.iterator("age"); - if (tIt.hasNext()) { - ageHeader = tIt.next().toLongLong(); - // 0 on error - } - - if (dateHeader != -1) { - m_request.cacheTag.servedDate = dateHeader; - } else if (ageHeader) { - m_request.cacheTag.servedDate = currentDate - ageHeader; - } else { - m_request.cacheTag.servedDate = currentDate; - } - } - - bool hasCacheDirective = false; - // determine when the response "expires", i.e. becomes stale and needs revalidation - { - // (we also parse other cache directives here) - qint64 maxAgeHeader = 0; - tIt = tokenizer.iterator("cache-control"); - while (tIt.hasNext()) { - QByteArray cacheStr = tIt.next().toLower(); - if (cacheStr.startsWith("no-cache") || cacheStr.startsWith("no-store")) { // krazy:exclude=strings - // Don't put in cache - mayCache = false; - hasCacheDirective = true; - } else if (cacheStr.startsWith("max-age=")) { // krazy:exclude=strings - QByteArray ba = cacheStr.mid(qstrlen("max-age=")).trimmed(); - bool ok = false; - maxAgeHeader = ba.toLongLong(&ok); - if (ok) { - hasCacheDirective = true; - } - } - } - - qint64 expiresHeader = -1; - tIt = tokenizer.iterator("expires"); - if (tIt.hasNext()) { - expiresHeader = toTime_t(toQString(tIt.next()), KDateTime::RFCDate); - kDebug(7113) << "parsed expire date from 'expires' header:" << tIt.current(); - } - - if (maxAgeHeader) { - m_request.cacheTag.expireDate = m_request.cacheTag.servedDate + maxAgeHeader; - } else if (expiresHeader != -1) { - m_request.cacheTag.expireDate = expiresHeader; - } else { - // heuristic expiration date - if (m_request.cacheTag.lastModifiedDate != -1) { - // expAge is following the RFC 2616 suggestion for heuristic expiration - qint64 expAge = (m_request.cacheTag.servedDate - - m_request.cacheTag.lastModifiedDate) / 10; - // not in the RFC: make sure not to have a huge heuristic cache lifetime - expAge = qMin(expAge, qint64(3600 * 24)); - m_request.cacheTag.expireDate = m_request.cacheTag.servedDate + expAge; - } else { - m_request.cacheTag.expireDate = m_request.cacheTag.servedDate + - DEFAULT_CACHE_EXPIRE; - } - } - // make sure that no future clock monkey business causes the cache entry to un-expire - if (m_request.cacheTag.expireDate < currentDate) { - m_request.cacheTag.expireDate = 0; // January 1, 1970 :) - } - } - - tIt = tokenizer.iterator("etag"); - if (tIt.hasNext()) { - QString prevEtag = m_request.cacheTag.etag; - m_request.cacheTag.etag = toQString(tIt.next()); - if (m_request.cacheTag.etag != prevEtag && m_request.responseCode == 304) { - kDebug(7103) << "304 Not Modified but new entity tag - I don't think this is legal HTTP."; - } - } - - // whoops.. we received a warning - tIt = tokenizer.iterator("warning"); - if (tIt.hasNext()) { - //Don't use warning() here, no need to bother the user. - //Those warnings are mostly about caches. - infoMessage(toQString(tIt.next())); - } - - // Cache management (HTTP 1.0) - tIt = tokenizer.iterator("pragma"); - while (tIt.hasNext()) { - if (tIt.next().toLower().startsWith("no-cache")) { // krazy:exclude=strings - mayCache = false; - hasCacheDirective = true; - } - } - - // The deprecated Refresh Response - tIt = tokenizer.iterator("refresh"); - if (tIt.hasNext()) { - mayCache = false; - setMetaData(QLatin1String("http-refresh"), toQString(tIt.next().trimmed())); - } - - // We don't cache certain text objects - if (m_mimeType.startsWith(QLatin1String("text/")) && (m_mimeType != QLatin1String("text/css")) && - (m_mimeType != QLatin1String("text/x-javascript")) && !hasCacheDirective) { - // Do not cache secure pages or pages - // originating from password protected sites - // unless the webserver explicitly allows it. - if (isUsingSsl() || m_wwwAuth) { - mayCache = false; - } - } - - // note that we've updated cacheTag, so the plan() is with current data - if (m_request.cacheTag.plan(m_maxCacheAge) == CacheTag::ValidateCached) { - kDebug(7113) << "Cache needs validation"; - if (m_request.responseCode == 304) { - kDebug(7113) << "...was revalidated by response code but not by updated expire times. " - "We're going to set the expire date to 60 seconds in the future..."; - m_request.cacheTag.expireDate = currentDate + 60; - if (m_request.cacheTag.policy == CC_Verify && - m_request.cacheTag.plan(m_maxCacheAge) != CacheTag::UseCached) { - // "apparently" because we /could/ have made an error ourselves, but the errors I - // witnessed were all the server's fault. - kDebug(7113) << "this proxy or server apparently sends bogus expiry information."; - } - } - } - - // validation handling - if (mayCache && m_request.responseCode == 200 && !m_mimeType.isEmpty()) { - kDebug(7113) << "Cache, adding" << m_request.url; - // ioMode can still be ReadFromCache here if we're performing a conditional get - // aka validation - m_request.cacheTag.ioMode = WriteToCache; - if (!cacheFileOpenWrite()) { - kDebug(7113) << "Error creating cache entry for " << m_request.url << "!\n"; - } - m_maxCacheSize = config()->readEntry("MaxCacheSize", DEFAULT_MAX_CACHE_SIZE); - } else if (m_request.responseCode == 304 && m_request.cacheTag.file) { - if (!mayCache) { - kDebug(7113) << "This webserver is confused about the cacheability of the data it sends."; - } - // the cache file should still be open for reading, see satisfyRequestFromCache(). - Q_ASSERT(m_request.cacheTag.file->openMode() == QIODevice::ReadOnly); - Q_ASSERT(m_request.cacheTag.ioMode == ReadFromCache); - } else { - cacheFileClose(); - } - - setCacheabilityMetadata(mayCache); -} - -void HTTPProtocol::setCacheabilityMetadata(bool cachingAllowed) -{ - if (!cachingAllowed) { - setMetaData(QLatin1String("no-cache"), QLatin1String("true")); - setMetaData(QLatin1String("expire-date"), QLatin1String("1")); // Expired - } else { - QString tmp; - tmp.setNum(m_request.cacheTag.expireDate); - setMetaData(QLatin1String("expire-date"), tmp); - // slightly changed semantics from old creationDate, probably more correct now - tmp.setNum(m_request.cacheTag.servedDate); - setMetaData(QLatin1String("cache-creation-date"), tmp); - } -} - -bool HTTPProtocol::sendCachedBody() -{ - infoMessage(i18n("Sending data to %1" , m_request.url.host())); - - const qint64 size = m_POSTbuf->size(); - QByteArray cLength ("Content-Length: "); - cLength += QByteArray::number(size); - cLength += "\r\n\r\n"; - - kDebug(7113) << "sending cached data (size=" << size << ")"; - - // Send the content length... - bool sendOk = (write(cLength.data(), cLength.size()) == (ssize_t) cLength.size()); - if (!sendOk) { - kDebug( 7113 ) << "Connection broken when sending " - << "content length: (" << m_request.url.host() << ")"; - error( ERR_CONNECTION_BROKEN, m_request.url.host() ); - return false; - } - - totalSize(size); - // Make sure the read head is at the beginning... - m_POSTbuf->reset(); - KIO::filesize_t totalBytesSent = 0; - - // Send the data... - while (!m_POSTbuf->atEnd()) { - const QByteArray buffer = m_POSTbuf->read(65536); - const ssize_t bytesSent = write(buffer.data(), buffer.size()); - if (bytesSent != static_cast(buffer.size())) { - kDebug(7113) << "Connection broken when sending message body: (" - << m_request.url.host() << ")"; - error( ERR_CONNECTION_BROKEN, m_request.url.host() ); - return false; - } - - totalBytesSent += bytesSent; - processedSize(totalBytesSent); - } - - return true; -} - -bool HTTPProtocol::sendBody() -{ - // If we have cached data, the it is either a repost or a DAV request so send - // the cached data... - if (m_POSTbuf) - return sendCachedBody(); - - if (m_iPostDataSize == NO_SIZE) { - // Try the old approach of retireving content data from the job - // before giving up. - if (retrieveAllData()) - return sendCachedBody(); - - error(ERR_POST_NO_SIZE, m_request.url.host()); - return false; - } - - kDebug(7113) << "sending data (size=" << m_iPostDataSize << ")"; - - infoMessage(i18n("Sending data to %1", m_request.url.host())); - - QByteArray cLength ("Content-Length: "); - cLength += QByteArray::number(m_iPostDataSize); - cLength += "\r\n\r\n"; - - kDebug(7113) << cLength.trimmed(); - - // Send the content length... - bool sendOk = (write(cLength.data(), cLength.size()) == (ssize_t) cLength.size()); - if (!sendOk) { - // The server might have closed the connection due to a timeout, or maybe - // some transport problem arose while the connection was idle. - if (m_request.isKeepAlive) - { - httpCloseConnection(); - return true; // Try again - } - - kDebug(7113) << "Connection broken while sending POST content size to" << m_request.url.host(); - error( ERR_CONNECTION_BROKEN, m_request.url.host() ); - return false; - } - - // Send the amount - totalSize(m_iPostDataSize); - - // If content-length is 0, then do nothing but simply return true. - if (m_iPostDataSize == 0) - return true; - - sendOk = true; - KIO::filesize_t bytesSent = 0; - - while (true) { - dataReq(); - - QByteArray buffer; - const int bytesRead = readData(buffer); - - // On done... - if (bytesRead == 0) { - sendOk = (bytesSent == m_iPostDataSize); - break; - } - - // On error return false... - if (bytesRead < 0) { - error(ERR_ABORTED, m_request.url.host()); - sendOk = false; - break; - } - - // Cache the POST data in case of a repost request. - cachePostData(buffer); - - // This will only happen if transmitting the data fails, so we will simply - // cache the content locally for the potential re-transmit... - if (!sendOk) - continue; - - if (write(buffer.data(), bytesRead) == static_cast(bytesRead)) { - bytesSent += bytesRead; - processedSize(bytesSent); // Send update status... - continue; - } - - kDebug(7113) << "Connection broken while sending POST content to" << m_request.url.host(); - error(ERR_CONNECTION_BROKEN, m_request.url.host()); - sendOk = false; - } - - return sendOk; -} - -void HTTPProtocol::httpClose( bool keepAlive ) -{ - kDebug(7113) << "keepAlive =" << keepAlive; - - cacheFileClose(); - - // Only allow persistent connections for GET requests. - // NOTE: we might even want to narrow this down to non-form - // based submit requests which will require a meta-data from - // khtml. - if (keepAlive) { - if (!m_request.keepAliveTimeout) - m_request.keepAliveTimeout = DEFAULT_KEEP_ALIVE_TIMEOUT; - else if (m_request.keepAliveTimeout > 2*DEFAULT_KEEP_ALIVE_TIMEOUT) - m_request.keepAliveTimeout = 2*DEFAULT_KEEP_ALIVE_TIMEOUT; - - kDebug(7113) << "keep alive (" << m_request.keepAliveTimeout << ")"; - QByteArray data; - QDataStream stream( &data, QIODevice::WriteOnly ); - stream << int(99); // special: Close connection - setTimeoutSpecialCommand(m_request.keepAliveTimeout, data); - - return; - } - - httpCloseConnection(); -} - -void HTTPProtocol::closeConnection() -{ - kDebug(7113); - httpCloseConnection(); -} - -void HTTPProtocol::httpCloseConnection() -{ - kDebug(7113); - m_server.clear(); - disconnectFromHost(); - clearUnreadBuffer(); - setTimeoutSpecialCommand(-1); // Cancel any connection timeout -} - -void HTTPProtocol::slave_status() -{ - kDebug(7113); - - if ( !isConnected() ) - httpCloseConnection(); - - slaveStatus( m_server.url.host(), isConnected() ); -} - -void HTTPProtocol::mimetype( const KUrl& url ) -{ - kDebug(7113) << url; - - if (!maybeSetRequestUrl(url)) - return; - resetSessionSettings(); - - m_request.method = HTTP_HEAD; - m_request.cacheTag.policy= CC_Cache; - - if (proceedUntilResponseHeader()) { - httpClose(m_request.isKeepAlive); - finished(); - } - - kDebug(7113) << m_mimeType; -} - -void HTTPProtocol::special( const QByteArray &data ) -{ - kDebug(7113); - - int tmp; - QDataStream stream(data); - - stream >> tmp; - switch (tmp) { - case 1: // HTTP POST - { - KUrl url; - qint64 size; - stream >> url >> size; - post( url, size ); - break; - } - case 2: // cache_update - { - KUrl url; - bool no_cache; - qint64 expireDate; - stream >> url >> no_cache >> expireDate; - if (no_cache) { - QString filename = cacheFilePathFromUrl(url); - // there is a tiny risk of deleting the wrong file due to hash collisions here. - // this is an unimportant performance issue. - // FIXME on Windows we may be unable to delete the file if open - QFile::remove(filename); - finished(); - break; - } - // let's be paranoid and inefficient here... - HTTPRequest savedRequest = m_request; - - m_request.url = url; - if (cacheFileOpenRead()) { - m_request.cacheTag.expireDate = expireDate; - cacheFileClose(); // this sends an update command to the cache cleaner process - } - - m_request = savedRequest; - finished(); - break; - } - case 5: // WebDAV lock - { - KUrl url; - QString scope, type, owner; - stream >> url >> scope >> type >> owner; - davLock( url, scope, type, owner ); - break; - } - case 6: // WebDAV unlock - { - KUrl url; - stream >> url; - davUnlock( url ); - break; - } - case 7: // Generic WebDAV - { - KUrl url; - int method; - qint64 size; - stream >> url >> method >> size; - davGeneric( url, (KIO::HTTP_METHOD) method, size ); - break; - } - case 99: // Close Connection - { - httpCloseConnection(); - break; - } - default: - // Some command we don't understand. - // Just ignore it, it may come from some future version of KDE. - break; - } -} - -/** - * Read a chunk from the data stream. - */ -int HTTPProtocol::readChunked() -{ - if ((m_iBytesLeft == 0) || (m_iBytesLeft == NO_SIZE)) - { - // discard CRLF from previous chunk, if any, and read size of next chunk - - int bufPos = 0; - m_receiveBuf.resize(4096); - - bool foundCrLf = readDelimitedText(m_receiveBuf.data(), &bufPos, m_receiveBuf.size(), 1); - - if (foundCrLf && bufPos == 2) { - // The previous read gave us the CRLF from the previous chunk. As bufPos includes - // the trailing CRLF it has to be > 2 to possibly include the next chunksize. - bufPos = 0; - foundCrLf = readDelimitedText(m_receiveBuf.data(), &bufPos, m_receiveBuf.size(), 1); - } - if (!foundCrLf) { - kDebug(7113) << "Failed to read chunk header."; - return -1; - } - Q_ASSERT(bufPos > 2); - - long long nextChunkSize = STRTOLL(m_receiveBuf.data(), 0, 16); - if (nextChunkSize < 0) - { - kDebug(7113) << "Negative chunk size"; - return -1; - } - m_iBytesLeft = nextChunkSize; - - kDebug(7113) << "Chunk size =" << m_iBytesLeft << "bytes"; - - if (m_iBytesLeft == 0) - { - // Last chunk; read and discard chunk trailer. - // The last trailer line ends with CRLF and is followed by another CRLF - // so we have CRLFCRLF like at the end of a standard HTTP header. - // Do not miss a CRLFCRLF spread over two of our 4K blocks: keep three previous bytes. - //NOTE the CRLF after the chunksize also counts if there is no trailer. Copy it over. - char trash[4096]; - trash[0] = m_receiveBuf.constData()[bufPos - 2]; - trash[1] = m_receiveBuf.constData()[bufPos - 1]; - int trashBufPos = 2; - bool done = false; - while (!done && !m_isEOF) { - if (trashBufPos > 3) { - // shift everything but the last three bytes out of the buffer - for (int i = 0; i < 3; i++) { - trash[i] = trash[trashBufPos - 3 + i]; - } - trashBufPos = 3; - } - done = readDelimitedText(trash, &trashBufPos, 4096, 2); - } - if (m_isEOF && !done) { - kDebug(7113) << "Failed to read chunk trailer."; - return -1; - } - - return 0; - } - } - - int bytesReceived = readLimited(); - if (!m_iBytesLeft) { - m_iBytesLeft = NO_SIZE; // Don't stop, continue with next chunk - } - return bytesReceived; -} - -int HTTPProtocol::readLimited() -{ - if (!m_iBytesLeft) - return 0; - - m_receiveBuf.resize(4096); - - int bytesToReceive; - if (m_iBytesLeft > KIO::filesize_t(m_receiveBuf.size())) - bytesToReceive = m_receiveBuf.size(); - else - bytesToReceive = m_iBytesLeft; - - const int bytesReceived = readBuffered(m_receiveBuf.data(), bytesToReceive, false); - - if (bytesReceived <= 0) - return -1; // Error: connection lost - - m_iBytesLeft -= bytesReceived; - return bytesReceived; -} - -int HTTPProtocol::readUnlimited() -{ - if (m_request.isKeepAlive) - { - kDebug(7113) << "Unbounded datastream on a Keep-alive connection!"; - m_request.isKeepAlive = false; - } - - m_receiveBuf.resize(4096); - - int result = readBuffered(m_receiveBuf.data(), m_receiveBuf.size()); - if (result > 0) - return result; - - m_isEOF = true; - m_iBytesLeft = 0; - return 0; -} - -void HTTPProtocol::slotData(const QByteArray &_d) -{ - if (!_d.size()) - { - m_isEOD = true; - return; - } - - if (m_iContentLeft != NO_SIZE) - { - if (m_iContentLeft >= KIO::filesize_t(_d.size())) - m_iContentLeft -= _d.size(); - else - m_iContentLeft = NO_SIZE; - } - - QByteArray d = _d; - if ( !m_dataInternal ) - { - // If a broken server does not send the mime-type, - // we try to id it from the content before dealing - // with the content itself. - if ( m_mimeType.isEmpty() && !m_isRedirection && - !( m_request.responseCode >= 300 && m_request.responseCode <=399) ) - { - kDebug(7113) << "Determining mime-type from content..."; - int old_size = m_mimeTypeBuffer.size(); - m_mimeTypeBuffer.resize( old_size + d.size() ); - memcpy( m_mimeTypeBuffer.data() + old_size, d.data(), d.size() ); - if ( (m_iBytesLeft != NO_SIZE) && (m_iBytesLeft > 0) - && (m_mimeTypeBuffer.size() < 1024) ) - { - m_cpMimeBuffer = true; - return; // Do not send up the data since we do not yet know its mimetype! - } - - kDebug(7113) << "Mimetype buffer size:" << m_mimeTypeBuffer.size(); - - KMimeType::Ptr mime = KMimeType::findByNameAndContent(m_request.url.fileName(), m_mimeTypeBuffer); - if( mime && !mime->isDefault() ) - { - m_mimeType = mime->name(); - kDebug(7113) << "Mimetype from content:" << m_mimeType; - } - - if ( m_mimeType.isEmpty() ) - { - m_mimeType = QLatin1String( DEFAULT_MIME_TYPE ); - kDebug(7113) << "Using default mimetype:" << m_mimeType; - } - - //### we could also open the cache file here - - if ( m_cpMimeBuffer ) - { - d.resize(0); - d.resize(m_mimeTypeBuffer.size()); - memcpy(d.data(), m_mimeTypeBuffer.data(), d.size()); - } - mimeType(m_mimeType); - m_mimeTypeBuffer.resize(0); - } - - //kDebug(7113) << "Sending data of size" << d.size(); - data( d ); - if (m_request.cacheTag.ioMode == WriteToCache) { - cacheFileWritePayload(d); - } - } - else - { - uint old_size = m_webDavDataBuf.size(); - m_webDavDataBuf.resize (old_size + d.size()); - memcpy (m_webDavDataBuf.data() + old_size, d.data(), d.size()); - } -} - -/** - * This function is our "receive" function. It is responsible for - * downloading the message (not the header) from the HTTP server. It - * is called either as a response to a client's KIOJob::dataEnd() - * (meaning that the client is done sending data) or by 'sendQuery()' - * (if we are in the process of a PUT/POST request). It can also be - * called by a webDAV function, to receive stat/list/property/etc. - * data; in this case the data is stored in m_webDavDataBuf. - */ -bool HTTPProtocol::readBody( bool dataInternal /* = false */ ) -{ - // special case for reading cached body since we also do it in this function. oh well. - if (!canHaveResponseBody(m_request.responseCode, m_request.method) && - !(m_request.cacheTag.ioMode == ReadFromCache && m_request.responseCode == 304 && - m_request.method != HTTP_HEAD)) { - return true; - } - - m_isEOD = false; - // Note that when dataInternal is true, we are going to: - // 1) save the body data to a member variable, m_webDavDataBuf - // 2) _not_ advertise the data, speed, size, etc., through the - // corresponding functions. - // This is used for returning data to WebDAV. - m_dataInternal = dataInternal; - if (dataInternal) { - m_webDavDataBuf.clear(); - } - - // Check if we need to decode the data. - // If we are in copy mode, then use only transfer decoding. - bool useMD5 = !m_contentMD5.isEmpty(); - - // Deal with the size of the file. - KIO::filesize_t sz = m_request.offset; - if ( sz ) - m_iSize += sz; - - if (!m_isRedirection) { - // Update the application with total size except when - // it is compressed, or when the data is to be handled - // internally (webDAV). If compressed we have to wait - // until we uncompress to find out the actual data size - if ( !dataInternal ) { - if ((m_iSize > 0) && (m_iSize != NO_SIZE)) { - totalSize(m_iSize); - infoMessage(i18n("Retrieving %1 from %2...", KIO::convertSize(m_iSize), - m_request.url.host())); - } else { - totalSize(0); - } - } - - if (m_request.cacheTag.ioMode == ReadFromCache) { - kDebug(7113) << "reading data from cache..."; - - m_iContentLeft = NO_SIZE; - - QByteArray d; - while (true) { - d = cacheFileReadPayload(MAX_IPC_SIZE); - if (d.isEmpty()) { - break; - } - slotData(d); - sz += d.size(); - if (!dataInternal) { - processedSize(sz); - } - } - - m_receiveBuf.resize(0); - - if (!dataInternal) { - data(QByteArray()); - } - - return true; - } - } - - if (m_iSize != NO_SIZE) - m_iBytesLeft = m_iSize - sz; - else - m_iBytesLeft = NO_SIZE; - - m_iContentLeft = m_iBytesLeft; - - if (m_isChunked) - m_iBytesLeft = NO_SIZE; - - kDebug(7113) << KIO::number(m_iBytesLeft) << "bytes left."; - - // Main incoming loop... Gather everything while we can... - m_cpMimeBuffer = false; - m_mimeTypeBuffer.resize(0); - - HTTPFilterChain chain; - - // redirection ignores the body - if (!m_isRedirection) { - QObject::connect(&chain, SIGNAL(output(QByteArray)), - this, SLOT(slotData(QByteArray))); - } - QObject::connect(&chain, SIGNAL(error(QString)), - this, SLOT(slotFilterError(QString))); - - // decode all of the transfer encodings - while (!m_transferEncodings.isEmpty()) - { - QString enc = m_transferEncodings.takeLast(); - if ( enc == QLatin1String("gzip") ) - chain.addFilter(new HTTPFilterGZip); - else if ( enc == QLatin1String("deflate") ) - chain.addFilter(new HTTPFilterDeflate); - } - - // From HTTP 1.1 Draft 6: - // The MD5 digest is computed based on the content of the entity-body, - // including any content-coding that has been applied, but not including - // any transfer-encoding applied to the message-body. If the message is - // received with a transfer-encoding, that encoding MUST be removed - // prior to checking the Content-MD5 value against the received entity. - HTTPFilterMD5 *md5Filter = 0; - if ( useMD5 ) - { - md5Filter = new HTTPFilterMD5; - chain.addFilter(md5Filter); - } - - // now decode all of the content encodings - // -- Why ?? We are not - // -- a proxy server, be a client side implementation!! The applications - // -- are capable of determinig how to extract the encoded implementation. - // WB: That's a misunderstanding. We are free to remove the encoding. - // WB: Some braindead www-servers however, give .tgz files an encoding - // WB: of "gzip" (or even "x-gzip") and a content-type of "applications/tar" - // WB: They shouldn't do that. We can work around that though... - while (!m_contentEncodings.isEmpty()) - { - QString enc = m_contentEncodings.takeLast(); - if ( enc == QLatin1String("gzip") ) - chain.addFilter(new HTTPFilterGZip); - else if ( enc == QLatin1String("deflate") ) - chain.addFilter(new HTTPFilterDeflate); - } - - while (!m_isEOF) - { - int bytesReceived; - - if (m_isChunked) - bytesReceived = readChunked(); - else if (m_iSize != NO_SIZE) - bytesReceived = readLimited(); - else - bytesReceived = readUnlimited(); - - // make sure that this wasn't an error, first - // kDebug(7113) << "bytesReceived:" - // << (int) bytesReceived << " m_iSize:" << (int) m_iSize << " Chunked:" - // << m_isChunked << " BytesLeft:"<< (int) m_iBytesLeft; - if (bytesReceived == -1) - { - if (m_iContentLeft == 0) - { - // gzip'ed data sometimes reports a too long content-length. - // (The length of the unzipped data) - m_iBytesLeft = 0; - break; - } - // Oh well... log an error and bug out - kDebug(7113) << "bytesReceived==-1 sz=" << (int)sz - << " Connection broken !"; - error(ERR_CONNECTION_BROKEN, m_request.url.host()); - return false; - } - - // I guess that nbytes == 0 isn't an error.. but we certainly - // won't work with it! - if (bytesReceived > 0) - { - // Important: truncate the buffer to the actual size received! - // Otherwise garbage will be passed to the app - m_receiveBuf.truncate( bytesReceived ); - - chain.slotInput(m_receiveBuf); - - if (m_iError) - return false; - - sz += bytesReceived; - if (!dataInternal) - processedSize( sz ); - } - m_receiveBuf.resize(0); // res - - if (m_iBytesLeft && m_isEOD && !m_isChunked) - { - // gzip'ed data sometimes reports a too long content-length. - // (The length of the unzipped data) - m_iBytesLeft = 0; - } - - if (m_iBytesLeft == 0) - { - kDebug(7113) << "EOD received! Left ="<< KIO::number(m_iBytesLeft); - break; - } - } - chain.slotInput(QByteArray()); // Flush chain. - - if ( useMD5 ) - { - QString calculatedMD5 = md5Filter->md5(); - - if ( m_contentMD5 != calculatedMD5 ) - kWarning(7113) << "MD5 checksum MISMATCH! Expected:" - << calculatedMD5 << ", Got:" << m_contentMD5; - } - - // Close cache entry - if (m_iBytesLeft == 0) { - cacheFileClose(); // no-op if not necessary - } - - if (!dataInternal && sz <= 1) - { - if (m_request.responseCode >= 500 && m_request.responseCode <= 599) { - error(ERR_INTERNAL_SERVER, m_request.url.host()); - return false; - } else if (m_request.responseCode >= 400 && m_request.responseCode <= 499 && - !isAuthenticationRequired(m_request.responseCode)) { - error(ERR_DOES_NOT_EXIST, m_request.url.host()); - return false; - } - } - - if (!dataInternal && !m_isRedirection) - data( QByteArray() ); - - return true; -} - -void HTTPProtocol::slotFilterError(const QString &text) -{ - error(KIO::ERR_SLAVE_DEFINED, text); -} - -void HTTPProtocol::error( int _err, const QString &_text ) -{ - // Close the connection only on connection errors. Otherwise, honor the - // keep alive flag. - if (_err == ERR_CONNECTION_BROKEN || _err == ERR_COULD_NOT_CONNECT) - httpClose(false); - else - httpClose(m_request.isKeepAlive); - - if (!m_request.id.isEmpty()) - { - forwardHttpResponseHeader(); - sendMetaData(); - } - - // It's over, we don't need it anymore - clearPostDataBuffer(); - - SlaveBase::error( _err, _text ); - m_iError = _err; -} - - -void HTTPProtocol::addCookies( const QString &url, const QByteArray &cookieHeader ) -{ - qlonglong windowId = m_request.windowId.toLongLong(); - QDBusInterface kcookiejar( QLatin1String("org.kde.kded"), QLatin1String("/modules/kcookiejar"), QLatin1String("org.kde.KCookieServer") ); - (void)kcookiejar.call( QDBus::NoBlock, QLatin1String("addCookies"), url, - cookieHeader, windowId ); -} - -QString HTTPProtocol::findCookies( const QString &url) -{ - qlonglong windowId = m_request.windowId.toLongLong(); - QDBusInterface kcookiejar( QLatin1String("org.kde.kded"), QLatin1String("/modules/kcookiejar"), QLatin1String("org.kde.KCookieServer") ); - QDBusReply reply = kcookiejar.call( QLatin1String("findCookies"), url, windowId ); - - if ( !reply.isValid() ) - { - kWarning(7113) << "Can't communicate with kded_kcookiejar!"; - return QString(); - } - return reply; -} - -/******************************* CACHING CODE ****************************/ - -HTTPProtocol::CacheTag::CachePlan HTTPProtocol::CacheTag::plan(int maxCacheAge) const -{ - //notable omission: we're not checking cache file presence or integrity - switch (policy) { - case KIO::CC_Refresh: - // Conditional GET requires the presence of either an ETag or - // last modified date. - if (lastModifiedDate != -1 || !etag.isEmpty()) { - return ValidateCached; - } - break; - case KIO::CC_Reload: - return IgnoreCached; - case KIO::CC_CacheOnly: - case KIO::CC_Cache: - return UseCached; - default: - break; - } - - Q_ASSERT((policy == CC_Verify || policy == CC_Refresh)); - qint64 currentDate = QDateTime::currentMSecsSinceEpoch()/1000; - if ((servedDate != -1 && currentDate > (servedDate + maxCacheAge)) || - (expireDate != -1 && currentDate > expireDate)) { - return ValidateCached; - } - return UseCached; -} - -// !START SYNC! -// The following code should be kept in sync -// with the code in http_cache_cleaner.cpp - -// we use QDataStream; this is just an illustration -struct BinaryCacheFileHeader -{ - quint8 version[2]; - quint8 compression; // for now fixed to 0 - quint8 reserved; // for now; also alignment - qint32 useCount; - qint64 servedDate; - qint64 lastModifiedDate; - qint64 expireDate; - qint32 bytesCached; - // packed size should be 36 bytes; we explicitly set it here to make sure that no compiler - // padding ruins it. We write the fields to disk without any padding. - static const int size = 36; -}; - -enum CacheCleanerCommandCode { - InvalidCommand = 0, - CreateFileNotificationCommand, - UpdateFileCommand -}; - -// illustration for cache cleaner update "commands" -struct CacheCleanerCommand -{ - BinaryCacheFileHeader header; - quint32 commandCode; - // filename in ASCII, binary isn't worth the coding and decoding - quint8 filename[s_hashedUrlNibbles]; -}; - -QByteArray HTTPProtocol::CacheTag::serialize() const -{ - QByteArray ret; - QDataStream stream(&ret, QIODevice::WriteOnly); - stream << quint8('A'); - stream << quint8('\n'); - stream << quint8(0); - stream << quint8(0); - - stream << fileUseCount; - stream << servedDate; - stream << lastModifiedDate; - stream << expireDate; - - stream << bytesCached; - Q_ASSERT(ret.size() == BinaryCacheFileHeader::size); - return ret; -} - -static bool compareByte(QDataStream *stream, quint8 value) -{ - quint8 byte; - *stream >> byte; - return byte == value; -} - -// If starting a new file cacheFileWriteVariableSizeHeader() must have been called *before* -// calling this! This is to fill in the headerEnd field. -// If the file is not new headerEnd has already been read from the file and in fact the variable -// size header *may* not be rewritten because a size change would mess up the file layout. -bool HTTPProtocol::CacheTag::deserialize(const QByteArray &d) -{ - if (d.size() != BinaryCacheFileHeader::size) { - return false; - } - QDataStream stream(d); - - bool ok = true; - ok = ok && compareByte(&stream, 'A'); - ok = ok && compareByte(&stream, '\n'); - ok = ok && compareByte(&stream, 0); - ok = ok && compareByte(&stream, 0); - if (!ok) { - return false; - } - - stream >> fileUseCount; - stream >> servedDate; - stream >> lastModifiedDate; - stream >> expireDate; - stream >> bytesCached; - - return true; -} - -/* Text part of the header, directly following the binary first part: -URL\n -etag\n -mimetype\n -header line\n -header line\n -... -\n -*/ - -static KUrl storableUrl(const KUrl &url) -{ - KUrl ret(url); - ret.setPassword(QString()); - ret.setFragment(QString()); - return ret; -} - -static void writeLine(QIODevice *dev, const QByteArray &line) -{ - static const char linefeed = '\n'; - dev->write(line); - dev->write(&linefeed, 1); -} - -void HTTPProtocol::cacheFileWriteTextHeader() -{ - QFile *&file = m_request.cacheTag.file; - Q_ASSERT(file); - Q_ASSERT(file->openMode() & QIODevice::WriteOnly); - - file->seek(BinaryCacheFileHeader::size); - writeLine(file, storableUrl(m_request.url).toEncoded()); - writeLine(file, m_request.cacheTag.etag.toLatin1()); - writeLine(file, m_mimeType.toLatin1()); - writeLine(file, m_responseHeaders.join(QString(QLatin1Char('\n'))).toLatin1()); - // join("\n") adds no \n to the end, but writeLine() does. - // Add another newline to mark the end of text. - writeLine(file, QByteArray()); -} - -static bool readLineChecked(QIODevice *dev, QByteArray *line) -{ - *line = dev->readLine(MAX_IPC_SIZE); - // 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; -} - -bool HTTPProtocol::cacheFileReadTextHeader1(const KUrl &desiredUrl) -{ - QFile *&file = m_request.cacheTag.file; - Q_ASSERT(file); - Q_ASSERT(file->openMode() == QIODevice::ReadOnly); - - QByteArray readBuf; - bool ok = readLineChecked(file, &readBuf); - if (storableUrl(desiredUrl).toEncoded() != readBuf) { - kDebug(7103) << "You have witnessed a very improbable hash collision!"; - return false; - } - - ok = ok && readLineChecked(file, &readBuf); - m_request.cacheTag.etag = toQString(readBuf); - - return ok; -} - -bool HTTPProtocol::cacheFileReadTextHeader2() -{ - QFile *&file = m_request.cacheTag.file; - Q_ASSERT(file); - Q_ASSERT(file->openMode() == QIODevice::ReadOnly); - - bool ok = true; - QByteArray readBuf; -#ifndef NDEBUG - // we assume that the URL and etag have already been read - qint64 oldPos = file->pos(); - file->seek(BinaryCacheFileHeader::size); - ok = ok && readLineChecked(file, &readBuf); - ok = ok && readLineChecked(file, &readBuf); - Q_ASSERT(file->pos() == oldPos); -#endif - ok = ok && readLineChecked(file, &readBuf); - m_mimeType = toQString(readBuf); - - m_responseHeaders.clear(); - // read as long as no error and no empty line found - while (true) { - ok = ok && readLineChecked(file, &readBuf); - if (ok && !readBuf.isEmpty()) { - m_responseHeaders.append(toQString(readBuf)); - } else { - break; - } - } - return ok; // it may still be false ;) -} - -static QString filenameFromUrl(const KUrl &url) -{ - QCryptographicHash hash(QCryptographicHash::Sha1); - hash.addData(storableUrl(url).toEncoded()); - return toQString(hash.result().toHex()); -} - -QString HTTPProtocol::cacheFilePathFromUrl(const KUrl &url) const -{ - QString filePath = m_strCacheDir; - if (!filePath.endsWith(QLatin1Char('/'))) { - filePath.append(QLatin1Char('/')); - } - filePath.append(filenameFromUrl(url)); - return filePath; -} - -bool HTTPProtocol::cacheFileOpenRead() -{ - kDebug(7113); - QString filename = cacheFilePathFromUrl(m_request.url); - - QFile *&file = m_request.cacheTag.file; - if (file) { - kDebug(7113) << "File unexpectedly open; old file is" << file->fileName() - << "new name is" << filename; - Q_ASSERT(file->fileName() == filename); - } - Q_ASSERT(!file); - file = new QFile(filename); - if (file->open(QIODevice::ReadOnly)) { - QByteArray header = file->read(BinaryCacheFileHeader::size); - if (!m_request.cacheTag.deserialize(header)) { - kDebug(7103) << "Cache file header is invalid."; - - file->close(); - } - } - - if (file->isOpen() && !cacheFileReadTextHeader1(m_request.url)) { - file->close(); - } - - if (!file->isOpen()) { - cacheFileClose(); - return false; - } - return true; -} - - -bool HTTPProtocol::cacheFileOpenWrite() -{ - kDebug(7113); - QString filename = cacheFilePathFromUrl(m_request.url); - - // if we open a cache file for writing while we have a file open for reading we must have - // found out that the old cached content is obsolete, so delete the file. - QFile *&file = m_request.cacheTag.file; - if (file) { - // ensure that the file is in a known state - either open for reading or null - Q_ASSERT(!qobject_cast(file)); - Q_ASSERT((file->openMode() & QIODevice::WriteOnly) == 0); - Q_ASSERT(file->fileName() == filename); - kDebug(7113) << "deleting expired cache entry and recreating."; - file->remove(); - delete file; - file = 0; - } - - // note that QTemporaryFile will automatically append random chars to filename - file = new QTemporaryFile(filename); - file->open(QIODevice::WriteOnly); - - // if we have started a new file we have not initialized some variables from disk data. - m_request.cacheTag.fileUseCount = 0; // the file has not been *read* yet - m_request.cacheTag.bytesCached = 0; - - if ((file->openMode() & QIODevice::WriteOnly) == 0) { - kDebug(7113) << "Could not open file for writing:" << file->fileName() - << "due to error" << file->error(); - cacheFileClose(); - return false; - } - return true; -} - -static QByteArray makeCacheCleanerCommand(const HTTPProtocol::CacheTag &cacheTag, - CacheCleanerCommandCode cmd) -{ - QByteArray ret = cacheTag.serialize(); - QDataStream stream(&ret, QIODevice::WriteOnly); - - stream.skipRawData(BinaryCacheFileHeader::size); - // append the command code - stream << quint32(cmd); - // append the filename - QString fileName = cacheTag.file->fileName(); - int basenameStart = fileName.lastIndexOf(QLatin1Char('/')) + 1; - QByteArray baseName = fileName.mid(basenameStart, s_hashedUrlNibbles).toLatin1(); - stream.writeRawData(baseName.constData(), baseName.size()); - - Q_ASSERT(ret.size() == BinaryCacheFileHeader::size + sizeof(quint32) + s_hashedUrlNibbles); - return ret; -} - -//### not yet 100% sure when and when not to call this -void HTTPProtocol::cacheFileClose() -{ - kDebug(7113); - - QFile *&file = m_request.cacheTag.file; - if (!file) { - return; - } - - m_request.cacheTag.ioMode = NoCache; - - QByteArray ccCommand; - QTemporaryFile *tempFile = qobject_cast(file); - - if (file->openMode() & QIODevice::WriteOnly) { - Q_ASSERT(tempFile); - - if (m_request.cacheTag.bytesCached && !m_iError) { - QByteArray header = m_request.cacheTag.serialize(); - tempFile->seek(0); - tempFile->write(header); - - ccCommand = makeCacheCleanerCommand(m_request.cacheTag, CreateFileNotificationCommand); - } else { - // oh, we've never written payload data to the cache file. - // the temporary file is closed and removed and no proper cache entry is created. - } - } else if (file->openMode() == QIODevice::ReadOnly) { - Q_ASSERT(!tempFile); - ccCommand = makeCacheCleanerCommand(m_request.cacheTag, UpdateFileCommand); - } - delete file; - file = 0; - - if (!ccCommand.isEmpty()) { - sendCacheCleanerCommand(ccCommand); - } -} - -void HTTPProtocol::sendCacheCleanerCommand(const QByteArray &command) -{ - kDebug(7113); - Q_ASSERT(command.size() == BinaryCacheFileHeader::size + s_hashedUrlNibbles + sizeof(quint32)); - int attempts = 0; - while (m_cacheCleanerConnection.state() != QLocalSocket::ConnectedState && attempts < 6) { - if (attempts == 2) { - KToolInvocation::startServiceByDesktopPath(QLatin1String("http_cache_cleaner.desktop")); - } - QString socketFileName = KStandardDirs::locateLocal("socket", QLatin1String("kio_http_cache_cleaner")); - m_cacheCleanerConnection.connectToServer(socketFileName, QIODevice::WriteOnly); - m_cacheCleanerConnection.waitForConnected(1500); - attempts++; - } - - if (m_cacheCleanerConnection.state() == QLocalSocket::ConnectedState) { - m_cacheCleanerConnection.write(command); - m_cacheCleanerConnection.flush(); - } else { - // updating the stats is not vital, so we just give up. - kDebug(7113) << "Could not connect to cache cleaner, not updating stats of this cache file."; - } -} - -QByteArray HTTPProtocol::cacheFileReadPayload(int maxLength) -{ - Q_ASSERT(m_request.cacheTag.file); - Q_ASSERT(m_request.cacheTag.ioMode == ReadFromCache); - Q_ASSERT(m_request.cacheTag.file->openMode() == QIODevice::ReadOnly); - QByteArray ret = m_request.cacheTag.file->read(maxLength); - if (ret.isEmpty()) { - cacheFileClose(); - } - return ret; -} - - -void HTTPProtocol::cacheFileWritePayload(const QByteArray &d) -{ - if (!m_request.cacheTag.file) { - return; - } - - // If the file being downloaded is so big that it exceeds the max cache size, - // do not cache it! See BR# 244215. NOTE: this can be improved upon in the - // future... - if (m_iSize >= KIO::filesize_t(m_maxCacheSize * 1024)) { - kDebug(7113) << "Caching disabled because content size is too big."; - cacheFileClose(); - return; - } - - Q_ASSERT(m_request.cacheTag.ioMode == WriteToCache); - Q_ASSERT(m_request.cacheTag.file->openMode() & QIODevice::WriteOnly); - - if (d.isEmpty()) { - cacheFileClose(); - } - - //TODO: abort if file grows too big! - - // write the variable length text header as soon as we start writing to the file - if (!m_request.cacheTag.bytesCached) { - cacheFileWriteTextHeader(); - } - m_request.cacheTag.bytesCached += d.size(); - m_request.cacheTag.file->write(d); -} - -void HTTPProtocol::cachePostData(const QByteArray& data) -{ - if (!m_POSTbuf) { - m_POSTbuf = createPostBufferDeviceFor(qMax(m_iPostDataSize, static_cast(data.size()))); - if (!m_POSTbuf) - return; - } - - m_POSTbuf->write (data.constData(), data.size()); -} - -void HTTPProtocol::clearPostDataBuffer() -{ - if (!m_POSTbuf) - return; - - delete m_POSTbuf; - m_POSTbuf = 0; -} - -bool HTTPProtocol::retrieveAllData() +HttpProtocol::HttpProtocol( const QByteArray &pool, const QByteArray &app ) + : SlaveBase( "http", pool, app ) { - if (!m_POSTbuf) { - m_POSTbuf = createPostBufferDeviceFor(s_MaxInMemPostBufSize + 1); - } - - if (!m_POSTbuf) { - error (ERR_OUT_OF_MEMORY, m_request.url.host()); - return false; - } - - while (true) { - dataReq(); - QByteArray buffer; - const int bytesRead = readData(buffer); - - if (bytesRead < 0) { - error(ERR_ABORTED, m_request.url.host()); - return false; - } - - if (bytesRead == 0) { - break; - } - - m_POSTbuf->write(buffer.constData(), buffer.size()); - } - - return true; -} - -// The above code should be kept in sync -// with the code in http_cache_cleaner.cpp -// !END SYNC! - -//************************** AUTHENTICATION CODE ********************/ - -QString HTTPProtocol::authenticationHeader() -{ - QByteArray ret; - - // If the internal meta-data "cached-www-auth" is set, then check for cached - // authentication data and preemtively send the authentication header if a - // matching one is found. - if (!m_wwwAuth && config()->readEntry("cached-www-auth", false)) { - KIO::AuthInfo authinfo; - authinfo.url = m_request.url; - authinfo.realmValue = config()->readEntry("www-auth-realm", QString()); - // If no relam metadata, then make sure path matching is turned on. - authinfo.verifyPath = (authinfo.realmValue.isEmpty()); - - const bool useCachedAuth = (m_request.responseCode == 401 || !config()->readEntry("no-preemptive-auth-reuse", false)); - - if (useCachedAuth && checkCachedAuthentication(authinfo)) { - const QByteArray cachedChallenge = config()->readEntry("www-auth-challenge", QByteArray()); - if (!cachedChallenge.isEmpty()) { - m_wwwAuth = KAbstractHttpAuthentication::newAuth(cachedChallenge, config()); - if (m_wwwAuth) { - kDebug(7113) << "creating www authentcation header from cached info"; - m_wwwAuth->setChallenge(cachedChallenge, m_request.url, m_request.sentMethodString); - m_wwwAuth->generateResponse(authinfo.username, authinfo.password); - } - } - } - } - - // If the internal meta-data "cached-proxy-auth" is set, then check for cached - // authentication data and preemtively send the authentication header if a - // matching one is found. - if (!m_proxyAuth && config()->readEntry("cached-proxy-auth", false)) { - KIO::AuthInfo authinfo; - authinfo.url = m_request.proxyUrl; - authinfo.realmValue = config()->readEntry("proxy-auth-realm", QString()); - // If no relam metadata, then make sure path matching is turned on. - authinfo.verifyPath = (authinfo.realmValue.isEmpty()); - - if (checkCachedAuthentication(authinfo)) { - const QByteArray cachedChallenge = config()->readEntry("proxy-auth-challenge", QByteArray()); - if (!cachedChallenge.isEmpty()) { - m_proxyAuth = KAbstractHttpAuthentication::newAuth(cachedChallenge, config()); - if (m_proxyAuth) { - kDebug(7113) << "creating proxy authentcation header from cached info"; - m_proxyAuth->setChallenge(cachedChallenge, m_request.proxyUrl, m_request.sentMethodString); - m_proxyAuth->generateResponse(authinfo.username, authinfo.password); - } - } - } - } - - // the authentication classes don't know if they are for proxy or webserver authentication... - if (m_wwwAuth && !m_wwwAuth->isError()) { - ret += "Authorization: "; - ret += m_wwwAuth->headerFragment(); - } - - if (m_proxyAuth && !m_proxyAuth->isError()) { - ret += "Proxy-Authorization: "; - ret += m_proxyAuth->headerFragment(); - } - - return toQString(ret); // ## encoding ok? -} - -static QString protocolForProxyType(QNetworkProxy::ProxyType type) -{ - switch (type) { - case QNetworkProxy::DefaultProxy: - break; - case QNetworkProxy::Socks5Proxy: - return QLatin1String("socks"); - case QNetworkProxy::NoProxy: - break; - case QNetworkProxy::HttpProxy: - case QNetworkProxy::HttpCachingProxy: - case QNetworkProxy::FtpCachingProxy: - default: - break; - } - - return QLatin1String("http"); -} - -void HTTPProtocol::proxyAuthenticationForSocket(const QNetworkProxy &proxy, QAuthenticator *authenticator) -{ - kDebug(7113) << "realm:" << authenticator->realm() << "user:" << authenticator->user(); - - // Set the proxy URL... - m_request.proxyUrl.setProtocol(protocolForProxyType(proxy.type())); - m_request.proxyUrl.setUser(proxy.user()); - m_request.proxyUrl.setHost(proxy.hostName()); - m_request.proxyUrl.setPort(proxy.port()); - - AuthInfo info; - info.url = m_request.proxyUrl; - info.realmValue = authenticator->realm(); - info.username = authenticator->user(); - info.verifyPath = info.realmValue.isEmpty(); - - const bool haveCachedCredentials = checkCachedAuthentication(info); - const bool retryAuth = (m_socketProxyAuth != 0); - - // if m_socketProxyAuth is a valid pointer then authentication has been attempted before, - // and it was not successful. see below and saveProxyAuthenticationForSocket(). - if (!haveCachedCredentials || retryAuth) { - // Save authentication info if the connection succeeds. We need to disconnect - // this after saving the auth data (or an error) so we won't save garbage afterwards! - connect(socket(), SIGNAL(connected()), - this, SLOT(saveProxyAuthenticationForSocket())); - //### fillPromptInfo(&info); - info.prompt = i18n("You need to supply a username and a password for " - "the proxy server listed below before you are allowed " - "to access any sites."); - info.keepPassword = true; - info.commentLabel = i18n("Proxy:"); - info.comment = i18n("%1 at %2", htmlEscape(info.realmValue), m_request.proxyUrl.host()); - - const QString errMsg ((retryAuth ? i18n("Proxy Authentication Failed.") : QString())); - - if (!openPasswordDialog(info, errMsg)) { - kDebug(7113) << "looks like the user canceled proxy authentication."; - error(ERR_USER_CANCELED, m_request.proxyUrl.host()); - delete m_proxyAuth; - m_proxyAuth = 0; - return; - } - } - authenticator->setUser(info.username); - authenticator->setPassword(info.password); - authenticator->setOption(QLatin1String("keepalive"), info.keepPassword); - - if (m_socketProxyAuth) { - *m_socketProxyAuth = *authenticator; - } else { - m_socketProxyAuth = new QAuthenticator(*authenticator); - } - - if (!m_request.proxyUrl.user().isEmpty()) { - m_request.proxyUrl.setUser(info.username); - } } -void HTTPProtocol::saveProxyAuthenticationForSocket() +HttpProtocol::~HttpProtocol() { - kDebug(7113) << "Saving authenticator"; - disconnect(socket(), SIGNAL(connected()), - this, SLOT(saveProxyAuthenticationForSocket())); - Q_ASSERT(m_socketProxyAuth); - if (m_socketProxyAuth) { - kDebug(7113) << "realm:" << m_socketProxyAuth->realm() << "user:" << m_socketProxyAuth->user(); - KIO::AuthInfo a; - a.verifyPath = true; - a.url = m_request.proxyUrl; - a.realmValue = m_socketProxyAuth->realm(); - a.username = m_socketProxyAuth->user(); - a.password = m_socketProxyAuth->password(); - a.keepPassword = m_socketProxyAuth->option(QLatin1String("keepalive")).toBool(); - cacheAuthentication(a); - } - delete m_socketProxyAuth; - m_socketProxyAuth = 0; + kDebug(7103); } -void HTTPProtocol::saveAuthenticationData() +void HttpProtocol::setHost( const QString& host, quint16 port, const QString& user, const QString& pass ) { - KIO::AuthInfo authinfo; - bool alreadyCached = false; - KAbstractHttpAuthentication *auth = 0; - switch (m_request.prevResponseCode) { - case 401: - auth = m_wwwAuth; - alreadyCached = config()->readEntry("cached-www-auth", false); - break; - case 407: - auth = m_proxyAuth; - alreadyCached = config()->readEntry("cached-proxy-auth", false); - break; - default: - Q_ASSERT(false); // should never happen! - } - - // Prevent recaching of the same credentials over and over again. - if (auth && (!auth->realm().isEmpty() || !alreadyCached)) { - auth->fillKioAuthInfo(&authinfo); - if (auth == m_wwwAuth) { - setMetaData(QLatin1String("{internal~currenthost}cached-www-auth"), QLatin1String("true")); - if (!authinfo.realmValue.isEmpty()) - setMetaData(QLatin1String("{internal~currenthost}www-auth-realm"), authinfo.realmValue); - if (!authinfo.digestInfo.isEmpty()) - setMetaData(QLatin1String("{internal~currenthost}www-auth-challenge"), authinfo.digestInfo); - } else { - setMetaData(QLatin1String("{internal~allhosts}cached-proxy-auth"), QLatin1String("true")); - if (!authinfo.realmValue.isEmpty()) - setMetaData(QLatin1String("{internal~allhosts}proxy-auth-realm"), authinfo.realmValue); - if (!authinfo.digestInfo.isEmpty()) - setMetaData(QLatin1String("{internal~allhosts}proxy-auth-challenge"), authinfo.digestInfo); - } - - kDebug(7113) << "Cache authentication info ?" << authinfo.keepPassword; + Q_UNUSED(port); + Q_UNUSED(user); + Q_UNUSED(pass); - if (authinfo.keepPassword) { - cacheAuthentication(authinfo); - kDebug(7113) << "Cached authentication for" << m_request.url; - } - } - // Update our server connection state which includes www and proxy username and password. - m_server.updateCredentials(m_request); + kDebug(7103); + error(KIO::ERR_UNSUPPORTED_ACTION, host); } -bool HTTPProtocol::handleAuthenticationHeader(const HeaderTokenizer* tokenizer) +void HttpProtocol::stat( const KUrl &url ) { - KIO::AuthInfo authinfo; - QList authTokens; - KAbstractHttpAuthentication **auth; - - if (m_request.responseCode == 401) { - auth = &m_wwwAuth; - authTokens = tokenizer->iterator("www-authenticate").all(); - authinfo.url = m_request.url; - authinfo.username = m_server.url.user(); - authinfo.prompt = i18n("You need to supply a username and a " - "password to access this site."); - authinfo.commentLabel = i18n("Site:"); - } else { - // make sure that the 407 header hasn't escaped a lower layer when it shouldn't. - // this may break proxy chains which were never tested anyway, and AFAIK they are - // rare to nonexistent in the wild. - Q_ASSERT(QNetworkProxy::applicationProxy().type() == QNetworkProxy::NoProxy); - auth = &m_proxyAuth; - authTokens = tokenizer->iterator("proxy-authenticate").all(); - authinfo.url = m_request.proxyUrl; - authinfo.username = m_request.proxyUrl.user(); - authinfo.prompt = i18n("You need to supply a username and a password for " - "the proxy server listed below before you are allowed " - "to access any sites." ); - authinfo.commentLabel = i18n("Proxy:"); - } - - bool authRequiresAnotherRoundtrip = false; - - // Workaround brain dead server responses that violate the spec and - // incorrectly return a 401/407 without the required WWW/Proxy-Authenticate - // header fields. See bug 215736... - if (!authTokens.isEmpty()) { - QString errorMsg; - authRequiresAnotherRoundtrip = true; - - if (m_request.responseCode == m_request.prevResponseCode && *auth) { - // Authentication attempt failed. Retry... - if ((*auth)->wasFinalStage()) { - errorMsg = (m_request.responseCode == 401 ? - i18n("Authentication Failed.") : - i18n("Proxy Authentication Failed.")); - delete *auth; - *auth = 0; - } else { // Create authentication header - // WORKAROUND: The following piece of code prevents brain dead IIS - // servers that send back multiple "WWW-Authenticate" headers from - // screwing up our authentication logic during the challenge - // phase (Type 2) of NTLM authenticaiton. - QMutableListIterator it (authTokens); - const QByteArray authScheme ((*auth)->scheme().trimmed()); - while (it.hasNext()) { - if (qstrnicmp(authScheme.constData(), it.next().constData(), authScheme.length()) != 0) { - it.remove(); - } - } - } - } - -try_next_auth_scheme: - QByteArray bestOffer = KAbstractHttpAuthentication::bestOffer(authTokens); - if (*auth) { - const QByteArray authScheme ((*auth)->scheme().trimmed()); - if (qstrnicmp(authScheme.constData(), bestOffer.constData(), authScheme.length()) != 0) { - // huh, the strongest authentication scheme offered has changed. - delete *auth; - *auth = 0; - } - } - - if (!(*auth)) { - *auth = KAbstractHttpAuthentication::newAuth(bestOffer, config()); - } - - if (*auth) { - kDebug(7113) << "Trying authentication scheme:" << (*auth)->scheme(); - - // remove trailing space from the method string, or digest auth will fail - (*auth)->setChallenge(bestOffer, authinfo.url, m_request.sentMethodString); - - QString username, password; - bool generateAuthHeader = true; - if ((*auth)->needCredentials()) { - // use credentials supplied by the application if available - if (!m_request.url.user().isEmpty() && !m_request.url.pass().isEmpty()) { - username = m_request.url.user(); - password = m_request.url.pass(); - // don't try this password any more - m_request.url.setPass(QString()); - } else { - // try to get credentials from kpasswdserver's cache, then try asking the user. - authinfo.verifyPath = false; // we have realm, no path based checking please! - authinfo.realmValue = (*auth)->realm(); - if (authinfo.realmValue.isEmpty() && !(*auth)->supportsPathMatching()) - authinfo.realmValue = QLatin1String((*auth)->scheme()); - - // Save the current authinfo url because it can be modified by the call to - // checkCachedAuthentication. That way we can restore it if the call - // modified it. - const KUrl reqUrl = authinfo.url; - if (!errorMsg.isEmpty() || !checkCachedAuthentication(authinfo)) { - // Reset url to the saved url... - authinfo.url = reqUrl; - authinfo.keepPassword = true; - authinfo.comment = i18n("%1 at %2", - htmlEscape(authinfo.realmValue), authinfo.url.host()); - - if (!openPasswordDialog(authinfo, errorMsg)) { - generateAuthHeader = false; - authRequiresAnotherRoundtrip = false; - if (!sendErrorPageNotification()) { - error(ERR_ACCESS_DENIED, reqUrl.host()); - } - kDebug(7113) << "looks like the user canceled the authentication dialog"; - delete *auth; - *auth = 0; - } - } - username = authinfo.username; - password = authinfo.password; - } - } - - if (generateAuthHeader) { - (*auth)->generateResponse(username, password); - (*auth)->setCachePasswordEnabled(authinfo.keepPassword); - - kDebug(7113) << "isError=" << (*auth)->isError() - << "needCredentials=" << (*auth)->needCredentials() - << "forceKeepAlive=" << (*auth)->forceKeepAlive() - << "forceDisconnect=" << (*auth)->forceDisconnect(); - - if ((*auth)->isError()) { - authTokens.removeOne(bestOffer); - if (!authTokens.isEmpty()) { - goto try_next_auth_scheme; - } else { - error(ERR_UNSUPPORTED_ACTION, i18n("Authorization failed.")); - authRequiresAnotherRoundtrip = false; - } - //### return false; ? - } else if ((*auth)->forceKeepAlive()) { - //### think this through for proxied / not proxied - m_request.isKeepAlive = true; - } else if ((*auth)->forceDisconnect()) { - //### think this through for proxied / not proxied - m_request.isKeepAlive = false; - httpCloseConnection(); - } - } - } else { - authRequiresAnotherRoundtrip = false; - if (!sendErrorPageNotification()) { - error(ERR_UNSUPPORTED_ACTION, i18n("Unknown Authorization method.")); - } - } - } - - return authRequiresAnotherRoundtrip; + kDebug(7103); + error(KIO::ERR_UNSUPPORTED_ACTION, url.prettyUrl()); } -void HTTPProtocol::copyPut(const KUrl& src, const KUrl& dest, JobFlags flags) +void HttpProtocol::get( const KUrl& url ) { - kDebug(7113) << src << "->" << dest; - - if (!maybeSetRequestUrl(dest)) { - return; - } - - resetSessionSettings(); - - if (!(flags & KIO::Overwrite)) { - // check to make sure this host supports WebDAV - if (!davHostOk()) { - return; - } - - // Checks if the destination exists and return an error if it does. - if (!davStatDestination()) { - return; - } - } - - m_POSTbuf = new QFile (src.toLocalFile()); - if (!m_POSTbuf->open(QFile::ReadOnly)) { - error(KIO::ERR_CANNOT_OPEN_FOR_READING, QString()); - return; - } - - m_request.method = HTTP_PUT; - m_request.cacheTag.policy = CC_Reload; - - proceedUntilResponseContent(); + kDebug(7103); + error(KIO::ERR_UNSUPPORTED_ACTION, url.prettyUrl()); } -bool HTTPProtocol::davStatDestination() +void HttpProtocol::put( const KUrl& url, int permissions, KIO::JobFlags flags ) { - const QByteArray request ("" - "" - "" - "" - "" - "" - ""); - davSetRequest(request); - - // WebDAV Stat or List... - m_request.method = DAV_PROPFIND; - m_request.url.setQuery(QString()); - m_request.cacheTag.policy = CC_Reload; - m_request.davData.depth = 0; - - proceedUntilResponseContent(true); + Q_UNUSED(permissions); + Q_UNUSED(flags); - if (!m_request.isKeepAlive) { - httpCloseConnection(); // close connection if server requested it. - m_request.isKeepAlive = true; // reset the keep alive flag. - } - - if (m_request.responseCode == 207) { - error(ERR_FILE_ALREADY_EXIST, QString()); - return false; - } - - // force re-authentication... - delete m_wwwAuth; - m_wwwAuth = 0; - - return true; + kDebug(7103); + error(KIO::ERR_UNSUPPORTED_ACTION, url.prettyUrl()); } - -#include "moc_http.cpp" diff --git a/kioslave/http/http.h b/kioslave/http/http.h index 2a581d13..3373ac4a 100644 --- a/kioslave/http/http.h +++ b/kioslave/http/http.h @@ -1,602 +1,42 @@ -/* - Copyright (C) 2000,2001 Dawit Alemayehu - Copyright (C) 2000,2001 Waldo Bastian - Copyright (C) 2000,2001 George Staikos - Copyright (C) 2001,2002 Hamish Rodda - Copyright (C) 2007 Daniel Nicoletti - Copyright (C) 2008,2009 Andreas Hartmetz +/* This file is part of the KDE libraries + Copyright (C) 2022 Ivailo Monev - 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 -#include -#include -#include -#include -#include - -#include -#include -#include +#include #include -#include "kio/tcpslavebase.h" -#include "kio/http.h" +#include - -#include -#include -#include - -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 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 diff --git a/kioslave/http/http.protocol b/kioslave/http/http.protocol index 2d97cd47..bf99028a 100644 --- a/kioslave/http/http.protocol +++ b/kioslave/http/http.protocol @@ -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 diff --git a/kioslave/http/http_cache_cleaner.cpp b/kioslave/http/http_cache_cleaner.cpp deleted file mode 100644 index 73af1073..00000000 --- a/kioslave/http/http_cache_cleaner.cpp +++ /dev/null @@ -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 -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -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(intTime); - qint64 check = static_cast(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(&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(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(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::ConstIterator it = m_scoreboard.constBegin(); - for (; it != m_scoreboard.constEnd(); ++it) { - const char *indexData = reinterpret_cast(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::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 &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 realFiles; - Q_FOREACH (CacheFileInfo *fi, fiList) { - realFiles.insert(CacheIndex(fi->baseName)); - } - QHash::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 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 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 ", 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 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; -} diff --git a/kioslave/http/http_cache_cleaner.desktop b/kioslave/http/http_cache_cleaner.desktop deleted file mode 100644 index ab5ad75c..00000000 --- a/kioslave/http/http_cache_cleaner.desktop +++ /dev/null @@ -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 diff --git a/kioslave/http/httpauthentication.cpp b/kioslave/http/httpauthentication.cpp deleted file mode 100644 index 98516216..00000000 --- a/kioslave/http/httpauthentication.cpp +++ /dev/null @@ -1,927 +0,0 @@ -/* This file is part of the KDE libraries - Copyright (C) 2008, 2009 Andreas Hartmetz - - 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 -#else -#include -#endif /* GSSAPI_MIT */ - -// Catch uncompatible crap (BR86019) -#if defined(GSS_RFC_COMPLIANT_OIDS) && (GSS_RFC_COMPLIANT_OIDS == 0) -#include -#define GSS_C_NT_HOSTBASED_SERVICE gss_nt_service_name -#endif - -#endif /* HAVE_LIBGSSAPI */ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -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 parseChallenge(QByteArray &ba, QByteArray *scheme, QByteArray* nextAuth = 0) -{ - QList 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 &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 &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 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 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(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 diff --git a/kioslave/http/httpauthentication.h b/kioslave/http/httpauthentication.h deleted file mode 100644 index 0bec6c36..00000000 --- a/kioslave/http/httpauthentication.h +++ /dev/null @@ -1,251 +0,0 @@ -/* This file is part of the KDE libraries - Copyright (C) 2008, 2009 Andreas Hartmetz - - 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 - -#include - -#include -#include -#include - -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 &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 splitOffers(const QList &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 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 diff --git a/kioslave/http/https.protocol b/kioslave/http/https.protocol index 62a43ad1..45f4eb89 100644 --- a/kioslave/http/https.protocol +++ b/kioslave/http/https.protocol @@ -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 diff --git a/kioslave/http/kcookiejar/CMakeLists.txt b/kioslave/http/kcookiejar/CMakeLists.txt deleted file mode 100644 index 691de1c6..00000000 --- a/kioslave/http/kcookiejar/CMakeLists.txt +++ /dev/null @@ -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} -) - - - diff --git a/kioslave/http/kcookiejar/kcookiejar.cpp b/kioslave/http/kcookiejar/kcookiejar.cpp deleted file mode 100644 index a31ea5df..00000000 --- a/kioslave/http/kcookiejar/kcookiejar.cpp +++ /dev/null @@ -1,1633 +0,0 @@ -/* This file is part of the KDE File Manager - - Copyright (C) 1998-2000 Waldo Bastian (bastian@kde.org) - Copyright (C) 2000,2001 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 Cookies - -// -// The cookie protocol is a mess. RFC2109 is a joke since nobody seems to -// use it. Apart from that it is badly written. -// We try to implement Netscape Cookies and try to behave us according to -// RFC2109 as much as we can. -// -// We assume cookies do not contain any spaces (Netscape spec.) -// According to RFC2109 this is allowed though. -// - -#include "kcookiejar.h" - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -// BR87227 -// Waba: Should the number of cookies be limited? -// I am not convinced of the need of such limit -// Mozilla seems to limit to 20 cookies / domain -// but it is unclear which policy it uses to expire -// cookies when it exceeds that amount -#undef MAX_COOKIE_LIMIT - -#define MAX_COOKIES_PER_HOST 25 -#define READ_BUFFER_SIZE 8192 -#define IP_ADDRESS_EXPRESSION "(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)" - -// Note with respect to QLatin1String( ).... -// Cookies are stored as 8 bit data and passed to kio_http as Latin1 -// regardless of their actual encoding. -#define QL1S(x) QLatin1String(x) -#define QL1C(x) QLatin1Char(x) - - -static QString removeWeekday(const QString& value) -{ - const int index = value.indexOf(QL1C(' ')); - if (index > -1) { - int pos = 0; - const QString weekday = value.left(index); - for (int i = 1; i < 8; ++i) { - if (weekday.startsWith(QDate::shortDayName(i), Qt::CaseInsensitive) || - weekday.startsWith(QDate::longDayName(i), Qt::CaseInsensitive)) { - pos = index + 1; - break; - } - } - if (pos > 0) { - return value.mid(pos); - } - } - return value; -} - -static QDateTime parseDate(const QString& _value) -{ - // Handle sites sending invalid weekday as part of the date. #298660 - const QString value (removeWeekday(_value)); - - // Check if expiration date matches RFC dates as specified under - // RFC 2616 sec 3.3.1 & RFC 6265 sec 4.1.1 - KDateTime dt = KDateTime::fromString(value, KDateTime::RFCDate); - - // In addition to the RFC date formats we support the ANSI C asctime format - // per RFC 2616 sec 3.3.1 and a variation of that detected @ amazon.com - if (!dt.isValid()) { - static const char* date_formats[] = { - "%:B%t%d%t%H:%M:%S%t%Y%t%Z", /* ANSI C's asctime() format (#145244): Jan 01 00:00:00 1970 GMT */ - "%:B%t%d%t%Y%t%H:%M:%S%t%Z", /* A variation on the above format seen @ amazon.com: Jan 01 1970 00:00:00 GMT */ - 0 - }; - - for (int i = 0; date_formats[i]; ++i) { - dt = KDateTime::fromString(value, QL1S(date_formats[i])); - if (dt.isValid()) { - break; - } - } - } - - return dt.toUtc().dateTime(); // Per RFC 2616 sec 3.3.1 always convert to UTC. -} - -static qint64 toEpochSecs(const QDateTime& dt) -{ - return (dt.toMSecsSinceEpoch()/1000); // convert to seconds... -} - -static qint64 epoch() -{ - return toEpochSecs(QDateTime::currentDateTimeUtc()); -} - -QString KCookieJar::adviceToStr(KCookieAdvice _advice) -{ - switch( _advice ) - { - case KCookieAccept: return QL1S("Accept"); - case KCookieAcceptForSession: return QL1S("AcceptForSession"); - case KCookieReject: return QL1S("Reject"); - case KCookieAsk: return QL1S("Ask"); - default: return QL1S("Dunno"); - } -} - -KCookieAdvice KCookieJar::strToAdvice(const QString &_str) -{ - if (_str.isEmpty()) - return KCookieDunno; - - QString advice = _str.toLower(); - - if (advice == QL1S("accept")) - return KCookieAccept; - else if (advice == QL1S("acceptforsession")) - return KCookieAcceptForSession; - else if (advice == QL1S("reject")) - return KCookieReject; - else if (advice == QL1S("ask")) - return KCookieAsk; - - return KCookieDunno; -} - -// KHttpCookie -/////////////////////////////////////////////////////////////////////////// - -// -// Cookie constructor -// -KHttpCookie::KHttpCookie(const QString &_host, - const QString &_domain, - const QString &_path, - const QString &_name, - const QString &_value, - qint64 _expireDate, - int _protocolVersion, - bool _secure, - bool _httpOnly, - bool _explicitPath) : - mHost(_host), - mDomain(_domain), - mPath(_path.isEmpty() ? QString() : _path), - mName(_name), - mValue(_value), - mExpireDate(_expireDate), - mProtocolVersion(_protocolVersion), - mSecure(_secure), - mCrossDomain(false), - mHttpOnly(_httpOnly), - mExplicitPath(_explicitPath), - mUserSelectedAdvice(KCookieDunno) -{ -} - -// -// Checks if a cookie has been expired -// -bool KHttpCookie::isExpired(qint64 currentDate) const -{ - if (currentDate == -1) - currentDate = epoch(); - - return (mExpireDate != 0) && (mExpireDate < currentDate); -} - -// -// Returns a string for a HTTP-header -// -QString KHttpCookie::cookieStr(bool useDOMFormat) const -{ - QString result; - - if (useDOMFormat || (mProtocolVersion == 0)) { - if ( mName.isEmpty() ) - result = mValue; - else - result = mName + QL1C('=') + mValue; - } else { - result = mName + QL1C('=') + mValue; - if (mExplicitPath) - result += QL1S("; $Path=\"") + mPath + QL1C('"'); - if (!mDomain.isEmpty()) - result += QL1S("; $Domain=\"") + mDomain + QL1C('"'); - if (!mPorts.isEmpty()) { - if (mPorts.length() == 2 && mPorts.at(0) == -1) - result += QL1S("; $Port"); - else { - QString portNums; - Q_FOREACH(int port, mPorts) - portNums += QString::number(port) + QL1C(' '); - result += QL1S("; $Port=\"") + portNums.trimmed() + QL1C('"'); - } - } - } - return result; -} - -// -// Returns whether this cookie should be send to this location. -bool KHttpCookie::match(const QString &fqdn, const QStringList &domains, - const QString &path, int port) const -{ - // Cookie domain match check - if (mDomain.isEmpty()) - { - if (fqdn != mHost) - return false; - } - else if (!domains.contains(mDomain)) - { - if (mDomain[0] == '.') - return false; - - // Maybe the domain needs an extra dot. - const QString domain = QL1C('.') + mDomain; - if ( !domains.contains( domain ) ) - if ( fqdn != mDomain ) - return false; - } - else if (mProtocolVersion != 0 && port != -1 && - !mPorts.isEmpty() && !mPorts.contains(port)) - { - return false; - } - - // Cookie path match check - if (mPath.isEmpty()) - return true; - - // According to the netscape spec http://www.acme.com/foobar, - // http://www.acme.com/foo.bar and http://www.acme.com/foo/bar - // should all match http://www.acme.com/foo... - // We only match http://www.acme.com/foo/bar - if( path.startsWith(mPath) && - ( - (path.length() == mPath.length() ) || // Paths are exact match - mPath.endsWith(QL1C('/')) || // mPath ended with a slash - (path[mPath.length()] == QL1C('/')) // A slash follows - )) - return true; // Path of URL starts with cookie-path - - return false; -} - -// KCookieJar -/////////////////////////////////////////////////////////////////////////// - -// -// Constructs a new cookie jar -// -// One jar should be enough for all cookies. -// -KCookieJar::KCookieJar() -{ - m_globalAdvice = KCookieDunno; - m_configChanged = false; - m_cookiesChanged = false; -} - -// -// Destructs the cookie jar -// -// Poor little cookies, they will all be eaten by the cookie monster! -// -KCookieJar::~KCookieJar() -{ - qDeleteAll(m_cookieDomains); - // Not much to do here -} - -// cookiePtr is modified: the window ids of the existing cookie in the list are added to it -static void removeDuplicateFromList(KHttpCookieList *list, KHttpCookie& cookiePtr, bool nameMatchOnly=false, bool updateWindowId=false) -{ - QString domain1 = cookiePtr.domain(); - if (domain1.isEmpty()) - domain1 = cookiePtr.host(); - - QMutableListIterator cookieIterator(*list); - while (cookieIterator.hasNext()) { - const KHttpCookie& cookie = cookieIterator.next(); - QString domain2 = cookie.domain(); - if (domain2.isEmpty()) - domain2 = cookie.host(); - - if (cookiePtr.name() == cookie.name() && - (nameMatchOnly || (domain1 == domain2 && cookiePtr.path() == cookie.path()))) - { - if (updateWindowId) { - Q_FOREACH(long windowId, cookie.windowIds()) { - if (windowId && (!cookiePtr.windowIds().contains(windowId))) { - cookiePtr.windowIds().append(windowId); - } - } - } - cookieIterator.remove(); - break; - } - } -} - - -// -// 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. -// -QString KCookieJar::findCookies(const QString &_url, bool useDOMFormat, long windowId, KHttpCookieList *pendingCookies) -{ - QString cookieStr, fqdn, path; - QStringList domains; - int port = -1; - - if (!parseUrl(_url, fqdn, path, &port)) - return cookieStr; - - const bool secureRequest = (_url.startsWith(QL1S("https://"), Qt::CaseInsensitive) || - _url.startsWith(QL1S("webdavs://"), Qt::CaseInsensitive)); - if (port == -1) - port = (secureRequest ? 443 : 80); - - extractDomains(fqdn, domains); - - KHttpCookieList allCookies; - for (QStringList::ConstIterator it = domains.constBegin(), itEnd = domains.constEnd();;++it) - { - KHttpCookieList *cookieList = 0; - if (it == itEnd) - { - cookieList = pendingCookies; // Add pending cookies - pendingCookies = 0; - if (!cookieList) - break; - } - else - { - if (!(*it).isNull()) - cookieList = m_cookieDomains.value(*it); - - if (!cookieList) - continue; // No cookies for this domain - } - - QMutableListIterator cookieIt (*cookieList); - while (cookieIt.hasNext()) - { - KHttpCookie& cookie = cookieIt.next(); - if (cookieAdvice(cookie) == KCookieReject) - continue; - - if (!cookie.match(fqdn, domains, path, port)) - continue; - - if( cookie.isSecure() && !secureRequest ) - continue; - - if( cookie.isHttpOnly() && useDOMFormat ) - continue; - - // Do not send expired cookies. - if ( cookie.isExpired()) - { - // NOTE: there is no need to delete the cookie here because the - // cookieserver will invoke its saveCookieJar function as a result - // of the state change below. This will then result in the cookie - // being deleting at that point. - m_cookiesChanged = true; - continue; - } - - if (windowId && (cookie.windowIds().indexOf(windowId) == -1)) - cookie.windowIds().append(windowId); - - if (it == itEnd) // Only needed when processing pending cookies - removeDuplicateFromList(&allCookies, cookie); - - allCookies.append(cookie); - } - - if (it == itEnd) - break; // Finished. - } - - int protVersion = 0; - Q_FOREACH(const KHttpCookie& cookie, allCookies) { - if (cookie.protocolVersion() > protVersion) - protVersion = cookie.protocolVersion(); - } - - if (!allCookies.isEmpty()) - { - if (!useDOMFormat) - cookieStr = QL1S("Cookie: "); - - if (protVersion > 0) - cookieStr = cookieStr + QL1S("$Version=") + QString::number(protVersion) + QL1S("; "); - - Q_FOREACH(const KHttpCookie& cookie, allCookies) - cookieStr = cookieStr + cookie.cookieStr(useDOMFormat) + QL1S("; "); - - cookieStr.truncate(cookieStr.length() - 2); // Remove the trailing ';' - } - - return cookieStr; -} - -// -// This function parses a string like 'my_name="my_value";' and returns -// 'my_name' in Name and 'my_value' in Value. -// -// A pointer to the end of the parsed part is returned. -// This pointer points either to: -// '\0' - The end of the string has reached. -// ';' - Another my_name="my_value" pair follows -// ',' - Another cookie follows -// '\n' - Another header follows -static const char * parseNameValue(const char *header, - QString &Name, - QString &Value, - bool keepQuotes=false, - bool rfcQuotes=false) -{ - const char *s = header; - // Parse 'my_name' part - for(; (*s != '='); s++) - { - if ((*s=='\0') || (*s==';') || (*s=='\n')) - { - // No '=' sign -> use string as the value, name is empty - // (behavior found in Mozilla and IE) - Name = QL1S(""); - Value = QL1S(header); - Value.truncate( s - header ); - Value = Value.trimmed(); - return s; - } - } - - Name = QL1S(header); - Name.truncate( s - header ); - Name = Name.trimmed(); - - // *s == '=' - s++; - - // Skip any whitespace - for(; (*s == ' ') || (*s == '\t'); s++) - { - if ((*s=='\0') || (*s==';') || (*s=='\n')) - { - // End of Name - Value = ""; - return s; - } - } - - if ((rfcQuotes || !keepQuotes) && (*s == '\"')) - { - // Parse '"my_value"' part (quoted value) - if (keepQuotes) - header = s++; - else - header = ++s; // skip " - for(;(*s != '\"');s++) - { - if ((*s=='\0') || (*s=='\n')) - { - // End of Name - Value = QL1S(header); - Value.truncate(s - header); - return s; - } - } - Value = QL1S(header); - // *s == '\"'; - if (keepQuotes) - Value.truncate( ++s - header ); - else - Value.truncate( s++ - header ); - - // Skip any remaining garbage - for(;; s++) - { - if ((*s=='\0') || (*s==';') || (*s=='\n')) - break; - } - } - else - { - // Parse 'my_value' part (unquoted value) - header = s; - while ((*s != '\0') && (*s != ';') && (*s != '\n')) - s++; - // End of Name - Value = QL1S(header); - Value.truncate( s - header ); - Value = Value.trimmed(); - } - return s; - -} - -void KCookieJar::stripDomain(const QString &_fqdn, QString &_domain) const -{ - QStringList domains; - extractDomains(_fqdn, domains); - if (domains.count() > 3) - _domain = domains[3]; - else if ( domains.count() > 0 ) - _domain = domains[0]; - else - _domain = QL1S(""); -} - -QString KCookieJar::stripDomain(const KHttpCookie& cookie) const -{ - QString domain; // We file the cookie under this domain. - if (cookie.domain().isEmpty()) - stripDomain( cookie.host(), domain); - else - domain = cookie.domain(); - return domain; -} - -bool KCookieJar::parseUrl(const QString &_url, QString &_fqdn, QString &_path, int *port) -{ - KUrl kurl(_url); - if (!kurl.isValid() || kurl.protocol().isEmpty()) - return false; - - _fqdn = kurl.host().toLower(); - // Cookie spoofing protection. Since there is no way a path separator, - // a space or the escape encoding character is allowed in the hostname - // according to RFC 2396, reject attempts to include such things there! - if (_fqdn.contains(QL1C('/')) || _fqdn.contains(QL1C('%'))) - return false; // deny everything!! - - // Set the port number from the protocol when one is found... - if (port) - *port = kurl.port(); - - _path = kurl.path(); - if (_path.isEmpty()) - _path = QL1S("/"); - - return true; -} - -void KCookieJar::extractDomains(const QString &_fqdn, QStringList &_domains) -{ - static const QStringList s_twoLevelTLD = QStringList() - << "name" - << "ai" - << "au" - << "bd" - << "bh" - << "ck" - << "eg" - << "et" - << "fk" - << "il" - << "in" - << "kh" - << "kr" - << "mk" - << "mt" - << "na" - << "np" - << "nz" - << "pg" - << "pk" - << "qa" - << "sa" - << "sb" - << "sg" - << "sv" - << "ua" - << "ug" - << "uk" - << "uy" - << "vn" - << "za" - << "zw"; - - static const QStringList s_gTLDs = QStringList() - << "com" - << "edu" - << "gov" - << "int" - << "mil" - << "net" - << "org" - << "biz" - << "info" - << "name" - << "pro" - << "aero" - << "coop" - << "museum" - << "asia" - << "cat" - << "jobs" - << "mobi" - << "tel" - << "travel"; - - if (_fqdn.isEmpty()) { - _domains.append( QL1S("localhost") ); - return; - } - - // Return numeric IPv6 addresses as is... - if (_fqdn[0] == '[') - { - _domains.append( _fqdn ); - return; - } - // Return numeric IPv4 addresses as is... - if (_fqdn[0] >= '0' && _fqdn[0] <= '9' && _fqdn.indexOf(QRegExp(IP_ADDRESS_EXPRESSION)) > -1) - { - _domains.append( _fqdn ); - return; - } - - // Always add the FQDN at the start of the list for - // hostname == cookie-domainname checks! - _domains.append(_fqdn); - _domains.append(QL1C('.') + _fqdn); - - QStringList partList = _fqdn.split(QL1C('.'), QString::SkipEmptyParts); - - if (partList.count()) - partList.erase(partList.begin()); // Remove hostname - - while(partList.count()) - { - - if (partList.count() == 1) - break; // We only have a TLD left. - - if ((partList.count() == 2) && s_twoLevelTLD.contains(partList[1].toLower())) - { - // This domain uses two-level TLDs in the form xxxx.yy - break; - } - - if ((partList.count() == 2) && (partList[1].length() == 2)) - { - // If this is a TLD, we should stop. (e.g. co.uk) - // We assume this is a TLD if it ends with .xx.yy or .x.yy - if (partList[0].length() <= 2) - break; // This is a TLD. - - // Catch some TLDs that we miss with the previous check - // e.g. com.au, org.uk, mil.co - if (s_gTLDs.contains(partList[0].toLower())) - break; - } - - QString domain = partList.join(QL1S(".")); - _domains.append(domain); - _domains.append(QL1C('.') + domain); - partList.erase(partList.begin()); // Remove part - } -} - -// -// This function parses cookie_headers and returns a linked list of -// 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 KCookieJar::makeCookies(const QString &_url, - const QByteArray &cookie_headers, - long windowId) -{ - QString fqdn, path; - - if (!parseUrl(_url, fqdn, path)) - return KHttpCookieList(); // Error parsing _url - - QString Name, Value; - KHttpCookieList cookieList, cookieList2; - - bool isRFC2965 = false; - bool crossDomain = false; - const char *cookieStr = cookie_headers.constData(); - - QString defaultPath; - const int index = path.lastIndexOf(QL1C('/')); - if (index > 0) - defaultPath = path.left(index); - - // Check for cross-domain flag from kio_http - if (qstrncmp(cookieStr, "Cross-Domain\n", 13) == 0) - { - cookieStr += 13; - crossDomain = true; - } - - // The hard stuff :) - for(;;) - { - // check for "Set-Cookie" - if (qstrnicmp(cookieStr, "Set-Cookie:", 11) == 0) - { - cookieStr = parseNameValue(cookieStr+11, Name, Value, true); - - // Host = FQDN - // Default domain = "" - // Default path according to rfc2109 - - - KHttpCookie cookie(fqdn, QL1S(""), defaultPath, Name, Value); - if (windowId) - cookie.mWindowIds.append(windowId); - cookie.mCrossDomain = crossDomain; - - // Insert cookie in chain - cookieList.append(cookie); - } - else if (qstrnicmp(cookieStr, "Set-Cookie2:", 12) == 0) - { - // Attempt to follow rfc2965 - isRFC2965 = true; - cookieStr = parseNameValue(cookieStr+12, Name, Value, true, true); - - // Host = FQDN - // Default domain = "" - // Default path according to rfc2965 - - KHttpCookie cookie(fqdn, QL1S(""), defaultPath, Name, Value); - if (windowId) - cookie.mWindowIds.append(windowId); - cookie.mCrossDomain = crossDomain; - - // Insert cookie in chain - cookieList2.append(cookie); - } - else - { - // This is not the start of a cookie header, skip till next line. - while (*cookieStr && *cookieStr != '\n') - cookieStr++; - - if (*cookieStr == '\n') - cookieStr++; - - if (!*cookieStr) - break; // End of cookie_headers - else - continue; // end of this header, continue with next. - } - - while ((*cookieStr == ';') || (*cookieStr == ' ')) - { - cookieStr++; - - // Name-Value pair follows - cookieStr = parseNameValue(cookieStr, Name, Value); - KHttpCookie& lastCookie = (isRFC2965 ? cookieList2.last() : cookieList.last()); - - if (Name.compare(QL1S("domain"), Qt::CaseInsensitive) == 0) - { - QString dom = Value.toLower(); - // RFC2965 3.2.2: If an explicitly specified value does not - // start with a dot, the user agent supplies a leading dot - if(dom.length() && dom[0] != '.') - dom.prepend("."); - // remove a trailing dot - if(dom.length() > 2 && dom[dom.length()-1] == '.') - dom = dom.left(dom.length()-1); - - if(dom.count(QL1C('.')) > 1 || dom == ".local") - lastCookie.mDomain = dom; - } - else if (Name.compare(QL1S("max-age"), Qt::CaseInsensitive) == 0) - { - int max_age = Value.toInt(); - if (max_age == 0) - lastCookie.mExpireDate = 1; - else - lastCookie.mExpireDate = toEpochSecs(QDateTime::currentDateTimeUtc().addSecs(max_age)); - } - else if (Name.compare(QL1S("expires"), Qt::CaseInsensitive) == 0) - { - const QDateTime dt = parseDate(Value); - - if (dt.isValid()) { - lastCookie.mExpireDate = toEpochSecs(dt); - if (lastCookie.mExpireDate == 0) - lastCookie.mExpireDate = 1; - } - } - else if (Name.compare(QL1S("path"), Qt::CaseInsensitive) == 0) - { - if (Value.isEmpty()) - lastCookie.mPath.clear(); // Catch "" <> QString() - else - lastCookie.mPath = QUrl::fromPercentEncoding(Value.toLatin1()); - lastCookie.mExplicitPath = true; - } - else if (Name.compare(QL1S("version"), Qt::CaseInsensitive) == 0) - { - lastCookie.mProtocolVersion = Value.toInt(); - } - else if (Name.compare(QL1S("secure"), Qt::CaseInsensitive) == 0 || - (Name.isEmpty() && Value.compare(QL1S("secure"), Qt::CaseInsensitive) == 0)) - { - lastCookie.mSecure = true; - } - else if (Name.compare(QL1S("httponly"), Qt::CaseInsensitive) == 0 || - (Name.isEmpty() && Value.compare(QL1S("httponly"), Qt::CaseInsensitive) == 0)) - { - lastCookie.mHttpOnly = true; - } - else if (isRFC2965 && (Name.compare(QL1S("port"), Qt::CaseInsensitive) == 0 || - (Name.isEmpty() && Value.compare(QL1S("port"), Qt::CaseInsensitive) == 0))) - { - // Based on the port selection rule of RFC 2965 section 3.3.4... - if (Name.isEmpty()) - { - // We intentionally append a -1 first in order to distinguish - // between only a 'Port' vs a 'Port="80 443"' in the sent cookie. - lastCookie.mPorts.append(-1); - const bool secureRequest = (_url.startsWith(QL1S("https://"), Qt::CaseInsensitive) || - _url.startsWith(QL1S("webdavs://"), Qt::CaseInsensitive)); - if (secureRequest) - lastCookie.mPorts.append(443); - else - lastCookie.mPorts.append(80); - } - else - { - bool ok; - const QStringList portNums = Value.split(QL1C(' '), QString::SkipEmptyParts); - Q_FOREACH(const QString& portNum, portNums) - { - const int port = portNum.toInt(&ok); - if (ok) - lastCookie.mPorts.append(port); - } - } - } - } - - if (*cookieStr == '\0') - break; // End of header - - // Skip ';' or '\n' - cookieStr++; - } - - // RFC2965 cookies come last so that they override netscape cookies. - while(!cookieList2.isEmpty()) { - KHttpCookie& lastCookie = cookieList2.first(); - removeDuplicateFromList(&cookieList, lastCookie, true); - cookieList.append(lastCookie); - cookieList2.removeFirst(); - } - - return cookieList; -} - -/** -* Parses cookie_domstr and returns a linked list of KHttpCookie objects. -* cookie_domstr should be a semicolon-delimited list of "name=value" -* pairs. Any whitespace before "name" or around '=' is discarded. -* If no cookies are found, 0 is returned. -*/ -KHttpCookieList KCookieJar::makeDOMCookies(const QString &_url, - const QByteArray &cookie_domstring, - long windowId) -{ - // A lot copied from above - KHttpCookieList cookieList; - - const char *cookieStr = cookie_domstring.data(); - QString fqdn; - QString path; - - if (!parseUrl(_url, fqdn, path)) - { - // Error parsing _url - return KHttpCookieList(); - } - - QString Name; - QString Value; - // This time it's easy - while(*cookieStr) - { - cookieStr = parseNameValue(cookieStr, Name, Value); - - // Host = FQDN - // Default domain = "" - // Default path = "" - KHttpCookie cookie(fqdn, QString(), QString(), - Name, Value ); - if (windowId) - cookie.mWindowIds.append(windowId); - - cookieList.append(cookie); - - if (*cookieStr != '\0') - cookieStr++; // Skip ';' or '\n' - } - - return cookieList; -} - -// KHttpCookieList sorting -/////////////////////////////////////////////////////////////////////////// - -// We want the longest path first -static bool compareCookies(const KHttpCookie& item1, const KHttpCookie& item2) -{ - return item1.path().length() > item2.path().length(); -} - - -#ifdef MAX_COOKIE_LIMIT -static void makeRoom(KHttpCookieList *cookieList, KHttpCookiePtr &cookiePtr) -{ - // Too many cookies: throw one away, try to be somewhat clever - KHttpCookiePtr lastCookie = 0; - for(KHttpCookiePtr cookie = cookieList->first(); cookie; cookie = cookieList->next()) - { - if (compareCookies(cookie, cookiePtr)) - break; - lastCookie = cookie; - } - if (!lastCookie) - lastCookie = cookieList->first(); - cookieList->removeRef(lastCookie); -} -#endif - -// -// This function hands a KHttpCookie object over to the cookie jar. -// -void KCookieJar::addCookie(KHttpCookie &cookie) -{ - QStringList domains; - // We always need to do this to make sure that the - // that cookies of type hostname == cookie-domainname - // are properly removed and/or updated as necessary! - extractDomains( cookie.host(), domains ); - - // If the cookie specifies a domain, check whether it is valid. Otherwise, - // accept the cookie anyways but removes the domain="" value to prevent - // cross-site cookie injection. - if (!cookie.domain().isEmpty()) { - if (!domains.contains(cookie.domain()) && - !cookie.domain().endsWith(QL1C('.') + cookie.host())) - cookie.fixDomain(QString()); - } - - QStringListIterator it (domains); - while (it.hasNext()) - { - const QString& key = it.next(); - KHttpCookieList* list = 0; - - if (!key.isNull()) - list = m_cookieDomains.value(key); - - if (list) - removeDuplicateFromList(list, cookie, false, true); - } - - const QString domain = stripDomain( cookie ); - KHttpCookieList* cookieList = 0; - if (!domain.isNull()) - cookieList = m_cookieDomains.value(domain); - - if (!cookieList) - { - // Make a new cookie list - cookieList = new KHttpCookieList(); - - // All cookies whose domain is not already - // known to us should be added with KCookieDunno. - // KCookieDunno means that we use the global policy. - cookieList->setAdvice( KCookieDunno ); - - m_cookieDomains.insert( domain, cookieList); - - // Update the list of domains - m_domainList.append(domain); - } - - // Add the cookie to the cookie list - // The cookie list is sorted 'longest path first' - if (!cookie.isExpired()) - { -#ifdef MAX_COOKIE_LIMIT - if (cookieList->count() >= MAX_COOKIES_PER_HOST) - makeRoom(cookieList, cookie); // Delete a cookie -#endif - cookieList->push_back(cookie); - // Use a stable sort so that unit tests are reliable. - // In practice it doesn't matter though. - qStableSort(cookieList->begin(), cookieList->end(), compareCookies); - - m_cookiesChanged = true; - } -} - -// -// This function advices whether a single KHttpCookie object should -// be added to the cookie jar. -// -KCookieAdvice KCookieJar::cookieAdvice(const KHttpCookie& cookie) const -{ - if (m_rejectCrossDomainCookies && cookie.isCrossDomain()) - return KCookieReject; - - if (cookie.getUserSelectedAdvice() != KCookieDunno) - return cookie.getUserSelectedAdvice(); - - if (m_autoAcceptSessionCookies && cookie.expireDate() == 0) - return KCookieAccept; - - QStringList domains; - extractDomains(cookie.host(), domains); - - KCookieAdvice advice = KCookieDunno; - QStringListIterator it (domains); - while(advice == KCookieDunno && it.hasNext()) { - const QString& domain = it.next(); - if (domain.startsWith(QL1C('.')) || cookie.host() == domain) { - KHttpCookieList *cookieList = m_cookieDomains.value(domain); - if (cookieList) - advice = cookieList->getAdvice(); - } - } - - if (advice == KCookieDunno) - advice = m_globalAdvice; - - return advice; -} - -// -// 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 KCookieJar::cookieIsPersistent(const KHttpCookie& cookie) const -{ - if (cookie.expireDate() == 0) - return false; - - KCookieAdvice advice = cookieAdvice(cookie); - - if (advice == KCookieReject || advice == KCookieAcceptForSession) - return false; - - return true; -} - -// -// This function gets the advice for all cookies originating from -// _domain. -// -KCookieAdvice KCookieJar::getDomainAdvice(const QString &_domain) const -{ - KHttpCookieList *cookieList = m_cookieDomains.value(_domain); - KCookieAdvice advice; - - if (cookieList) - advice = cookieList->getAdvice(); - else - advice = KCookieDunno; - - return advice; -} - -// -// This function sets the advice for all cookies originating from -// _domain. -// -void KCookieJar::setDomainAdvice(const QString &_domain, KCookieAdvice _advice) -{ - QString domain(_domain); - KHttpCookieList *cookieList = m_cookieDomains.value(domain); - - if (cookieList) { - if (cookieList->getAdvice() != _advice) { - m_configChanged = true; - // domain is already known - cookieList->setAdvice( _advice); - } - - if ((cookieList->isEmpty()) && (_advice == KCookieDunno)) { - // This deletes cookieList! - delete m_cookieDomains.take(domain); - m_domainList.removeAll(domain); - } - } else { - // domain is not yet known - if (_advice != KCookieDunno) { - // We should create a domain entry - m_configChanged = true; - // Make a new cookie list - cookieList = new KHttpCookieList(); - cookieList->setAdvice(_advice); - m_cookieDomains.insert(domain, cookieList); - // Update the list of domains - m_domainList.append( domain); - } - } -} - -// -// This function sets the advice for all cookies originating from -// the same domain as _cookie -// -void KCookieJar::setDomainAdvice(const KHttpCookie& cookie, KCookieAdvice _advice) -{ - QString domain; - stripDomain(cookie.host(), domain); // We file the cookie under this domain. - setDomainAdvice(domain, _advice); -} - -// -// This function sets the global advice for cookies -// -void KCookieJar::setGlobalAdvice(KCookieAdvice _advice) -{ - if (m_globalAdvice != _advice) - m_configChanged = true; - m_globalAdvice = _advice; -} - -// -// Get a list of all domains known to the cookie jar. -// -const QStringList& KCookieJar::getDomainList() -{ - return m_domainList; -} - -// -// Get a list of all cookies in the cookie jar originating from _domain. -// -KHttpCookieList *KCookieJar::getCookieList(const QString & _domain, - const QString & _fqdn ) -{ - QString domain; - - if (_domain.isEmpty()) - stripDomain(_fqdn, domain); - else - domain = _domain; - - return m_cookieDomains.value(domain); -} - -// -// Eat a cookie out of the jar. -// cookieIterator should be one of the cookies returned by getCookieList() -// -void KCookieJar::eatCookie(KHttpCookieList::iterator cookieIterator) -{ - const KHttpCookie& cookie = *cookieIterator; - const QString domain = stripDomain(cookie); // We file the cookie under this domain. - KHttpCookieList *cookieList = m_cookieDomains.value(domain); - - if (cookieList) { - // This deletes cookie! - cookieList->erase(cookieIterator); - - if ((cookieList->isEmpty()) && - (cookieList->getAdvice() == KCookieDunno)) - { - // This deletes cookieList! - delete m_cookieDomains.take(domain); - m_domainList.removeAll(domain); - } - } -} - -void KCookieJar::eatCookiesForDomain(const QString &domain) -{ - KHttpCookieList *cookieList = m_cookieDomains.value(domain); - if (!cookieList || cookieList->isEmpty()) return; - - cookieList->clear(); - if (cookieList->getAdvice() == KCookieDunno) - { - // This deletes cookieList! - delete m_cookieDomains.take(domain); - m_domainList.removeAll(domain); - } - m_cookiesChanged = true; -} - -void KCookieJar::eatSessionCookies( long windowId ) -{ - if (!windowId) - return; - - Q_FOREACH(const QString& domain, m_domainList) - eatSessionCookies( domain, windowId, false ); -} - -void KCookieJar::eatAllCookies() -{ - Q_FOREACH(const QString& domain, m_domainList) - eatCookiesForDomain(domain); // This might remove domain from m_domainList! -} - -void KCookieJar::eatSessionCookies( const QString& fqdn, long windowId, - bool isFQDN ) -{ - KHttpCookieList* cookieList; - if ( !isFQDN ) - cookieList = m_cookieDomains.value(fqdn); - else { - QString domain; - stripDomain( fqdn, domain ); - cookieList = m_cookieDomains.value(domain); - } - - if (cookieList) { - QMutableListIterator cookieIterator(*cookieList); - while (cookieIterator.hasNext()) { - KHttpCookie& cookie = cookieIterator.next(); - - if (cookieIsPersistent(cookie)) - continue; - - QList &ids = cookie.windowIds(); - -#ifndef NDEBUG - if (ids.contains(windowId)) { - if (ids.count() > 1) - kDebug(7104) << "removing window id" << windowId << "from session cookie"; - else - kDebug(7104) << "deleting session cookie"; - } -#endif - if (!ids.removeAll(windowId) || !ids.isEmpty()) { - continue; - } - cookieIterator.remove(); - } - } -} - -static QString hostWithPort(const KHttpCookie* cookie) -{ - const QList& ports = cookie->ports(); - - if (ports.isEmpty()) - return cookie->host(); - - QStringList portList; - Q_FOREACH(int port, ports) - portList << QString::number(port); - - return (cookie->host() + QL1C(':') + portList.join(QL1S(","))); -} - -// -// Saves all cookies to the file '_filename'. -// On succes 'true' is returned. -// On failure 'false' is returned. -bool KCookieJar::saveCookies(const QString &_filename) -{ - KSaveFile cookieFile(_filename); - - if (!cookieFile.open()) - return false; - cookieFile.setPermissions(QFile::ReadUser|QFile::WriteUser); - - QTextStream ts(&cookieFile); - - ts << "# KDE Cookie File v2\n#\n"; - - ts << qSetFieldWidth(20); - ts << "# Host" << "Domain"; - ts << qSetFieldWidth(12); - ts << "Path"; - ts << qSetFieldWidth(4); - ts << "Prot"; - ts << qSetFieldWidth(20); - ts << "Name"; - ts << qSetFieldWidth(4); - ts << "Sec"; - ts << qSetFieldWidth(1); - ts << "Value\n"; - - QStringListIterator it(m_domainList); - while (it.hasNext()) - { - const QString& domain = it.next(); - bool domainPrinted = false; - - KHttpCookieList *cookieList = m_cookieDomains.value(domain); - if (!cookieList) - continue; - - QMutableListIterator cookieIterator(*cookieList); - while (cookieIterator.hasNext()) { - const KHttpCookie& cookie = cookieIterator.next(); - - if (cookie.isExpired()) { - // Delete expired cookies - cookieIterator.remove(); - continue; - } - if (cookieIsPersistent(cookie)) { - // Only save cookies that are not "session-only cookies" - if (!domainPrinted) { - domainPrinted = true; - ts << '[' << domain.toLocal8Bit().data() << "]\n"; - } - // Store persistent cookies - const QString path = QL1S("\"") + cookie.path() + QL1C('"'); - const QString domain = QL1S("\"") + cookie.domain() + QL1C('"'); - const QString host = hostWithPort(&cookie); - - ts << host.toLatin1().constData() << domain.toLatin1().constData(); - ts << qSetFieldWidth(20); - ts << path.toLatin1().constData(); - ts << qSetFieldWidth(12); - ts << cookie.expireDate(); - ts << qSetFieldWidth(10); - ts << cookie.protocolVersion(); - ts << qSetFieldWidth(3); - if(cookie.name().isEmpty()) { - ts << cookie.value().toLatin1().constData(); - } else { - ts << cookie.name().toLatin1().constData(); - } - ts << qSetFieldWidth(20); - ts << (cookie.isSecure() ? 1 : 0) + (cookie.isHttpOnly() ? 2 : 0) + - (cookie.hasExplicitPath() ? 4 : 0) + (cookie.name().isEmpty() ? 8 : 0); - ts << qSetFieldWidth(4); - ts << cookie.value().toLatin1().constData(); - ts << qSetFieldWidth(1); - ts << "\n"; - } - } - } - - return cookieFile.finalize(); -} - -static const char *parseField(char* &buffer, bool keepQuotes=false) -{ - char *result; - if (!keepQuotes && (*buffer == '\"')) - { - // Find terminating " - buffer++; - result = buffer; - while((*buffer != '\"') && (*buffer)) - buffer++; - } - else - { - // Find first white space - result = buffer; - while((*buffer != ' ') && (*buffer != '\t') && (*buffer != '\n') && (*buffer)) - buffer++; - } - - if (!*buffer) - return result; // - *buffer++ = '\0'; - - // Skip white-space - while((*buffer == ' ') || (*buffer == '\t') || (*buffer == '\n')) - buffer++; - - return result; -} - - -static QString extractHostAndPorts(const QString& str, QList* ports = 0) -{ - if (str.isEmpty()) - return str; - - const int index = str.indexOf(QL1C(':')); - if (index == -1) - return str; - - const QString host = str.left(index); - if (ports) { - bool ok; - QStringList portList = str.mid(index+1).split(QL1C(',')); - Q_FOREACH(const QString& portStr, portList) { - const int portNum = portStr.toInt(&ok); - if (ok) - ports->append(portNum); - } - } - - return host; -} - -// -// Reloads all cookies from the file '_filename'. -// On succes 'true' is returned. -// On failure 'false' is returned. -bool KCookieJar::loadCookies(const QString &_filename) -{ - QFile cookieFile (_filename); - - if (!cookieFile.open(QIODevice::ReadOnly)) - return false; - - int version = 1; - bool success = false; - char *buffer = new char[READ_BUFFER_SIZE]; - qint64 len = cookieFile.readLine(buffer, READ_BUFFER_SIZE-1); - - if (len != -1) - { - if (qstrcmp(buffer, "# KDE Cookie File\n") == 0) - { - success = true; - } - else if(qstrcmp(buffer, "# KDE Cookie File v") > 0) - { - bool ok = false; - const int verNum = QByteArray(buffer+19, len-19).trimmed().toInt(&ok); - if (ok) - { - version = verNum; - success = true; - } - } - } - - if (success) - { - const qint64 currentTime = epoch(); - QList ports; - - while(cookieFile.readLine(buffer, READ_BUFFER_SIZE-1) != -1) - { - char *line = buffer; - // Skip lines which begin with '#' or '[' - if ((line[0] == '#') || (line[0] == '[')) - continue; - - const QString host = extractHostAndPorts(QL1S(parseField(line)), &ports); - const QString domain = QL1S( parseField(line) ); - if (host.isEmpty() && domain.isEmpty()) - continue; - const QString path = QL1S( parseField(line) ); - const QString expStr = QL1S( parseField(line) ); - if (expStr.isEmpty()) continue; - const qint64 expDate = expStr.toLongLong(); - const QString verStr = QL1S( parseField(line) ); - if (verStr.isEmpty()) continue; - int protVer = verStr.toInt(); - QString name = QL1S( parseField(line) ); - bool keepQuotes = false; - bool secure = false; - bool httpOnly = false; - bool explicitPath = false; - const char *value = 0; - if ((version == 2) || (protVer >= 200)) - { - if (protVer >= 200) - protVer -= 200; - int i = atoi( parseField(line) ); - secure = i & 1; - httpOnly = i & 2; - explicitPath = i & 4; - if (i & 8) - name = ""; - line[strlen(line)-1] = '\0'; // Strip LF. - value = line; - } - else - { - if (protVer >= 100) - { - protVer -= 100; - keepQuotes = true; - } - value = parseField(line, keepQuotes); - secure = QByteArray(parseField(line)).toShort(); - } - - // Expired or parse error - if (!value || expDate == 0 || expDate < currentTime) - continue; - - KHttpCookie cookie(host, domain, path, name, value, expDate, - protVer, secure, httpOnly, explicitPath); - if (ports.count()) - cookie.mPorts = ports; - addCookie(cookie); - } - } - - delete [] buffer; - m_cookiesChanged = false; - return success; -} - -// -// Save the cookie configuration -// - -void KCookieJar::saveConfig(KConfig *_config) -{ - if (!m_configChanged) - return; - - KConfigGroup dlgGroup(_config, "Cookie Dialog"); - dlgGroup.writeEntry("PreferredPolicy", static_cast(m_preferredPolicy)); - dlgGroup.writeEntry("ShowCookieDetails", m_showCookieDetails ); - KConfigGroup policyGroup(_config,"Cookie Policy"); - policyGroup.writeEntry("CookieGlobalAdvice", adviceToStr( m_globalAdvice)); - - QStringList domainSettings; - QStringListIterator it (m_domainList); - while (it.hasNext()) - { - const QString& domain = it.next(); - KCookieAdvice advice = getDomainAdvice( domain); - if (advice != KCookieDunno) - { - const QString value = domain + QL1C(':') + adviceToStr(advice); - domainSettings.append(value); - } - } - policyGroup.writeEntry("CookieDomainAdvice", domainSettings); - _config->sync(); - m_configChanged = false; -} - - -// -// Load the cookie configuration -// - -void KCookieJar::loadConfig(KConfig *_config, bool reparse ) -{ - if ( reparse ) - _config->reparseConfiguration(); - - KConfigGroup dlgGroup(_config, "Cookie Dialog"); - m_showCookieDetails = dlgGroup.readEntry( "ShowCookieDetails" , false ); - m_preferredPolicy = static_cast(dlgGroup.readEntry("PreferredPolicy", 0)); - - KConfigGroup policyGroup(_config,"Cookie Policy"); - const QStringList domainSettings = policyGroup.readEntry("CookieDomainAdvice", QStringList()); - // Warning: those default values are duplicated in the kcm (kio/kcookiespolicies.cpp) - m_rejectCrossDomainCookies = policyGroup.readEntry("RejectCrossDomainCookies", true); - m_autoAcceptSessionCookies = policyGroup.readEntry("AcceptSessionCookies", true); - m_globalAdvice = strToAdvice(policyGroup.readEntry("CookieGlobalAdvice", QString(QL1S("Accept")))); - - // Reset current domain settings first. - Q_FOREACH( const QString &domain, m_domainList ) - setDomainAdvice(domain, KCookieDunno); - - // Now apply the domain settings read from config file... - for (QStringList::ConstIterator it = domainSettings.constBegin(), itEnd = domainSettings.constEnd(); - it != itEnd; ++it) - { - const QString& value = *it; - const int sepPos = value.lastIndexOf(QL1C(':')); - if (sepPos <= 0) - continue; - - const QString domain(value.left(sepPos)); - KCookieAdvice advice = strToAdvice( value.mid(sepPos + 1) ); - setDomainAdvice(domain, advice); - } -} - -QDebug operator<<(QDebug dbg, const KHttpCookie& cookie) -{ - dbg.nospace() << cookie.cookieStr(false); - return dbg.space(); -} - -QDebug operator<<(QDebug dbg, const KHttpCookieList& list) -{ - Q_FOREACH(const KHttpCookie& cookie, list) - dbg << cookie; - return dbg; -} diff --git a/kioslave/http/kcookiejar/kcookiejar.desktop b/kioslave/http/kcookiejar/kcookiejar.desktop deleted file mode 100644 index 3053723f..00000000 --- a/kioslave/http/kcookiejar/kcookiejar.desktop +++ /dev/null @@ -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 diff --git a/kioslave/http/kcookiejar/kcookiejar.h b/kioslave/http/kcookiejar/kcookiejar.h deleted file mode 100644 index 1c5098e6..00000000 --- a/kioslave/http/kcookiejar/kcookiejar.h +++ /dev/null @@ -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 -#include -#include -#include - -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 mWindowIds; - QList 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 &windowIds() { return mWindowIds; } - const QList &windowIds() const { return mWindowIds; } - const QList &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 -{ -public: - KHttpCookieList() : QList(), 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 m_cookieDomains; - - bool m_configChanged; - bool m_cookiesChanged; - bool m_showCookieDetails; - bool m_rejectCrossDomainCookies; - bool m_autoAcceptSessionCookies; - - KCookieDefaultPolicy m_preferredPolicy; -}; -#endif diff --git a/kioslave/http/kcookiejar/kcookiejar_include.h b/kioslave/http/kcookiejar/kcookiejar_include.h deleted file mode 100644 index 4053a537..00000000 --- a/kioslave/http/kcookiejar/kcookiejar_include.h +++ /dev/null @@ -1,11 +0,0 @@ -// krazy:excludeall=copyright,license (nothing substantial in this file) -// remove as soon as dbusxml2cpp is fixed, Alex - -#include - -#ifndef QDECLAREQLISTINT_HACK_H -#define QDECLAREQLISTINT_HACK_H - -Q_DECLARE_METATYPE (QList) - -#endif diff --git a/kioslave/http/kcookiejar/kcookieserver.cpp b/kioslave/http/kcookiejar/kcookieserver.cpp deleted file mode 100644 index 824f193f..00000000 --- a/kioslave/http/kcookiejar/kcookieserver.cpp +++ /dev/null @@ -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 -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include "kcookiejar.h" -#include "kcookiewin.h" -#include "kcookieserveradaptor.h" - -#define QL1S(x) QLatin1String(x) -#define QL1C(x) QLatin1Char(x) - -K_PLUGIN_FACTORY(KdedCookieServerFactory, - registerPlugin(); - ) -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 -{ -public: - RequestList() : QList() { } -}; - -KCookieServer::KCookieServer(QObject* parent, const QList&) - : 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 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 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(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 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 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& 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 &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" - diff --git a/kioslave/http/kcookiejar/kcookieserver.h b/kioslave/http/kcookiejar/kcookieserver.h deleted file mode 100644 index 25296f4e..00000000 --- a/kioslave/http/kcookiejar/kcookieserver.h +++ /dev/null @@ -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 -#include -#include -#include - -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&); - ~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 &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&); - void saveCookieJar(); -}; - -#endif diff --git a/kioslave/http/kcookiejar/kcookiewin.cpp b/kioslave/http/kcookiejar/kcookiewin.cpp deleted file mode 100644 index b9605822..00000000 --- a/kioslave/http/kcookiejar/kcookiewin.cpp +++ /dev/null @@ -1,341 +0,0 @@ -/* -This file is part of KDE - - Copyright (C) 2000- Waldo Bastian - Copyright (C) 2000- Dawit Alemayehu - -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 -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -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(""); - txt += i18ncp("%2 hostname, %3 optional cross domain suffix (translated below)", - "

You received a cookie from
" - "%2%3
" - "Do you want to accept or reject this cookie?

", - "

You received %1 cookies from
" - "%2%3
" - "Do you want to accept or reject these cookies?

", - count, - QUrl::fromAce(host.toLatin1()), - cookie.isCrossDomain() ? i18nc("@item:intext cross domain cookie", " [Cross Domain]") : QString()); - txt += QLatin1String(""); - - 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" diff --git a/kioslave/http/kcookiejar/kcookiewin.h b/kioslave/http/kcookiejar/kcookiewin.h deleted file mode 100644 index dd1be96b..00000000 --- a/kioslave/http/kcookiejar/kcookiewin.h +++ /dev/null @@ -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 -#include -#include -#include "kcookiejar.h" - -class KLineEdit; -#include - -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 diff --git a/kioslave/http/kcookiejar/main.cpp b/kioslave/http/kcookiejar/main.cpp deleted file mode 100644 index 619e7c67..00000000 --- a/kioslave/http/kcookiejar/main.cpp +++ /dev/null @@ -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 -#include -#include -#include -#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 ", 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; -} diff --git a/kioslave/http/kcookiejar/netscape_cookie_spec.html b/kioslave/http/kcookiejar/netscape_cookie_spec.html deleted file mode 100644 index 6b575e8c..00000000 --- a/kioslave/http/kcookiejar/netscape_cookie_spec.html +++ /dev/null @@ -1,331 +0,0 @@ - - -Client Side State - HTTP Cookies - - - - - -
- -Documentation - - - - - - - - - - -

-PERSISTENT -CLIENT -STATE
-HTTP COOKIES -

- -

Preliminary Specification - Use with caution

-
- -
- -
-

-INTRODUCTION -

-
- -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.

- -

-

-OVERVIEW -

-
- -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 cookie, for no compelling reason.

-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. - -

-

-SPECIFICATION -

-
- -A cookie is introduced to the client by including a Set-Cookie -header as part of an HTTP response, typically this will be generated -by a CGI script. - -

Syntax of the Set-Cookie HTTP Response Header

- -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. - -
-Set-Cookie: NAME=VALUE; expires=DATE;
-path=PATH; domain=DOMAIN_NAME; secure
-
-
-
NAME=VALUE
-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.

This is the only required attribute -on the Set-Cookie header.

-

expires=DATE -
-The expires 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.

-The date string is formatted as: -

Wdy, DD-Mon-YYYY HH:MM:SS GMT
-This is based on -RFC 822, -RFC 850, - -RFC 1036, and - -RFC 1123, -with the variations that the only legal time zone is GMT and -the separators between the elements of the date must be dashes. -

-expires is an optional attribute. If not specified, the cookie will -expire when the user's session ends.

-Note: There is a bug in Netscape Navigator version 1.1 and earlier. -Only cookies whose path attribute is set explicitly to "/" will -be properly saved between sessions if they have an expires -attribute.

- -

domain=DOMAIN_NAME -
-When searching the cookie list for valid cookies, a comparison of the -domain -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 path matching to see if it -should be sent. "Tail matching" means that domain attribute -is matched against the tail of the fully qualified domain name of -the host. A domain attribute of "acme.com" would match -host names "anvil.acme.com" as well as "shipping.crate.acme.com".

- -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". - -

-The default value of domain is the host name of the server -which generated the cookie response.

-

path=PATH -
-The path attribute is used to specify the subset of URLs in a -domain for -which the cookie is valid. If a cookie has already passed domain -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.

-If the path is not specified, it as assumed to be the same path -as the document being described by the header which contains the cookie. -

-

secure -
-If a cookie is marked secure, 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.

-If secure is not specified, a cookie is considered safe to be sent -in the clear over unsecured channels. -

- -

Syntax of the Cookie HTTP Request Header

- -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: -
-Cookie: NAME1=OPAQUE_STRING1; NAME2=OPAQUE_STRING2 ...
-
- -

Additional Notes

- -
    -
  • Multiple Set-Cookie headers can be issued in a single server -response. -

    -

  • 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. -

    -

  • 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.) -

    -

  • 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. -

    -

  • 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. -

    -

  • 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. - -
      -
    • 300 total cookies -
    • 4 kilobytes per cookie, where the name and the OPAQUE_STRING - combine to form the 4 kilobyte limit. -
    • 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) -
    -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. -

    -

  • If a CGI script wishes to delete a cookie, it can do so by -returning a cookie with the same name, and an expires 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. -

  • When caching HTTP, as a proxy server might do, the Set-cookie -response header should never be cached. -

  • If a proxy server receives a response which -contains a Set-cookie header, it should propagate the Set-cookie -header to the client, regardless of whether the response was 304 -(Not Modified) or 200 (OK). -

    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. -

- -
-

-EXAMPLES -

-
- -Here are some sample exchanges which are designed to illustrate the use -of cookies. -

First Example transaction sequence:

-
-
Client requests a document, and receives in the response:
-
-Set-Cookie: CUSTOMER=WILE_E_COYOTE; path=/; expires=Wednesday, 09-Nov-99 23:12:40 GMT
-
When client requests a URL in path "/" on this server, it sends:
-
Cookie: CUSTOMER=WILE_E_COYOTE
-
Client requests a document, and receives in the response:
-
Set-Cookie: PART_NUMBER=ROCKET_LAUNCHER_0001; path=/
-
When client requests a URL in path "/" on this server, it sends:
-
Cookie: CUSTOMER=WILE_E_COYOTE; PART_NUMBER=ROCKET_LAUNCHER_0001
-
Client receives:
-
Set-Cookie: SHIPPING=FEDEX; path=/foo
-
When client requests a URL in path "/" on this server, it sends:
-
Cookie: CUSTOMER=WILE_E_COYOTE; PART_NUMBER=ROCKET_LAUNCHER_0001
-
When client requests a URL in path "/foo" on this server, it sends:
-
Cookie: CUSTOMER=WILE_E_COYOTE; PART_NUMBER=ROCKET_LAUNCHER_0001; SHIPPING=FEDEX
-
-

Second Example transaction sequence:

-
-
Assume all mappings from above have been cleared.

-

Client receives:
-
Set-Cookie: PART_NUMBER=ROCKET_LAUNCHER_0001; path=/
-
When client requests a URL in path "/" on this server, it sends:
-
Cookie: PART_NUMBER=ROCKET_LAUNCHER_0001
-
Client receives:
-
Set-Cookie: PART_NUMBER=RIDING_ROCKET_0023; path=/ammo
-
When client requests a URL in path "/ammo" on this server, it sends:
-
Cookie: PART_NUMBER=RIDING_ROCKET_0023; PART_NUMBER=ROCKET_LAUNCHER_0001
-
NOTE: There are two name/value pairs named "PART_NUMBER" due to the -inheritance -of the "/" mapping in addition to the "/ammo" mapping. -
- -
-

- -

- - - - - - - - - - - - - -

Help   |   Site Map   |   How to Get Netscape Products   |   Advertise With Us   |   Add Site   |   Custom Browser Program
- -Autos   |   Business   |   Computing & Internet   |   Entertainment   |   Family   |   Games   |   Health   |   Lifestyles   |   Local   |   Netscape   |   Netscape Open Directory   |   News   |   Personal Finance   |   Real Estate   |   Research & Learn   |  Shopping   |   Small Business   |   Sports   |   Travel
- - - - - - -
- -© 1999 Netscape, All Rights Reserved. Legal & Privacy Notices
This site powered by Netscape SuiteSpot servers.
- - - - - -
-

- - - - - diff --git a/kioslave/http/kcookiejar/specifications b/kioslave/http/kcookiejar/specifications deleted file mode 100644 index 8e07c8cf..00000000 --- a/kioslave/http/kcookiejar/specifications +++ /dev/null @@ -1,3 +0,0 @@ -HTTP State Management Mechanism -http://www.ietf.org/rfc/rfc2965.txt -http://www.ietf.org/rfc/rfc2109.txt diff --git a/kioslave/http/kcookiejar/tests/CMakeLists.txt b/kioslave/http/kcookiejar/tests/CMakeLists.txt deleted file mode 100644 index 576f56c4..00000000 --- a/kioslave/http/kcookiejar/tests/CMakeLists.txt +++ /dev/null @@ -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}) diff --git a/kioslave/http/kcookiejar/tests/cookie.test b/kioslave/http/kcookiejar/tests/cookie.test deleted file mode 100644 index efb55b72..00000000 --- a/kioslave/http/kcookiejar/tests/cookie.test +++ /dev/null @@ -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"} diff --git a/kioslave/http/kcookiejar/tests/cookie_rfc.test b/kioslave/http/kcookiejar/tests/cookie_rfc.test deleted file mode 100644 index 25ad2682..00000000 --- a/kioslave/http/kcookiejar/tests/cookie_rfc.test +++ /dev/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/ diff --git a/kioslave/http/kcookiejar/tests/cookie_saving.test b/kioslave/http/kcookiejar/tests/cookie_saving.test deleted file mode 100644 index 28dea3ce..00000000 --- a/kioslave/http/kcookiejar/tests/cookie_saving.test +++ /dev/null @@ -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" diff --git a/kioslave/http/kcookiejar/tests/cookie_session.test b/kioslave/http/kcookiejar/tests/cookie_session.test deleted file mode 100644 index 21226609..00000000 --- a/kioslave/http/kcookiejar/tests/cookie_session.test +++ /dev/null @@ -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/ diff --git a/kioslave/http/kcookiejar/tests/cookie_settings.test b/kioslave/http/kcookiejar/tests/cookie_settings.test deleted file mode 100644 index 8a014cf9..00000000 --- a/kioslave/http/kcookiejar/tests/cookie_settings.test +++ /dev/null @@ -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="/" diff --git a/kioslave/http/kcookiejar/tests/kcookiejartest.cpp b/kioslave/http/kcookiejar/tests/kcookiejartest.cpp deleted file mode 100644 index c054fdc7..00000000 --- a/kioslave/http/kcookiejar/tests/kcookiejartest.cpp +++ /dev/null @@ -1,313 +0,0 @@ -/* - This file is part of KDE - - Copyright (C) 2004 Waldo Bastian (bastian@kde.org) - Copyright 2008 David Faure - - 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 -#include - -#include -#include - -#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("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("url"); - QTest::addColumn("expectedResult"); - QTest::addColumn("expectedFqdn"); - QTest::addColumn("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("fqdn"); - QTest::addColumn("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" diff --git a/kioslave/http/parsinghelpers.cpp b/kioslave/http/parsinghelpers.cpp deleted file mode 100644 index 6af51bc6..00000000 --- a/kioslave/http/parsinghelpers.cpp +++ /dev/null @@ -1,597 +0,0 @@ -/* This file is part of the KDE libraries - Copyright (C) 2008 Andreas Hartmetz - Copyright (C) 2010,2011 Rolf Eike Beer - - 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 - -#include -#include -#include -#include - -#include - -// 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 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 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 TokenIterator::all() const -{ - QList ret; - for (int i = 0; i < m_tokens.count(); i++) { - QPair 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(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(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 contentDispositionParserInternal(const QString &disposition) -{ - kDebug(7113) << "disposition: " << disposition; - int pos = 0; - const QString strDisposition = extractUntil(disposition, QLatin1Char(';'), pos, typeSpecials).toLower(); - - QMap parameters; - QMap contparams; // all parameters that contain continuations - QMap 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::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::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::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 contentDispositionParser(const QString &disposition) -{ - QMap 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; -} diff --git a/kioslave/http/parsinghelpers.h b/kioslave/http/parsinghelpers.h deleted file mode 100644 index af026b45..00000000 --- a/kioslave/http/parsinghelpers.h +++ /dev/null @@ -1,86 +0,0 @@ -/* This file is part of the KDE libraries - Copyright (C) 2008 Andreas Hartmetz - - 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 -#include -#include - -struct HeaderField { - HeaderField(bool multiValued) - { isMultiValued = multiValued; } - // QHash requires a default constructor - HeaderField() - { isMultiValued = false; } - - bool isMultiValued; - QList > beginEnd; -}; - -class HeaderTokenizer; -class TokenIterator -{ -public: - inline bool hasNext() const - { - return m_currentToken < m_tokens.count(); - } - - QByteArray next(); - - QByteArray current() const; - - QList all() const; - -private: - friend class HeaderTokenizer; - QList > m_tokens; - int m_currentToken; - const char *m_buffer; - TokenIterator(const QList > &tokens, const char *buffer) - : m_tokens(tokens), - m_currentToken(0), - m_buffer(buffer) {} -}; - -class HeaderTokenizer : public QHash -{ -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 > m_nullTokens; //long-lived, allows us to pass out references. -}; - -#endif //PARSINGHELPERS_H diff --git a/kioslave/http/shoutcast-icecast.txt b/kioslave/http/shoutcast-icecast.txt deleted file mode 100644 index 5207bb71..00000000 --- a/kioslave/http/shoutcast-icecast.txt +++ /dev/null @@ -1,605 +0,0 @@ - -Audio and Apache HTTPD -ApacheCon 2001 -Santa Clara, US - -April 6th, 2001 - -Sander van Zoest -Covalent Technologies, Inc. - - -Latest version can be found at: - - -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: - - - - - - - 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. - - - - - - - - Your browser does not support embedded WAV files. - - - 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= - Title1= - 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/> diff --git a/kioslave/http/tests/CMakeLists.txt b/kioslave/http/tests/CMakeLists.txt deleted file mode 100644 index dfb0f7c2..00000000 --- a/kioslave/http/tests/CMakeLists.txt +++ /dev/null @@ -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() diff --git a/kioslave/http/tests/httpauthenticationtest.cpp b/kioslave/http/tests/httpauthenticationtest.cpp deleted file mode 100644 index e6a14c62..00000000 --- a/kioslave/http/tests/httpauthenticationtest.cpp +++ /dev/null @@ -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; -} diff --git a/kioslave/http/tests/httpauthenticationtest.h b/kioslave/http/tests/httpauthenticationtest.h deleted file mode 100644 index 35b822a0..00000000 --- a/kioslave/http/tests/httpauthenticationtest.h +++ /dev/null @@ -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(); -}; diff --git a/kioslave/http/tests/httpheaderdispositiontest.cpp b/kioslave/http/tests/httpheaderdispositiontest.cpp deleted file mode 100644 index b0cfedde..00000000 --- a/kioslave/http/tests/httpheaderdispositiontest.cpp +++ /dev/null @@ -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); -} diff --git a/kioslave/http/tests/httpheaderdispositiontest.h b/kioslave/http/tests/httpheaderdispositiontest.h deleted file mode 100644 index 383ccf0f..00000000 --- a/kioslave/http/tests/httpheaderdispositiontest.h +++ /dev/null @@ -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 diff --git a/kioslave/http/tests/httpheadertokenizetest.cpp b/kioslave/http/tests/httpheadertokenizetest.cpp deleted file mode 100644 index e2c25d3b..00000000 --- a/kioslave/http/tests/httpheadertokenizetest.cpp +++ /dev/null @@ -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; -} diff --git a/kioslave/http/tests/httpheadertokenizetest.h b/kioslave/http/tests/httpheadertokenizetest.h deleted file mode 100644 index e8690daa..00000000 --- a/kioslave/http/tests/httpheadertokenizetest.h +++ /dev/null @@ -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 diff --git a/kioslave/http/tests/httpobjecttest.cpp b/kioslave/http/tests/httpobjecttest.cpp deleted file mode 100644 index af493689..00000000 --- a/kioslave/http/tests/httpobjecttest.cpp +++ /dev/null @@ -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); -} diff --git a/kioslave/http/tests/httpobjecttest.h b/kioslave/http/tests/httpobjecttest.h deleted file mode 100644 index 6d52ecd2..00000000 --- a/kioslave/http/tests/httpobjecttest.h +++ /dev/null @@ -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 diff --git a/kioslave/http/webdav.protocol b/kioslave/http/webdav.protocol index 262e57f5..3a74d60a 100644 --- a/kioslave/http/webdav.protocol +++ b/kioslave/http/webdav.protocol @@ -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 diff --git a/kioslave/http/webdavs.protocol b/kioslave/http/webdavs.protocol index 7017c24d..a053635c 100644 --- a/kioslave/http/webdavs.protocol +++ b/kioslave/http/webdavs.protocol @@ -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 diff --git a/kparts/browserrun.cpp b/kparts/browserrun.cpp index 50ccaf83..50555c5d 100644 --- a/kparts/browserrun.cpp +++ b/kparts/browserrun.cpp @@ -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"); diff --git a/plasma/CMakeLists.txt b/plasma/CMakeLists.txt index 23b7c3af..eb1ce3ce 100644 --- a/plasma/CMakeLists.txt +++ b/plasma/CMakeLists.txt @@ -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 diff --git a/plasma/applet.cpp b/plasma/applet.cpp index c5178d93..d3ddb61d 100644 --- a/plasma/applet.cpp +++ b/plasma/applet.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" diff --git a/plasma/private/declarative/declarativenetworkaccessmanagerfactory.cpp b/plasma/private/declarative/declarativenetworkaccessmanagerfactory.cpp deleted file mode 100644 index 696973bd..00000000 --- a/plasma/private/declarative/declarativenetworkaccessmanagerfactory.cpp +++ /dev/null @@ -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 -} - - diff --git a/plasma/private/declarative/declarativenetworkaccessmanagerfactory_p.h b/plasma/private/declarative/declarativenetworkaccessmanagerfactory_p.h deleted file mode 100644 index 2908cd31..00000000 --- a/plasma/private/declarative/declarativenetworkaccessmanagerfactory_p.h +++ /dev/null @@ -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 diff --git a/plasma/widgets/declarativewidget.cpp b/plasma/widgets/declarativewidget.cpp index a2537b95..17ef1c2b 100644 --- a/plasma/widgets/declarativewidget.cpp +++ b/plasma/widgets/declarativewidget.cpp @@ -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; }