1 /*
2     SPDX-FileCopyrightText: 2000, 2001 Dawit Alemayehu <adawit@kde.org>
3     SPDX-FileCopyrightText: 2000, 2001 Waldo Bastian <bastian@kde.org>
4     SPDX-FileCopyrightText: 2000, 2001 George Staikos <staikos@kde.org>
5     SPDX-FileCopyrightText: 2001, 2002 Hamish Rodda <rodda@kde.org>
6     SPDX-FileCopyrightText: 2007 Daniel Nicoletti <mirttex@users.sourceforge.net>
7     SPDX-FileCopyrightText: 2008, 2009 Andreas Hartmetz <ahartmetz@gmail.com>
8 
9     SPDX-License-Identifier: LGPL-2.0-or-later
10 */
11 
12 #ifndef HTTP_H
13 #define HTTP_H
14 
15 #include <QDateTime>
16 #include <QList>
17 #include <QLocalSocket>
18 #include <QStringList>
19 #include <QUrl>
20 
21 #include "httpmethod_p.h"
22 #include "kio/tcpslavebase.h"
23 
24 class QDomNodeList;
25 class QFile;
26 class QIODevice;
27 class QNetworkConfigurationManager;
28 namespace KIO
29 {
30 class AuthInfo;
31 }
32 
33 class HeaderTokenizer;
34 class KAbstractHttpAuthentication;
35 
36 class HTTPProtocol : public QObject, public KIO::TCPSlaveBase
37 {
38     Q_OBJECT
39 public:
40     HTTPProtocol(const QByteArray &protocol, const QByteArray &pool, const QByteArray &app);
41     ~HTTPProtocol() override;
42 
43     /** HTTP version **/
44     enum HTTP_REV { HTTP_None, HTTP_Unknown, HTTP_10, HTTP_11, SHOUTCAST };
45 
46     /** Authorization method used **/
47     enum AUTH_SCHEME { AUTH_None, AUTH_Basic, AUTH_NTLM, AUTH_Digest, AUTH_Negotiate };
48 
49     /** DAV-specific request elements for the current connection **/
50     struct DAVRequest {
DAVRequestDAVRequest51         DAVRequest()
52         {
53             overwrite = false;
54             depth = 0;
55         }
56 
57         QString desturl;
58         bool overwrite;
59         int depth;
60     };
61 
62     enum CacheIOMode {
63         NoCache = 0,
64         ReadFromCache = 1,
65         WriteToCache = 2,
66     };
67 
68     struct CacheTag {
CacheTagCacheTag69         CacheTag()
70         {
71             useCache = false;
72             ioMode = NoCache;
73             bytesCached = 0;
74             file = nullptr;
75         }
76 
77         enum CachePlan {
78             UseCached = 0,
79             ValidateCached,
80             IgnoreCached,
81         };
82         // int maxCacheAge refers to seconds
83         CachePlan plan(int maxCacheAge) const;
84 
85         QByteArray serialize() const;
86         bool deserialize(const QByteArray &);
87 
88         KIO::CacheControl policy; // ### initialize in the constructor?
89         bool useCache; // Whether the cache should be used
90         enum CacheIOMode ioMode; // Write to cache file, read from it, or don't use it.
91         quint32 fileUseCount;
92         quint32 bytesCached;
93         QString etag; // entity tag header as described in the HTTP standard.
94         QFile *file; // file on disk - either a QTemporaryFile (write) or QFile (read)
95         QDateTime servedDate; // Date when the resource was served by the origin server
96         QDateTime lastModifiedDate; // Last modified.
97         QDateTime expireDate; // Date when the cache entry will expire
98         QString charset;
99     };
100 
101     /** The request for the current connection **/
102     struct HTTPRequest {
HTTPRequestHTTPRequest103         HTTPRequest()
104         {
105             method = KIO::HTTP_UNKNOWN;
106             offset = 0;
107             endoffset = 0;
108             allowTransferCompression = false;
109             disablePassDialog = false;
110             doNotWWWAuthenticate = false;
111             doNotProxyAuthenticate = false;
112             preferErrorPage = false;
113             useCookieJar = false;
114         }
115 
116         QByteArray methodString() const;
117 
118         QUrl url;
119         QString encoded_hostname; //### can be calculated on-the-fly
120         // Persistent connections
121         bool isKeepAlive;
122         int keepAliveTimeout; // Timeout in seconds.
123 
124         KIO::HTTP_METHOD method;
125         QString methodStringOverride; // Overrides method if non-empty.
126         QByteArray sentMethodString; // Stores http method actually sent
127         KIO::filesize_t offset;
128         KIO::filesize_t endoffset;
129         QString windowId; // Window Id this request is related to.
130         // Header fields
131         QString referrer;
132         QString charsets;
133         QString languages;
134         QString userAgent;
135         // Previous and current response codes
136         unsigned int responseCode;
137         unsigned int prevResponseCode;
138         // Miscellaneous
139         QString id;
140         DAVRequest davData;
141         QUrl redirectUrl;
142         QUrl proxyUrl;
143         QStringList proxyUrls;
144 
145         bool isPersistentProxyConnection;
146         bool allowTransferCompression;
147         bool disablePassDialog;
148         bool doNotWWWAuthenticate;
149         bool doNotProxyAuthenticate;
150         // Indicates whether an error page or error message is preferred.
151         bool preferErrorPage;
152 
153         // Use the cookie jar (or pass cookies to the application as metadata instead)
154         bool useCookieJar;
155         // Cookie flags
156         enum { CookiesAuto, CookiesManual, CookiesNone } cookieMode;
157 
158         CacheTag cacheTag;
159     };
160 
161     /** State of the current connection to the server **/
162     struct HTTPServerState {
HTTPServerStateHTTPServerState163         HTTPServerState()
164         {
165             isKeepAlive = false;
166             isPersistentProxyConnection = false;
167         }
168 
initFromHTTPServerState169         void initFrom(const HTTPRequest &request)
170         {
171             url = request.url;
172             encoded_hostname = request.encoded_hostname;
173             isKeepAlive = request.isKeepAlive;
174             proxyUrl = request.proxyUrl;
175             isPersistentProxyConnection = request.isPersistentProxyConnection;
176         }
177 
updateCredentialsHTTPServerState178         void updateCredentials(const HTTPRequest &request)
179         {
180             if (url.host() == request.url.host() && url.port() == request.url.port()) {
181                 url.setUserName(request.url.userName());
182                 url.setPassword(request.url.password());
183             }
184             if (proxyUrl.host() == request.proxyUrl.host() && proxyUrl.port() == request.proxyUrl.port()) {
185                 proxyUrl.setUserName(request.proxyUrl.userName());
186                 proxyUrl.setPassword(request.proxyUrl.password());
187             }
188         }
189 
clearHTTPServerState190         void clear()
191         {
192             url.clear();
193             encoded_hostname.clear();
194             proxyUrl.clear();
195             isKeepAlive = false;
196             isPersistentProxyConnection = false;
197         }
198 
199         QUrl url;
200         QString encoded_hostname;
201         QUrl proxyUrl;
202         bool isKeepAlive;
203         bool isPersistentProxyConnection;
204     };
205 
206     //---------------------- Re-implemented methods ----------------
207     virtual void setHost(const QString &host, quint16 port, const QString &user, const QString &pass) override;
208 
209     void slave_status() override;
210 
211     void get(const QUrl &url) override;
212     void put(const QUrl &url, int _mode, KIO::JobFlags flags) override;
213 
214     //----------------- Re-implemented methods for WebDAV -----------
215     void listDir(const QUrl &url) override;
216     void mkdir(const QUrl &url, int _permissions) override;
217 
218     void rename(const QUrl &src, const QUrl &dest, KIO::JobFlags flags) override;
219     void copy(const QUrl &src, const QUrl &dest, int _permissions, KIO::JobFlags flags) override;
220     void del(const QUrl &url, bool _isfile) override;
221 
222     // ask the host whether it supports WebDAV & cache this info
223     bool davHostOk();
224 
225     // send generic DAV request
226     void davGeneric(const QUrl &url, KIO::HTTP_METHOD method, qint64 size = -1);
227 
228     // Send requests to lock and unlock resources
229     void davLock(const QUrl &url, const QString &scope, const QString &type, const QString &owner);
230     void davUnlock(const QUrl &url);
231 
232     // Calls httpClose() and finished()
233     void davFinished();
234 
235     // Handle error conditions
236     QString davError(int code = -1, const QString &url = QString());
237     //---------------------------- End WebDAV -----------------------
238 
239     /**
240      * Special commands supported by this slave :
241      * 1 - HTTP POST
242      * 2 - Cache has been updated
243      * 3 - SSL Certificate Cache has been updated
244      * 4 - HTTP multi get
245      * 5 - DAV LOCK     (see
246      * 6 - DAV UNLOCK     README.webdav)
247      */
248     void special(const QByteArray &data) override;
249 
250     void mimetype(const QUrl &url) override;
251 
252     void stat(const QUrl &url) override;
253 
254     void reparseConfiguration() override;
255 
256     /**
257      * Forced close of connection
258      */
259     void closeConnection() override;
260 
261     void post(const QUrl &url, qint64 size = -1);
262     void multiGet(const QByteArray &data) override;
263     bool maybeSetRequestUrl(const QUrl &);
264 
265     /**
266      * Generate and send error message based on response code.
267      */
268     bool sendHttpError();
269 
270     /**
271      * Call SlaveBase::errorPage() and remember that we've called it
272      */
273     bool sendErrorPageNotification();
274 
275     /**
276      * Check network status
277      */
278     bool isOffline();
279 
280 protected Q_SLOTS:
281     void slotData(const QByteArray &);
282     void slotFilterError(const QString &text);
283     void error(int errid, const QString &text);
284     void proxyAuthenticationForSocket(const QNetworkProxy &, QAuthenticator *);
285     void saveProxyAuthenticationForSocket();
286 
287 protected:
288     int readChunked(); ///< Read a chunk
289     int readLimited(); ///< Read maximum m_iSize bytes.
290     int readUnlimited(); ///< Read as much as possible.
291 
292     /**
293      * A thin wrapper around TCPSlaveBase::write() that will retry writing as
294      * long as no error occurs.
295      */
296     ssize_t write(const void *buf, size_t nbytes);
297     using SlaveBase::write;
298 
299     /**
300      * Add an encoding on to the appropriate stack this
301      * is necessary because transfer encodings and
302      * content encodings must be handled separately.
303      */
304     void addEncoding(const QString &, QStringList &);
305 
306     quint16 defaultPort() const;
307 
308     // The methods between here and sendQuery() are helpers for sendQuery().
309 
310     /**
311      * Return true if the request is already "done", false otherwise.
312      *
313      * @p cacheHasPage will be set to true if the page was found, false otherwise.
314      */
315     bool satisfyRequestFromCache(bool *cacheHasPage);
316     QString formatRequestUri() const;
317     /**
318      * create HTTP authentications response(s), if any
319      */
320     QString authenticationHeader();
321     bool sendQuery();
322 
323     /**
324      * Close transfer
325      */
326     void httpClose(bool keepAlive);
327     /**
328      * Open connection
329      */
330     bool httpOpenConnection();
331     /**
332      * Close connection
333      */
334     void httpCloseConnection();
335     /**
336      * Check whether to keep or close the connection.
337      */
338     bool httpShouldCloseConnection();
339 
340     void forwardHttpResponseHeader(bool forwardImmediately = true);
341 
342     /**
343      * fix common MIME type errors by webservers.
344      *
345      * Helper for readResponseHeader().
346      */
347     void fixupResponseMimetype();
348     /**
349      * fix common content-encoding errors by webservers.
350      *
351      * Helper for readResponseHeader().
352      */
353     void fixupResponseContentEncoding();
354 
355     bool readResponseHeader();
356     bool parseHeaderFromCache();
357     void parseContentDisposition(const QString &disposition);
358 
359     bool sendBody();
360     bool sendCachedBody();
361 
362     // where dataInternal == true, the content is to be made available
363     // to an internal function.
364     bool readBody(bool dataInternal = false);
365 
366     /**
367      * Performs a WebDAV stat or list
368      */
369     void davSetRequest(const QByteArray &requestXML);
370     void davStatList(const QUrl &url, bool stat = true);
371     void davParsePropstats(const QDomNodeList &propstats, KIO::UDSEntry &entry);
372     void davParseActiveLocks(const QDomNodeList &activeLocks, uint &lockCount);
373 
374     /**
375      * Parses a date & time string
376      */
377     QDateTime parseDateTime(const QString &input, const QString &type);
378 
379     /**
380      * Returns the error code from a "HTTP/1.1 code Code Name" string
381      */
382     int codeFromResponse(const QString &response);
383 
384     /**
385      * Extracts locks from metadata
386      * Returns the appropriate If: header
387      */
388     QString davProcessLocks();
389 
390     /**
391      * Send a cookie to the cookiejar
392      */
393     void addCookies(const QString &url, const QByteArray &cookieHeader);
394 
395     /**
396      * Look for cookies in the cookiejar
397      */
398     QString findCookies(const QString &url);
399 
400     void cacheParseResponseHeader(const HeaderTokenizer &tokenizer);
401 
402     QString cacheFilePathFromUrl(const QUrl &url) const;
403     bool cacheFileOpenRead();
404     bool cacheFileOpenWrite();
405     void cacheFileClose();
406     void sendCacheCleanerCommand(const QByteArray &command);
407 
408     QByteArray cacheFileReadPayload(int maxLength);
409     void cacheFileWritePayload(const QByteArray &d);
410     void cacheFileWriteTextHeader();
411     /**
412      * check URL to guard against hash collisions, and load the etag for validation
413      */
414     bool cacheFileReadTextHeader1(const QUrl &desiredUrl);
415     /**
416      * load the rest of the text fields
417      */
418     bool cacheFileReadTextHeader2();
419     void setCacheabilityMetadata(bool cachingAllowed);
420 
421     /**
422      * Do everything proceedUntilResponseHeader does, and also get the response body.
423      * This is being used as a replacement for proceedUntilResponseHeader() in
424      * situations where we actually expect the response to have a body / payload data.
425      *
426      * where dataInternal == true, the content is to be made available
427      * to an internal function.
428      */
429     void proceedUntilResponseContent(bool dataInternal = false);
430 
431     /**
432      * Ensure we are connected, send our query, and get the response header.
433      */
434     bool proceedUntilResponseHeader();
435 
436     /**
437      * Resets any per session settings.
438      */
439     void resetSessionSettings();
440 
441     /**
442      * Resets variables related to parsing a response.
443      */
444     void resetResponseParsing();
445 
446     /**
447      * Resets any per connection settings. These are different from
448      * per-session settings in that they must be invalidated every time
449      * a request is made, e.g. a retry to re-send the header to the
450      * server, as compared to only when a new request arrives.
451      */
452     void resetConnectionSettings();
453 
454     /**
455      * Caches the POST data in a temporary buffer.
456      *
457      * Depending on size of content, the temporary buffer might be
458      * created either in memory or on disk as (a temporary file).
459      */
460     void cachePostData(const QByteArray &);
461 
462     /**
463      * Clears the POST data buffer.
464      *
465      * Note that calling this function results in the POST data buffer
466      * getting completely deleted.
467      */
468     void clearPostDataBuffer();
469 
470     /**
471      * Returns true on successful retrieval of all content data.
472      */
473     bool retrieveAllData();
474 
475     /**
476      * Saves HTTP authentication data.
477      */
478     void saveAuthenticationData();
479 
480     /**
481      * Handles HTTP authentication.
482      */
483     bool handleAuthenticationHeader(const HeaderTokenizer *tokenizer);
484 
485     /**
486      * Handles file -> webdav put requests.
487      */
488     void copyPut(const QUrl &src, const QUrl &dest, KIO::JobFlags flags);
489 
490     /**
491      * Stats a remote DAV file and returns true if it already exists.
492      */
493     bool davDestinationExists();
494 
495     void virtual_hook(int id, void *data) override;
496 
497 private:
498     void fileSystemFreeSpace(const QUrl &url); // KF6 TODO: Once a virtual fileSystemFreeSpace method in SlaveBase exists, override it
499 
500 protected:
501     /* This stores information about the credentials already tried
502      * during the authentication stage (in case the auth method uses
503      * a username and password). Initially the job-provided credentials
504      * are used (if any). In case of failure the credential cache is
505      * queried and if this fails the user is asked to provide credentials
506      * interactively (unless forbidden by metadata) */
507     enum TriedCredentials {
508         NoCredentials = 0,
509         JobCredentials,
510         CachedCredentials,
511         UserInputCredentials,
512     };
513 
514     HTTPServerState m_server;
515     HTTPRequest m_request;
516     QList<HTTPRequest> m_requestQueue;
517 
518     // Processing related
519     KIO::filesize_t m_iSize; ///< Expected size of message
520     KIO::filesize_t m_iPostDataSize;
521     KIO::filesize_t m_iBytesLeft; ///< # of bytes left to receive in this message.
522     KIO::filesize_t m_iContentLeft; ///< # of content bytes left
523     QByteArray m_receiveBuf; ///< Receive buffer
524     bool m_dataInternal; ///< Data is for internal consumption
525     bool m_isChunked; ///< Chunked transfer encoding
526 
527     bool m_isBusy; ///< Busy handling request queue.
528     bool m_isEOF;
529     bool m_isEOD;
530 
531     //--- Settings related to a single response only
532     bool m_isRedirection; ///< Indicates current request is a redirection
533     QStringList m_responseHeaders; ///< All headers
534 
535     // Language/Encoding related
536     QStringList m_transferEncodings;
537     QStringList m_contentEncodings;
538     QString m_contentMD5;
539     QString m_mimeType; // TODO QByteArray?
540 
541     //--- WebDAV
542     // Data structure to hold data which will be passed to an internal func.
543     QByteArray m_webDavDataBuf;
544     QStringList m_davCapabilities;
545 
546     bool m_davHostOk;
547     bool m_davHostUnsupported;
548     //----------
549 
550     // Mimetype determination
551     bool m_cpMimeBuffer;
552     QByteArray m_mimeTypeBuffer;
553 
554     // Holds the POST data so it won't get lost on if we
555     // happened to get a 401/407 response when submitting
556     // a form.
557     QIODevice *m_POSTbuf;
558 
559     // Cache related
560     int m_maxCacheAge; ///< Maximum age of a cache entry in seconds.
561     long m_maxCacheSize; ///< Maximum cache size in Kb.
562     QString m_strCacheDir; ///< Location of the cache.
563     QLocalSocket m_cacheCleanerConnection; ///< Connection to the cache cleaner process
564 
565     // Operation mode
566     QByteArray m_protocol;
567 
568     KAbstractHttpAuthentication *m_wwwAuth;
569     QList<QByteArray> m_blacklistedWwwAuthMethods;
570     TriedCredentials m_triedWwwCredentials;
571     KAbstractHttpAuthentication *m_proxyAuth;
572     QList<QByteArray> m_blacklistedProxyAuthMethods;
573     TriedCredentials m_triedProxyCredentials;
574     // For proxy auth when it's handled by the Qt/KDE socket classes
575     QAuthenticator *m_socketProxyAuth;
576 
577     // To know if we are online or not
578     QNetworkConfigurationManager *m_networkConfig;
579     // The current KIO error on this request / response pair - zero / KJob::NoError if no error
580     int m_kioError;
581     // Whether we are loading an error page (body of a reply with error response code)
582     bool m_isLoadingErrorPage;
583 
584     // Values that determine the remote connection timeouts.
585     int m_remoteRespTimeout;
586 
587     // EOF Retry count
588     quint8 m_iEOFRetryCount;
589 
590     QByteArray m_unreadBuf;
591     void clearUnreadBuffer();
592     void unread(char *buf, size_t size);
593     size_t readBuffered(char *buf, size_t size, bool unlimited = true);
594     bool readDelimitedText(char *buf, int *idx, int end, int numNewlines);
595 };
596 #endif
597