File indexing completed on 2024-05-19 15:15:49

0001 /*
0002     SPDX-FileCopyrightText: 2000, 2001 Dawit Alemayehu <adawit@kde.org>
0003     SPDX-FileCopyrightText: 2000, 2001 Waldo Bastian <bastian@kde.org>
0004     SPDX-FileCopyrightText: 2000, 2001 George Staikos <staikos@kde.org>
0005     SPDX-FileCopyrightText: 2001, 2002 Hamish Rodda <rodda@kde.org>
0006     SPDX-FileCopyrightText: 2007 Daniel Nicoletti <mirttex@users.sourceforge.net>
0007     SPDX-FileCopyrightText: 2008, 2009 Andreas Hartmetz <ahartmetz@gmail.com>
0008     SPDX-FileCopyrightText: 2022 Harald Sitter <sitter@kde.org>
0009 
0010     SPDX-License-Identifier: LGPL-2.0-or-later
0011 */
0012 
0013 #ifndef HTTP_H
0014 #define HTTP_H
0015 
0016 #include <QDateTime>
0017 #include <QList>
0018 #include <QLocalSocket>
0019 #include <QStringList>
0020 #include <QUrl>
0021 
0022 #include "httpmethod_p.h"
0023 #include "kio/tcpworkerbase.h"
0024 
0025 class QDomNodeList;
0026 class QFile;
0027 class QIODevice;
0028 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
0029 class QNetworkConfigurationManager;
0030 #endif
0031 
0032 namespace KIO
0033 {
0034 class AuthInfo;
0035 }
0036 
0037 class HeaderTokenizer;
0038 class KAbstractHttpAuthentication;
0039 
0040 class HTTPProtocol : public QObject, public KIO::TCPWorkerBase
0041 {
0042     Q_OBJECT
0043 public:
0044     HTTPProtocol(const QByteArray &protocol, const QByteArray &pool, const QByteArray &app);
0045     ~HTTPProtocol() override;
0046 
0047     /** HTTP version **/
0048     enum HTTP_REV { HTTP_None, HTTP_Unknown, HTTP_10, HTTP_11, SHOUTCAST };
0049 
0050     /** Authorization method used **/
0051     enum AUTH_SCHEME { AUTH_None, AUTH_Basic, AUTH_NTLM, AUTH_Digest, AUTH_Negotiate };
0052 
0053     /** DAV-specific request elements for the current connection **/
0054     struct DAVRequest {
0055         DAVRequest()
0056         {
0057             overwrite = false;
0058             depth = 0;
0059         }
0060 
0061         QString desturl;
0062         bool overwrite;
0063         int depth;
0064     };
0065 
0066     enum CacheIOMode {
0067         NoCache = 0,
0068         ReadFromCache = 1,
0069         WriteToCache = 2,
0070     };
0071 
0072     struct CacheTag {
0073         CacheTag()
0074         {
0075             useCache = false;
0076             ioMode = NoCache;
0077             bytesCached = 0;
0078             file = nullptr;
0079         }
0080 
0081         enum CachePlan {
0082             UseCached = 0,
0083             ValidateCached,
0084             IgnoreCached,
0085         };
0086         // int maxCacheAge refers to seconds
0087         CachePlan plan(int maxCacheAge) const;
0088 
0089         QByteArray serialize() const;
0090         bool deserialize(const QByteArray &);
0091 
0092         KIO::CacheControl policy; // ### initialize in the constructor?
0093         bool useCache; // Whether the cache should be used
0094         enum CacheIOMode ioMode; // Write to cache file, read from it, or don't use it.
0095         quint32 fileUseCount;
0096         quint32 bytesCached;
0097         QString etag; // entity tag header as described in the HTTP standard.
0098         QFile *file; // file on disk - either a QTemporaryFile (write) or QFile (read)
0099         QDateTime servedDate; // Date when the resource was served by the origin server
0100         QDateTime lastModifiedDate; // Last modified.
0101         QDateTime expireDate; // Date when the cache entry will expire
0102         QString charset;
0103     };
0104 
0105     /** The request for the current connection **/
0106     struct HTTPRequest {
0107         HTTPRequest()
0108         {
0109             method = KIO::HTTP_UNKNOWN;
0110             offset = 0;
0111             endoffset = 0;
0112             allowTransferCompression = false;
0113             disablePassDialog = false;
0114             doNotWWWAuthenticate = false;
0115             doNotProxyAuthenticate = false;
0116             preferErrorPage = false;
0117             useCookieJar = false;
0118         }
0119 
0120         QByteArray methodString() const;
0121 
0122         QUrl url;
0123         QString encoded_hostname; // ### can be calculated on-the-fly
0124         // Persistent connections
0125         bool isKeepAlive;
0126         int keepAliveTimeout; // Timeout in seconds.
0127 
0128         KIO::HTTP_METHOD method;
0129         QString methodStringOverride; // Overrides method if non-empty.
0130         QByteArray sentMethodString; // Stores http method actually sent
0131         KIO::filesize_t offset;
0132         KIO::filesize_t endoffset;
0133         QString windowId; // Window Id this request is related to.
0134         // Header fields
0135         QString referrer;
0136         QString charsets;
0137         QString languages;
0138         QString userAgent;
0139         // Previous and current response codes
0140         unsigned int responseCode;
0141         unsigned int prevResponseCode;
0142         // Miscellaneous
0143         QString id;
0144         DAVRequest davData;
0145         QUrl redirectUrl;
0146         QUrl proxyUrl;
0147         QStringList proxyUrls;
0148 
0149         bool isPersistentProxyConnection;
0150         bool allowTransferCompression;
0151         bool disablePassDialog;
0152         bool doNotWWWAuthenticate;
0153         bool doNotProxyAuthenticate;
0154         // Indicates whether an error page or error message is preferred.
0155         bool preferErrorPage;
0156 
0157         // Use the cookie jar (or pass cookies to the application as metadata instead)
0158         bool useCookieJar;
0159         // Cookie flags
0160         enum { CookiesAuto, CookiesManual, CookiesNone } cookieMode;
0161 
0162         CacheTag cacheTag;
0163     };
0164 
0165     /** State of the current connection to the server **/
0166     struct HTTPServerState {
0167         HTTPServerState()
0168         {
0169             isKeepAlive = false;
0170             isPersistentProxyConnection = false;
0171         }
0172 
0173         void initFrom(const HTTPRequest &request)
0174         {
0175             url = request.url;
0176             encoded_hostname = request.encoded_hostname;
0177             isKeepAlive = request.isKeepAlive;
0178             proxyUrl = request.proxyUrl;
0179             isPersistentProxyConnection = request.isPersistentProxyConnection;
0180         }
0181 
0182         void updateCredentials(const HTTPRequest &request)
0183         {
0184             if (url.host() == request.url.host() && url.port() == request.url.port()) {
0185                 url.setUserName(request.url.userName());
0186                 url.setPassword(request.url.password());
0187             }
0188             if (proxyUrl.host() == request.proxyUrl.host() && proxyUrl.port() == request.proxyUrl.port()) {
0189                 proxyUrl.setUserName(request.proxyUrl.userName());
0190                 proxyUrl.setPassword(request.proxyUrl.password());
0191             }
0192         }
0193 
0194         void clear()
0195         {
0196             url.clear();
0197             encoded_hostname.clear();
0198             proxyUrl.clear();
0199             isKeepAlive = false;
0200             isPersistentProxyConnection = false;
0201         }
0202 
0203         QUrl url;
0204         QString encoded_hostname;
0205         QUrl proxyUrl;
0206         bool isKeepAlive;
0207         bool isPersistentProxyConnection;
0208     };
0209 
0210     //---------------------- Re-implemented methods ----------------
0211     virtual void setHost(const QString &host, quint16 port, const QString &user, const QString &pass) override;
0212 
0213     void worker_status() override;
0214 
0215     KIO::WorkerResult get(const QUrl &url) override;
0216     KIO::WorkerResult put(const QUrl &url, int _mode, KIO::JobFlags flags) override;
0217 
0218     //----------------- Re-implemented methods for WebDAV -----------
0219     KIO::WorkerResult listDir(const QUrl &url) override;
0220     KIO::WorkerResult mkdir(const QUrl &url, int _permissions) override;
0221 
0222     KIO::WorkerResult rename(const QUrl &src, const QUrl &dest, KIO::JobFlags flags) override;
0223     KIO::WorkerResult copy(const QUrl &src, const QUrl &dest, int _permissions, KIO::JobFlags flags) override;
0224     KIO::WorkerResult del(const QUrl &url, bool _isfile) override;
0225 
0226     // ask the host whether it supports WebDAV & cache this info
0227     Q_REQUIRED_RESULT KIO::WorkerResult davHostOk();
0228 
0229     // send generic DAV request
0230     Q_REQUIRED_RESULT KIO::WorkerResult davGeneric(const QUrl &url, KIO::HTTP_METHOD method, qint64 size = -1);
0231 
0232     // Send requests to lock and unlock resources
0233     Q_REQUIRED_RESULT KIO::WorkerResult davLock(const QUrl &url, const QString &scope, const QString &type, const QString &owner);
0234     Q_REQUIRED_RESULT KIO::WorkerResult davUnlock(const QUrl &url);
0235 
0236     // Calls httpClose() and finished()
0237     Q_REQUIRED_RESULT KIO::WorkerResult davFinished();
0238 
0239     // Handle error conditions
0240     Q_REQUIRED_RESULT KIO::WorkerResult davError(int code = -1, const QString &url = QString());
0241     Q_REQUIRED_RESULT KIO::WorkerResult davError(QString &errorMsg, int code = -1, const QString &url = QString());
0242     //---------------------------- End WebDAV -----------------------
0243 
0244     /**
0245      * Special commands supported by this worker :
0246      * 1 - HTTP POST
0247      * 2 - Cache has been updated
0248      * 3 - SSL Certificate Cache has been updated
0249      * 4 - HTTP multi get
0250      * 5 - DAV LOCK     (see
0251      * 6 - DAV UNLOCK     README.webdav)
0252      */
0253     KIO::WorkerResult special(const QByteArray &data) override;
0254 
0255     KIO::WorkerResult mimetype(const QUrl &url) override;
0256 
0257     KIO::WorkerResult stat(const QUrl &url) override;
0258 
0259     void reparseConfiguration() override;
0260 
0261     /**
0262      * Forced close of connection
0263      */
0264     void closeConnection() override;
0265 
0266     Q_REQUIRED_RESULT KIO::WorkerResult post(const QUrl &url, qint64 size = -1);
0267     KIO::WorkerResult multiGet(const QByteArray &data) override;
0268     Q_REQUIRED_RESULT KIO::WorkerResult maybeSetRequestUrl(const QUrl &);
0269 
0270     /**
0271      * Generate and send error message based on response code.
0272      */
0273     Q_REQUIRED_RESULT KIO::WorkerResult sendHttpError();
0274 
0275     /**
0276      * Call WorkerBase::errorPage() and remember that we've called it
0277      */
0278     bool sendErrorPageNotification();
0279 
0280     /**
0281      * Check network status
0282      */
0283     bool isOffline();
0284 
0285 protected Q_SLOTS:
0286     void slotData(const QByteArray &);
0287     Q_REQUIRED_RESULT KIO::WorkerResult slotFilterError(const QString &text);
0288     Q_REQUIRED_RESULT KIO::WorkerResult error(int errid, const QString &text);
0289     Q_REQUIRED_RESULT KIO::WorkerResult proxyAuthenticationForSocket(const QNetworkProxy &, QAuthenticator *);
0290     void saveProxyAuthenticationForSocket();
0291 
0292 protected:
0293     int readChunked(); ///< Read a chunk
0294     int readLimited(); ///< Read maximum m_iSize bytes.
0295     int readUnlimited(); ///< Read as much as possible.
0296 
0297     /**
0298      * A thin wrapper around TCPWorkerBase::write() that will retry writing as
0299      * long as no error occurs.
0300      */
0301     ssize_t write(const void *buf, size_t nbytes);
0302     using WorkerBase::write;
0303 
0304     /**
0305      * Add an encoding on to the appropriate stack this
0306      * is necessary because transfer encodings and
0307      * content encodings must be handled separately.
0308      */
0309     void addEncoding(const QString &, QStringList &);
0310 
0311     quint16 defaultPort() const;
0312 
0313     // The methods between here and sendQuery() are helpers for sendQuery().
0314 
0315     /**
0316      * Return true if the request is already "done", false otherwise.
0317      *
0318      * @p cacheHasPage will be set to true if the page was found, false otherwise.
0319      */
0320     Q_REQUIRED_RESULT bool satisfyRequestFromCache(bool *cacheHasPage, KIO::WorkerResult &result);
0321     QString formatRequestUri() const;
0322     /**
0323      * create HTTP authentications response(s), if any
0324      */
0325     QString authenticationHeader();
0326     Q_REQUIRED_RESULT KIO::WorkerResult sendQuery();
0327 
0328     /**
0329      * Close transfer
0330      */
0331     void httpClose(bool keepAlive);
0332     /**
0333      * Open connection
0334      */
0335     Q_REQUIRED_RESULT KIO::WorkerResult httpOpenConnection();
0336     /**
0337      * Close connection
0338      */
0339     void httpCloseConnection();
0340     /**
0341      * Check whether to keep or close the connection.
0342      */
0343     bool httpShouldCloseConnection();
0344 
0345     void forwardHttpResponseHeader(bool forwardImmediately = true);
0346 
0347     /**
0348      * fix common MIME type errors by webservers.
0349      *
0350      * Helper for readResponseHeader().
0351      */
0352     void fixupResponseMimetype();
0353     /**
0354      * fix common content-encoding errors by webservers.
0355      *
0356      * Helper for readResponseHeader().
0357      */
0358     void fixupResponseContentEncoding();
0359 
0360     Q_REQUIRED_RESULT KIO::WorkerResult readResponseHeader();
0361     bool parseHeaderFromCache();
0362     void parseContentDisposition(const QString &disposition);
0363 
0364     Q_REQUIRED_RESULT KIO::WorkerResult sendBody();
0365     Q_REQUIRED_RESULT KIO::WorkerResult sendCachedBody();
0366 
0367     // where dataInternal == true, the content is to be made available
0368     // to an internal function.
0369     Q_REQUIRED_RESULT KIO::WorkerResult readBody(bool dataInternal = false);
0370 
0371     /**
0372      * Performs a WebDAV stat or list
0373      */
0374     void davSetRequest(const QByteArray &requestXML);
0375     Q_REQUIRED_RESULT KIO::WorkerResult davStatList(const QUrl &url, bool stat = true);
0376     void davParsePropstats(const QDomNodeList &propstats, KIO::UDSEntry &entry);
0377     void davParseActiveLocks(const QDomNodeList &activeLocks, uint &lockCount);
0378 
0379     /**
0380      * Parses a date & time string
0381      */
0382     QDateTime parseDateTime(const QString &input, const QString &type);
0383 
0384     /**
0385      * Returns the error code from a "HTTP/1.1 code Code Name" string
0386      */
0387     int codeFromResponse(const QString &response);
0388 
0389     /**
0390      * Extracts locks from metadata
0391      * Returns the appropriate If: header
0392      */
0393     QString davProcessLocks();
0394 
0395     /**
0396      * Send a cookie to the cookiejar
0397      */
0398     void addCookies(const QString &url, const QByteArray &cookieHeader);
0399 
0400     /**
0401      * Look for cookies in the cookiejar
0402      */
0403     QString findCookies(const QString &url);
0404 
0405     void cacheParseResponseHeader(const HeaderTokenizer &tokenizer);
0406 
0407     QString cacheFilePathFromUrl(const QUrl &url) const;
0408     bool cacheFileOpenRead();
0409     bool cacheFileOpenWrite();
0410     void cacheFileClose();
0411     void sendCacheCleanerCommand(const QByteArray &command);
0412 
0413     QByteArray cacheFileReadPayload(int maxLength);
0414     void cacheFileWritePayload(const QByteArray &d);
0415     void cacheFileWriteTextHeader();
0416     /**
0417      * check URL to guard against hash collisions, and load the etag for validation
0418      */
0419     bool cacheFileReadTextHeader1(const QUrl &desiredUrl);
0420     /**
0421      * load the rest of the text fields
0422      */
0423     bool cacheFileReadTextHeader2();
0424     void setCacheabilityMetadata(bool cachingAllowed);
0425 
0426     /**
0427      * Do everything proceedUntilResponseHeader does, and also get the response body.
0428      * This is being used as a replacement for proceedUntilResponseHeader() in
0429      * situations where we actually expect the response to have a body / payload data.
0430      *
0431      * where dataInternal == true, the content is to be made available
0432      * to an internal function.
0433      */
0434     Q_REQUIRED_RESULT KIO::WorkerResult proceedUntilResponseContent(bool dataInternal = false);
0435 
0436     /**
0437      * Ensure we are connected, send our query, and get the response header.
0438      */
0439     Q_REQUIRED_RESULT KIO::WorkerResult proceedUntilResponseHeader();
0440 
0441     /**
0442      * Resets any per session settings.
0443      */
0444     void resetSessionSettings();
0445 
0446     /**
0447      * Resets variables related to parsing a response.
0448      */
0449     void resetResponseParsing();
0450 
0451     /**
0452      * Resets any per connection settings. These are different from
0453      * per-session settings in that they must be invalidated every time
0454      * a request is made, e.g. a retry to re-send the header to the
0455      * server, as compared to only when a new request arrives.
0456      */
0457     void resetConnectionSettings();
0458 
0459     /**
0460      * Caches the POST data in a temporary buffer.
0461      *
0462      * Depending on size of content, the temporary buffer might be
0463      * created either in memory or on disk as (a temporary file).
0464      */
0465     void cachePostData(const QByteArray &);
0466 
0467     /**
0468      * Clears the POST data buffer.
0469      *
0470      * Note that calling this function results in the POST data buffer
0471      * getting completely deleted.
0472      */
0473     void clearPostDataBuffer();
0474 
0475     /**
0476      * Returns true on successful retrieval of all content data.
0477      */
0478     Q_REQUIRED_RESULT KIO::WorkerResult retrieveAllData();
0479 
0480     /**
0481      * Saves HTTP authentication data.
0482      */
0483     void saveAuthenticationData();
0484 
0485     /**
0486      * Handles HTTP authentication.
0487      */
0488     bool handleAuthenticationHeader(const HeaderTokenizer *tokenizer);
0489 
0490     /**
0491      * Handles file -> webdav put requests.
0492      */
0493     Q_REQUIRED_RESULT KIO::WorkerResult copyPut(const QUrl &src, const QUrl &dest, KIO::JobFlags flags);
0494 
0495     /**
0496      * Stats a remote DAV file and returns true if it already exists.
0497      */
0498     bool davDestinationExists();
0499 
0500     KIO::WorkerResult fileSystemFreeSpace(const QUrl &url) override;
0501 
0502 protected:
0503     /* This stores information about the credentials already tried
0504      * during the authentication stage (in case the auth method uses
0505      * a username and password). Initially the job-provided credentials
0506      * are used (if any). In case of failure the credential cache is
0507      * queried and if this fails the user is asked to provide credentials
0508      * interactively (unless forbidden by metadata) */
0509     enum TriedCredentials {
0510         NoCredentials = 0,
0511         JobCredentials,
0512         CachedCredentials,
0513         UserInputCredentials,
0514     };
0515 
0516     HTTPServerState m_server;
0517     HTTPRequest m_request;
0518     QList<HTTPRequest> m_requestQueue;
0519 
0520     // Processing related
0521     KIO::filesize_t m_iSize; ///< Expected size of message
0522     KIO::filesize_t m_iPostDataSize;
0523     KIO::filesize_t m_iBytesLeft; ///< # of bytes left to receive in this message.
0524     KIO::filesize_t m_iContentLeft; ///< # of content bytes left
0525     QByteArray m_receiveBuf; ///< Receive buffer
0526     bool m_dataInternal; ///< Data is for internal consumption
0527     bool m_isChunked; ///< Chunked transfer encoding
0528 
0529     bool m_isBusy; ///< Busy handling request queue.
0530     bool m_isEOF;
0531     bool m_isEOD;
0532 
0533     //--- Settings related to a single response only
0534     bool m_isRedirection; ///< Indicates current request is a redirection
0535     QStringList m_responseHeaders; ///< All headers
0536 
0537     // Language/Encoding related
0538     QStringList m_transferEncodings;
0539     QStringList m_contentEncodings;
0540     QString m_contentMD5;
0541     QString m_mimeType; // TODO QByteArray?
0542 
0543     //--- WebDAV
0544     // Data structure to hold data which will be passed to an internal func.
0545     QByteArray m_webDavDataBuf;
0546     QStringList m_davCapabilities;
0547 
0548     bool m_davHostOk;
0549     bool m_davHostUnsupported;
0550     //----------
0551 
0552     // Mimetype determination
0553     bool m_cpMimeBuffer;
0554     QByteArray m_mimeTypeBuffer;
0555 
0556     // Holds the POST data so it won't get lost on if we
0557     // happened to get a 401/407 response when submitting
0558     // a form.
0559     QIODevice *m_POSTbuf;
0560 
0561     // Cache related
0562     int m_maxCacheAge; ///< Maximum age of a cache entry in seconds.
0563     long m_maxCacheSize; ///< Maximum cache size in Kb.
0564     QString m_strCacheDir; ///< Location of the cache.
0565     QLocalSocket m_cacheCleanerConnection; ///< Connection to the cache cleaner process
0566 
0567     // Operation mode
0568     QByteArray m_protocol;
0569 
0570     KAbstractHttpAuthentication *m_wwwAuth;
0571     QList<QByteArray> m_blacklistedWwwAuthMethods;
0572     TriedCredentials m_triedWwwCredentials;
0573     KAbstractHttpAuthentication *m_proxyAuth;
0574     QList<QByteArray> m_blacklistedProxyAuthMethods;
0575     TriedCredentials m_triedProxyCredentials;
0576     // For proxy auth when it's handled by the Qt/KDE socket classes
0577     QAuthenticator *m_socketProxyAuth;
0578 
0579     // To know if we are online or not
0580 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
0581     QNetworkConfigurationManager *m_networkConfig = nullptr;
0582 #endif
0583 
0584     // The current KIO error on this request / response pair - zero / KJob::NoError if no error
0585     int m_kioError;
0586     QString m_kioErrorString;
0587     // Whether we are loading an error page (body of a reply with error response code)
0588     bool m_isLoadingErrorPage;
0589 
0590     // Values that determine the remote connection timeouts.
0591     int m_remoteRespTimeout;
0592 
0593     // EOF Retry count
0594     quint8 m_iEOFRetryCount;
0595 
0596     QByteArray m_unreadBuf;
0597     void clearUnreadBuffer();
0598     void unread(char *buf, size_t size);
0599     size_t readBuffered(char *buf, size_t size, bool unlimited = true);
0600     bool readDelimitedText(char *buf, int *idx, int end, int numNewlines);
0601 };
0602 #endif